1 /*----------------------------------------------------------------------*/
2 /* elements.c --- xcircuit routines for creating basic elements		*/
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 
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
18 
19 #ifdef TCL_WRAPPER
20 #include <tk.h>
21 #else
22 #ifndef XC_WIN32
23 #include "Xw/Xw.h"
24 #endif
25 #endif
26 
27 /*----------------------------------------------------------------------*/
28 /* Local includes                                                       */
29 /*----------------------------------------------------------------------*/
30 
31 #include "xcircuit.h"
32 #include "colordefs.h"
33 
34 /*----------------------------------------------------------------------*/
35 /* Function prototype declarations					*/
36 /*----------------------------------------------------------------------*/
37 #include "prototypes.h"
38 
39 /*----------------------------------------------------------------------*/
40 /* Global Variable definitions                                          */
41 /*----------------------------------------------------------------------*/
42 
43 extern Display  *dpy;    /* Works well to make this globally accessible */
44 extern Cursor   appcursors[NUM_CURSORS];
45 extern XCWindowData *areawin;
46 extern Globaldata xobjs;
47 extern xcWidget   top;
48 extern fontinfo *fonts;
49 extern short fontcount;
50 extern char  _STR[150], _STR2[250];
51 extern int number_colors;
52 extern colorindex *colorlist;
53 #if !defined(HAVE_CAIRO)
54 extern Pixmap dbuf;
55 #endif
56 
57 extern double atan2();
58 
59 /*------------------------------------------------------------------------*/
60 /* Declarations of global variables                                       */
61 /*------------------------------------------------------------------------*/
62 
63 char extchar[20];
64 double saveratio;
65 u_char texttype;
66 
67 /*--------------------------------------*/
68 /* Element constructor functions	*/
69 /*--------------------------------------*/
70 
71 /*--------------------------------------------------------------*/
72 /* Label constructor:  Create a new label element in the object	*/
73 /* whose instance is "destinst" and return a pointer to it.	*/
74 /*								*/
75 /*	"destinst" is the destination instance.  If NULL, the	*/
76 /*	   top-level instance (areawin->topinstance) is used.	*/
77 /*	"strptr" is a pointer to a stringpart string, and may	*/
78 /*	   be NULL.  If non-NULL, should NOT be free'd by the	*/
79 /*	   calling routine.					*/
80 /*	"pintype" is NORMAL, LOCAL, GLOBAL, or INFO		*/
81 /*	"x" and "y" are the label coordinates.			*/
82 /*	if "dochange" is FALSE, then this label is being drawn	*/
83 /*	   as part of a font or library and should not cause	*/
84 /*	   changes count to increment.				*/
85 /*								*/
86 /* Other properties must be set individually by the calling	*/
87 /* 	routine.						*/
88 /*								*/
89 /* Return value is a pointer to the newly created label.	*/
90 /*--------------------------------------------------------------*/
91 
new_label(objinstptr destinst,stringpart * strptr,int pintype,int x,int y,u_char dochange)92 labelptr new_label(objinstptr destinst, stringpart *strptr, int pintype,
93 	int x, int y, u_char dochange)
94 {
95    labelptr *newlab;
96    objectptr destobject;
97    objinstptr locdestinst;
98 
99    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
100    destobject = locdestinst->thisobject;
101 
102    NEW_LABEL(newlab, destobject);
103    labeldefaults(*newlab, pintype, x, y);
104 
105    if (strptr->type == FONT_NAME) {
106       free ((*newlab)->string);
107       (*newlab)->string = strptr;
108    }
109    else
110       (*newlab)->string->nextpart = strptr;
111 
112    calcbboxvalues(locdestinst, (genericptr *)newlab);
113    updatepagebounds(destobject);
114    if (dochange != (u_char)0) incr_changes(destobject);
115    return *newlab;
116 }
117 
118 /*--------------------------------------------------------------*/
119 /* Variant of the above; creates a label from a (char *) string	*/
120 /* instead of a stringpart pointer.  Like the stringpart 	*/
121 /* pointer above, "cstr" should NOT be free'd by the calling	*/
122 /* routine.							*/
123 /*--------------------------------------------------------------*/
124 
new_simple_label(objinstptr destinst,char * cstr,int pintype,int x,int y)125 labelptr new_simple_label(objinstptr destinst, char *cstr,
126 	int pintype, int x, int y)
127 {
128    stringpart *strptr;
129 
130    strptr = (stringpart *)malloc(sizeof(stringpart));
131    strptr->type = TEXT_STRING;
132    strptr->nextpart = NULL;
133    strptr->data.string = cstr;
134 
135    return new_label(destinst, strptr, pintype, x, y, (u_char)0);
136 }
137 
138 /*--------------------------------------------------------------*/
139 /* Another variant of the above; creates a "temporary" label	*/
140 /* from a (char *) string.  As above, "cstr" should NOT be	*/
141 /* free'd by the calling routine.  The "temporary" label has no	*/
142 /* font information, and cannot be displayed nor saved/loaded	*/
143 /* from the PostScript output file.  Used to name networks or	*/
144 /* to mark port positions.  Pin type is always LOCAL.  Does not	*/
145 /* require updating bounding box info since it cannot be	*/
146 /* displayed.  Consequently, only requires passing the object	*/
147 /* to get the new label, not its instance.			*/
148 /*--------------------------------------------------------------*/
149 
new_temporary_label(objectptr destobject,char * cstr,int x,int y)150 labelptr new_temporary_label(objectptr destobject, char *cstr,
151 	int x, int y)
152 {
153    labelptr *newlab;
154 
155    NEW_LABEL(newlab, destobject);
156    labeldefaults(*newlab, LOCAL, x, y);
157 
158    (*newlab)->string->type = TEXT_STRING; /* overwrites FONT record */
159    (*newlab)->string->data.string = cstr;
160    return *newlab;
161 }
162 
163 /*--------------------------------------------------------------*/
164 /* Polygon constructor:  Create a new polygon element in the	*/
165 /* object whose instance is "destinst" and return a pointer to  */
166 /* it.								*/
167 /*								*/
168 /*	"destinst" is the destination instance.  If NULL, the	*/
169 /*	   top-level instance (areawin->topinstance) is used.	*/
170 /*	"points" is a list of XPoint pointers, should not be	*/
171 /*	   NULL.  It is transferred to the polygon verbatim,	*/
172 /*	   and should NOT be free'd by the calling routine.	*/
173 /*	"number" is the number of points in the list, or zero	*/
174 /*	   if "points" is NULL.					*/
175 /*								*/
176 /* Other properties must be set individually by the calling	*/
177 /* 	routine.						*/
178 /*								*/
179 /* Return value is a pointer to the newly created polygon.	*/
180 /*--------------------------------------------------------------*/
181 
new_polygon(objinstptr destinst,pointlist * points,int number)182 polyptr new_polygon(objinstptr destinst, pointlist *points, int number)
183 {
184    polyptr *newpoly;
185    objectptr destobject;
186    objinstptr locdestinst;
187 
188    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
189    destobject = locdestinst->thisobject;
190 
191    NEW_POLY(newpoly, destobject);
192    polydefaults(*newpoly, 0, 0, 0);
193    (*newpoly)->number = number;
194    (*newpoly)->points = *points;
195 
196    calcbboxvalues(locdestinst, (genericptr *)newpoly);
197    updatepagebounds(destobject);
198    incr_changes(destobject);
199    return *newpoly;
200 }
201 
202 /*--------------------------------------------------------------*/
203 /* Spline constructor:  Create a new spline element in the	*/
204 /* object whose instance is "destinst" and return a pointer to  */
205 /* it.								*/
206 /*								*/
207 /*	"destinst" is the destination instance.  If NULL, the	*/
208 /*	   top-level instance (areawin->topinstance) is used.	*/
209 /*	"points" is a array of 4 XPoints; should not be	NULL.	*/
210 /*								*/
211 /* Other properties must be set individually by the calling	*/
212 /* 	routine.						*/
213 /*								*/
214 /* Return value is a pointer to the newly created spline.	*/
215 /*--------------------------------------------------------------*/
216 
new_spline(objinstptr destinst,pointlist points)217 splineptr new_spline(objinstptr destinst, pointlist points)
218 {
219    splineptr *newspline;
220    objectptr destobject;
221    objinstptr locdestinst;
222    int i;
223 
224    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
225    destobject = locdestinst->thisobject;
226 
227    NEW_SPLINE(newspline, destobject);
228    splinedefaults(*newspline, 0, 0);
229 
230    for (i = 0; i < 4; i++)
231       (*newspline)->ctrl[i] = points[i];
232 
233    calcspline(*newspline);
234    calcbboxvalues(locdestinst, (genericptr *)newspline);
235    updatepagebounds(destobject);
236    incr_changes(destobject);
237    return *newspline;
238 }
239 
240 /*--------------------------------------------------------------*/
241 /* Arc constructor:  Create a new arc element in the object	*/
242 /* whose instance is "destinst" and return a pointer to it.	*/
243 /*								*/
244 /*	"destinst" is the destination instance.  If NULL, the	*/
245 /*	   top-level instance (areawin->topinstance) is used.	*/
246 /*	"radius" is the radius of the (circular) arc.		*/
247 /*	"x" and "y" represents the arc center position.		*/
248 /*								*/
249 /* Other properties must be set individually by the calling	*/
250 /* 	routine.						*/
251 /*								*/
252 /* Return value is a pointer to the newly created arc.		*/
253 /*--------------------------------------------------------------*/
254 
new_arc(objinstptr destinst,int radius,int x,int y)255 arcptr new_arc(objinstptr destinst, int radius, int x, int y)
256 {
257    arcptr *newarc;
258    objectptr destobject;
259    objinstptr locdestinst;
260 
261    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
262    destobject = locdestinst->thisobject;
263 
264    NEW_ARC(newarc, destobject);
265    arcdefaults(*newarc, x, y);
266    (*newarc)->radius = (*newarc)->yaxis = radius;
267 
268    calcarc(*newarc);
269    calcbboxvalues(locdestinst, (genericptr *)newarc);
270    updatepagebounds(destobject);
271    incr_changes(destobject);
272    return *newarc;
273 }
274 
275 /*--------------------------------------------------------------*/
276 /* Instance constructor:  Create a new object instance element	*/
277 /* in the object whose instance is "destinst" and return a	*/
278 /* pointer to it.						*/
279 /*								*/
280 /*	"destinst" is the destination instance.  If NULL, the	*/
281 /*	   top-level instance (areawin->topinstance) is used.	*/
282 /*	"srcinst" is the source instance of which this is a	*/
283 /*	   copy.						*/
284 /*	"x" and "y" represents the instance position.		*/
285 /*								*/
286 /* Other properties must be set individually by the calling	*/
287 /* 	routine.						*/
288 /*								*/
289 /* Return value is a pointer to the newly created arc.		*/
290 /*--------------------------------------------------------------*/
291 
new_objinst(objinstptr destinst,objinstptr srcinst,int x,int y)292 objinstptr new_objinst(objinstptr destinst, objinstptr srcinst, int x, int y)
293 {
294    objinstptr *newobjinst;
295    objectptr destobject;
296    objinstptr locdestinst;
297 
298    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
299    destobject = locdestinst->thisobject;
300 
301    NEW_OBJINST(newobjinst, destobject);
302    instcopy(*newobjinst, srcinst);
303    (*newobjinst)->position.x = x;
304    (*newobjinst)->position.y = y;
305 
306    calcbboxvalues(locdestinst, (genericptr *)newobjinst);
307    updatepagebounds(destobject);
308    incr_changes(destobject);
309    return *newobjinst;
310 }
311 
312 /*--------------------------------------------------------------*/
313 /* Generic element destructor function				*/
314 /* (Note---this function is not being used anywhere. . .)	*/
315 /*--------------------------------------------------------------*/
316 
remove_element(objinstptr destinst,genericptr genelem)317 void remove_element(objinstptr destinst, genericptr genelem)
318 {
319    objectptr destobject;
320    objinstptr locdestinst;
321 
322    locdestinst = (destinst == NULL) ? areawin->topinstance : destinst;
323    destobject = locdestinst->thisobject;
324 
325    genelem->type &= REMOVE_TAG;
326    delete_tagged(locdestinst);
327    calcbboxvalues(locdestinst, (genericptr *)NULL);
328    updatepagebounds(destobject);
329 }
330 
331 /*-------------------------------------*/
332 /* Sane values for a new path instance */
333 /*-------------------------------------*/
334 
pathdefaults(pathptr newpath,int x,int y)335 void pathdefaults(pathptr newpath, int x, int y)
336 {
337    UNUSED(x);
338    UNUSED(y);
339 
340    newpath->style = NORMAL;
341    newpath->width = areawin->linewidth;
342    newpath->style = areawin->style;
343    newpath->color = areawin->color;
344    newpath->parts = 0;
345    newpath->plist = (genericptr *)NULL;
346    newpath->passed = NULL;
347 }
348 
349 /*---------------------------------------*/
350 /* Sane values for a new object instance */
351 /*---------------------------------------*/
352 
instancedefaults(objinstptr newinst,objectptr thisobj,int x,int y)353 void instancedefaults(objinstptr newinst, objectptr thisobj, int x, int y)
354 {
355    newinst->position.x = x;
356    newinst->position.y = y;
357    newinst->rotation = 0.0;
358    newinst->scale = 1.0;
359    newinst->style = LINE_INVARIANT;
360    newinst->thisobject = thisobj;
361    newinst->color = areawin->color;
362    newinst->params = NULL;
363    newinst->passed = NULL;
364 
365    newinst->bbox.lowerleft.x = thisobj->bbox.lowerleft.x;
366    newinst->bbox.lowerleft.y = thisobj->bbox.lowerleft.y;
367    newinst->bbox.width = thisobj->bbox.width;
368    newinst->bbox.height = thisobj->bbox.height;
369 
370    newinst->schembbox = NULL;
371 }
372 
373 /*--------------------------------------*/
374 /* Draw a dot at the current point.     */
375 /*--------------------------------------*/
376 
drawdot(int xpos,int ypos)377 void drawdot(int xpos, int ypos)
378 {
379    arcptr *newarc;
380    objinstptr *newdot;
381    objectptr dotobj;
382 
383    /* Find the object "dot" in the builtin library, or else use an arc */
384 
385    if ((dotobj = finddot()) != (objectptr)NULL) {
386       NEW_OBJINST(newdot, topobject);
387       instancedefaults(*newdot, dotobj, xpos, ypos);
388       register_for_undo(XCF_Dot, UNDO_DONE, areawin->topinstance, *newdot);
389    }
390    else {
391       NEW_ARC(newarc, topobject);
392       arcdefaults(*newarc, xpos, ypos);
393       (*newarc)->radius = 6;
394       (*newarc)->yaxis = 6;
395       (*newarc)->width = 1.0;
396       (*newarc)->style = FILLED | FILLSOLID | NOBORDER;
397       (*newarc)->passed = NULL;
398       (*newarc)->cycle = NULL;
399       calcarc(*newarc);
400       register_for_undo(XCF_Arc, UNDO_DONE, areawin->topinstance, *newarc);
401    }
402    incr_changes(topobject);
403 }
404 
405 /*--------------------------------------*/
406 /* Sane default values for a label	*/
407 /*--------------------------------------*/
408 
labeldefaults(labelptr newlabel,u_char dopin,int x,int y)409 void labeldefaults(labelptr newlabel, u_char dopin, int x, int y)
410 {
411    newlabel->rotation = 0.0;
412    newlabel->color = areawin->color;
413    newlabel->scale = areawin->textscale;
414    newlabel->string = (stringpart *)malloc(sizeof(stringpart));
415    newlabel->passed = NULL;
416    newlabel->cycle = NULL;
417 
418    /* initialize string with font designator */
419    newlabel->string->type = FONT_NAME;
420    newlabel->string->data.font = areawin->psfont;
421    newlabel->string->nextpart = NULL;
422 
423    newlabel->pin = dopin;
424    if (dopin == LOCAL) newlabel->color = LOCALPINCOLOR;
425    else if (dopin == GLOBAL) newlabel->color = GLOBALPINCOLOR;
426    else if (dopin == INFO) newlabel->color = INFOLABELCOLOR;
427 
428    newlabel->anchor = areawin->anchor;
429    newlabel->position.x = x;
430    newlabel->position.y = y;
431 }
432 
433 /*--------------------------------------*/
434 /* Button handler when creating a label */
435 /*--------------------------------------*/
436 
textbutton(u_char dopin,int x,int y)437 void textbutton(u_char dopin, int x, int y)
438 {
439    labelptr *newlabel;
440    XPoint userpt;
441    short tmpheight, *newselect;
442 
443    XDefineCursor(dpy, areawin->window, TEXTPTR);
444    W3printf("Click to end or cancel.");
445 
446    if (fontcount == 0)
447       Wprintf("Warning:  No fonts available!");
448 
449    unselect_all();
450    NEW_LABEL(newlabel, topobject);
451    newselect = allocselect();
452    *newselect = topobject->parts - 1;
453    snap(x, y, &userpt);
454    labeldefaults(*newlabel, dopin, userpt.x, userpt.y);
455 
456    tmpheight = (short)(TEXTHEIGHT * (*newlabel)->scale);
457    userpt.y -= ((*newlabel)->anchor & NOTBOTTOM) ?
458 	(((*newlabel)->anchor & TOP) ? tmpheight : tmpheight / 2) : 0;
459    areawin->origin.x = userpt.x;
460    areawin->origin.y = userpt.y;
461    areawin->textpos = 1;  /* Text position is *after* the font declaration */
462 
463    text_mode_draw(xcDRAW_EDIT, *newlabel);
464 }
465 
466 /*----------------------------------------------------------------------*/
467 /* Report on characters surrounding the current text position		*/
468 /*----------------------------------------------------------------------*/
469 
470 #define MAXCHARS 10
471 
charreport(labelptr curlabel)472 void charreport(labelptr curlabel)
473 {
474    int i, locpos, cleft = 149;
475    stringpart *strptr;
476 
477    _STR2[0] = '\0';
478    for (i = areawin->textpos - MAXCHARS; i <= areawin->textpos + MAXCHARS - 1; i++) {
479       if (i < 0) continue;
480       strptr = findstringpart(i, &locpos, curlabel->string, areawin->topinstance);
481       if (i == areawin->textpos) {
482 	 strncat(_STR2, "| ", cleft);
483 	 cleft -= 2;
484       }
485       if (strptr == NULL) break;
486       if (strptr->type != RETURN || strptr->data.flags == 0) {
487          charprint(_STR, strptr, locpos);
488          cleft -= strlen(_STR);
489          strncat(_STR2, _STR, cleft);
490          strncat(_STR2, " ", --cleft);
491          if (cleft <= 0) break;
492       }
493    }
494    W3printf("%s", _STR2);
495 }
496 
497 /*----------------------------------------------------------------------*/
498 /* See if a (pin) label has a copy (at least one) in this drawing.	*/
499 /*----------------------------------------------------------------------*/
500 
findlabelcopy(labelptr curlabel,stringpart * curstring)501 labelptr findlabelcopy(labelptr curlabel, stringpart *curstring)
502 {
503    genericptr *tgen;
504    labelptr tlab;
505 
506    for (tgen = topobject->plist; tgen < topobject->plist + topobject->parts; tgen++) {
507       if (IS_LABEL(*tgen)) {
508          tlab = TOLABEL(tgen);
509 	 if (tlab->pin != LOCAL) continue;
510 	 else if (tlab == curlabel) continue;  /* Don't count self! */
511          else if (!stringcomp(tlab->string, curstring)) return tlab;
512       }
513    }
514    return NULL;
515 }
516 
517 /*--------------------------------------------------------------*/
518 /* Interpret string and add to current label.			*/
519 /* 	keypressed is a KeySym					*/
520 /*	clientdata can pass information for label controls	*/
521 /*								*/
522 /* Return TRUE if labeltext handled the character, FALSE if the	*/
523 /* character was not recognized.				*/
524 /*--------------------------------------------------------------*/
525 
labeltext(int keypressed,char * clientdata)526 Boolean labeltext(int keypressed, char *clientdata)
527 {
528    labelptr curlabel;
529    stringpart *curpos, *labelbuf;
530    int locpos;
531    Boolean r = True, do_redraw = False;
532    short cfont;
533    TextExtents tmpext;
534    TechPtr oldtech, newtech;
535 
536    curlabel = TOLABEL(EDITPART);
537 
538    if (curlabel == NULL || curlabel->type != LABEL) {
539       Wprintf("Error:  Bad label string");
540       text_mode_draw(xcDRAW_EMPTY, curlabel);
541       eventmode = NORMAL_MODE;
542       return FALSE;
543    }
544 
545    /* find text segment of the current position */
546    curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
547 		areawin->topinstance);
548 
549    if (clientdata != NULL && keypressed == TEXT_DELETE) {
550       if (areawin->textpos > 1) {
551          int curloc, strpos;
552          stringpart *strptr;
553 
554          if (areawin->textend == 0) areawin->textend = areawin->textpos - 1;
555 
556          undrawtext(curlabel);
557          for (strpos = areawin->textpos - 1; strpos >= areawin->textend; strpos--) {
558 	    strptr = findstringpart(strpos, &curloc, curlabel->string,
559 			 areawin->topinstance);
560 	    if (curloc >= 0) {
561 	       memmove(strptr->data.string + curloc,
562 			strptr->data.string + curloc + 1,
563 			strlen(strptr->data.string + curloc + 1) + 1);
564 	       if (strlen(strptr->data.string) == 0)
565 	          deletestring(strptr, &curlabel->string, areawin->topinstance);
566 	    }
567 
568             /* Don't delete any parameter boundaries---must use	*/
569             /* "unparameterize" command for that.		*/
570 
571 	    else if (strptr != NULL) {
572 	       if ((strptr->type != PARAM_START) && (strptr->type != PARAM_END))
573 	          deletestring(strptr, &curlabel->string, areawin->topinstance);
574 	       else
575 	          areawin->textpos++;
576 	    }
577 	    else
578 	       Fprintf(stdout, "Error:  Unexpected NULL string part\n");
579             areawin->textpos--;
580          }
581          areawin->textend = 0;
582          do_redraw = True;
583       }
584    }
585    else if (clientdata != NULL && keypressed == TEXT_DEL_PARAM) {
586       if (areawin->textpos > 1) {
587          int curloc, strpos;
588          stringpart *strptr;
589 
590          strpos = areawin->textpos - 1;
591          strptr = findstringpart(strpos, &curloc, curlabel->string,
592 			 areawin->topinstance);
593          if (curloc > 0) strpos -= curloc;	 /* move to beginning of string */
594          if ((strptr != NULL) && (strptr->type != PARAM_START) && (strpos > 0)) {
595             strpos--;
596 	    strptr = findstringpart(strpos--, &curloc, curlabel->string,
597 			areawin->topinstance);
598 	 }
599          if ((strptr != NULL) && (strptr->type == PARAM_START)) {
600             if (strpos >= 0) {
601 	        undrawtext(curlabel);
602 	        unmakeparam(curlabel, areawin->topinstance, strptr);
603                 do_redraw = True;
604 	    }
605 	 }
606       }
607    }
608    else if (clientdata != NULL && keypressed == TEXT_RETURN) {
609       Boolean hasstuff = False;   /* Check for null string */
610       stringpart *tmppos;
611 
612       for (tmppos = curlabel->string; tmppos != NULL; tmppos = tmppos->nextpart) {
613 	 if (tmppos->type == PARAM_START) hasstuff = True;
614 	 else if (tmppos->type == TEXT_STRING) hasstuff = True;
615       }
616       XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
617 
618       W3printf("");
619 
620       if (hasstuff && (eventmode != ETEXT_MODE && eventmode != CATTEXT_MODE)) {
621          register_for_undo(XCF_Text, UNDO_MORE, areawin->topinstance,
622                     curlabel);
623 
624 	 incr_changes(topobject);
625 	 invalidate_netlist(topobject);
626 
627       }
628       else if (!hasstuff && (eventmode == ETEXT_MODE)) {
629 	 if (*(areawin->selectlist) < topobject->parts) {
630 	    /* Force the "delete" undo record to be a continuation of	*/
631 	    /* the undo series containing the edit.  That way, "undo"	*/
632 	    /* does not generate a label with null text.		*/
633 
634 	    xobjs.undostack->idx = -xobjs.undostack->idx;
635 	    standard_element_delete(NORMAL);
636 	 }
637 	 else {
638 	    /* Label had just been created; just delete it w/o undo */
639 	    freelabel(curlabel->string);
640 	    free(curlabel);
641 	    topobject->parts--;
642 	    unselect_all();
643 	 }
644       }
645 
646       if ((!hasstuff) && (eventmode == CATTEXT_MODE)) {  /* can't have null labels! */
647 	 undo_action();
648 	 redrawtext(curlabel);
649          areawin->redraw_needed = False; /* ignore previous redraw requests */
650    	 text_mode_draw(xcDRAW_FINAL, curlabel);
651 	 Wprintf("Object must have a name!");
652 	 eventmode = CATALOG_MODE;
653       }
654       else if (!hasstuff) {
655          areawin->redraw_needed = False; /* ignore previous redraw requests */
656    	 text_mode_draw(xcDRAW_FINAL, curlabel);
657 	 eventmode = NORMAL_MODE;
658       }
659       else if (eventmode == CATTEXT_MODE) {
660 	 objectptr libobj;
661 	 stringpart *oldname;
662 	 int page, libnum;
663 
664 	 /* Get the library object whose name matches the original string */
665 	 oldname = get_original_string(curlabel);
666 	 if ((libobj = NameToObject(oldname->nextpart->data.string, NULL, FALSE))
667 			!= NULL) {
668 
669             /* Set name of object to new string.  Don't overwrite the	*/
670 	    /* object's technology *unless* the new string has a	*/
671 	    /* namespace, in which case the object's technology gets	*/
672 	    /* changed.							*/
673 
674 	    char *techptr, *libobjname = libobj->name;
675 	    if ((techptr = strstr(libobjname, "::")) != NULL &&
676 			(strstr(curlabel->string->nextpart->data.string, "::")
677 			== NULL))
678 	       libobjname = techptr + 2;
679 
680 	    /* Save a pointer to the old technology before we overwrite the name */
681 	    oldtech = GetObjectTechnology(libobj);
682 
683 	    strcpy(libobjname, curlabel->string->nextpart->data.string);
684 
685 	    /* If checkname() alters the name, it has to be copied back to */
686 	    /* the catalog label for the object.			   */
687 
688 	    if (checkname(libobj)) {
689 	       undrawtext(curlabel);
690 	       curlabel->string->nextpart->data.string = (char *)realloc(
691 			curlabel->string->nextpart->data.string,
692 		  	(strlen(libobj->name) + 1) * sizeof(char));
693 	       strcpy(curlabel->string->nextpart->data.string, libobj->name);
694 	       redrawtext(curlabel);
695 	    }
696 	    AddObjectTechnology(libobj);
697 
698 	    /* If the technology name has changed, then both the old	*/
699 	    /* technology and the new technology need to be marked as	*/
700 	    /* having been modified.					*/
701 
702 	    newtech = GetObjectTechnology(libobj);
703 
704 	    if (oldtech != newtech) {
705 	       if (oldtech) oldtech->flags |= (u_char)TECH_CHANGED;
706 	       if (newtech) newtech->flags |= (u_char)TECH_CHANGED;
707 	    }
708 	 }
709 
710 	 /* Check if we altered a page name */
711 	 else if ((libobj = NameToPageObject(oldname->nextpart->data.string,
712 		NULL, &page)) != NULL) {
713 	    strcpy(libobj->name, curlabel->string->nextpart->data.string);
714 	    renamepage(page);
715 	 }
716 
717 	 /* Check if we altered a library name */
718 	 else if ((libnum = NameToLibrary(oldname->nextpart->data.string)) != -1) {
719 	    libobj = xobjs.libtop[libnum + LIBRARY]->thisobject;
720 	    strcpy(libobj->name, curlabel->string->nextpart->data.string);
721 	 }
722  	 else {
723 	    Wprintf("Error:  Cannot match name to any object, page, or library!");
724 	    refresh(NULL, NULL, NULL);
725          }
726          areawin->redraw_needed = False; /* ignore previous redraw requests */
727    	 text_mode_draw(xcDRAW_FINAL, curlabel);
728 
729          eventmode = CATALOG_MODE;
730       }
731       else {	/* (hasstuff && eventmode != CATTEXT_MODE) */
732          areawin->redraw_needed = False; /* ignore previous redraw requests */
733    	 text_mode_draw(xcDRAW_FINAL, curlabel);
734 	 eventmode = NORMAL_MODE;
735 	 incr_changes(topobject);
736 	 if (curlabel->pin != False) invalidate_netlist(topobject);
737       }
738 
739       setdefaultfontmarks();
740       setcolormark(areawin->color);
741 
742       if (!hasstuff) {
743 	 /* Delete empty labels */
744 	 standard_element_delete(NORMAL);
745       }
746       else if ((labelbuf = get_original_string(curlabel)) != NULL) {
747 
748 	 /* If the original label (before modification) is a pin in a	*/
749 	 /* schematic/symbol with a matching symbol/schematic, and the	*/
750 	 /* name is unique, change every pin in the matching symbol/	*/
751 	 /* schematic to match the new text.				*/
752 
753 	 if ((curlabel->pin == LOCAL) && (topobject->symschem != NULL) &&
754 			(topobject->symschem->schemtype != PRIMARY)) {
755 	    if ((findlabelcopy(curlabel, labelbuf) == NULL)
756 			&& (findlabelcopy(curlabel, curlabel->string) == NULL)) {
757 	       if (changeotherpins(curlabel, labelbuf) > 0) {
758 	          if (topobject->schemtype == PRIMARY ||
759 				topobject->schemtype == SECONDARY)
760 	             Wprintf("Changed corresponding pin in associated symbol");
761 	          else
762 	             Wprintf("Changed corresponding pin in associated schematic");
763 	          incr_changes(topobject->symschem);
764 	          invalidate_netlist(topobject->symschem);
765 	       }
766 	    }
767 	 }
768 
769 	 resolveparams(areawin->topinstance);
770          updateinstparam(topobject);
771 	 setobjecttype(topobject);
772       }
773       else
774          calcbbox(areawin->topinstance);
775 
776       unselect_all();
777       return r;
778    }
779    else if (clientdata != NULL && keypressed == TEXT_RIGHT) {
780       if (curpos != NULL) areawin->textpos++;
781    }
782    else if (clientdata != NULL && keypressed == TEXT_LEFT) {
783       if (areawin->textpos > 1) areawin->textpos--;
784    }
785    else if (clientdata != NULL && keypressed == TEXT_DOWN) {
786       while (curpos != NULL) {
787 	 areawin->textpos++;
788 	 curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
789 			areawin->topinstance);
790 	 if (curpos != NULL)
791 	    if (curpos->type == RETURN || curpos->type == MARGINSTOP)
792 	       break;
793       }
794    }
795    else if (clientdata != NULL && keypressed == TEXT_UP) {
796       while (areawin->textpos > 1) {
797 	 areawin->textpos--;
798 	 curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
799 	 		areawin->topinstance);
800 	 if (curpos->type == RETURN || curpos->type == MARGINSTOP) {
801 	    if (areawin->textpos > 1) areawin->textpos--;
802 	    break;
803 	 }
804       }
805    }
806    else if (clientdata != NULL && keypressed == TEXT_HOME)
807       areawin->textpos = 1;
808    else if (clientdata != NULL && keypressed == TEXT_END)
809       areawin->textpos = stringlength(curlabel->string, True, areawin->topinstance);
810    else if (clientdata != NULL && keypressed == TEXT_SPLIT) {
811       labelptr *newlabel;
812       XPoint points[4], points1[4], points2[4];
813 
814       /* Everything after the cursor gets dumped into a new label */
815 
816       if ((areawin->textpos > 1) && (curpos != NULL)) {
817 	 labelbbox(curlabel, points, areawin->topinstance);
818          undrawtext(curlabel);
819 	 NEW_LABEL(newlabel, topobject);
820 	 labeldefaults(*newlabel, curlabel->pin, curlabel->position.x,
821 		curlabel->position.y);
822          if (locpos > 0)
823             curpos = splitstring(areawin->textpos, &curlabel->string,
824 			areawin->topinstance);
825 	 /* move back one position to find end of top part of string */
826          curpos = splitstring(areawin->textpos - 1, &curlabel->string,
827 			areawin->topinstance);
828 	 if (curpos->nextpart->type == FONT_NAME) {
829 	    freelabel((*newlabel)->string);
830 	    (*newlabel)->string = curpos->nextpart;
831 	 }
832 	 else {
833 	    (*newlabel)->string->data.font = curlabel->string->data.font;
834 	    (*newlabel)->string->nextpart = curpos->nextpart;
835 	 }
836 	 curpos->nextpart = NULL;
837 
838 	 /* Adjust position of both labels to retain their original	*/
839 	 /* relative positions.						*/
840 
841 	 labelbbox(curlabel, points1, areawin->topinstance);
842 	 labelbbox((*newlabel), points2, areawin->topinstance);
843 	 curlabel->position.x += (points[1].x - points1[1].x);
844 	 curlabel->position.y += (points[1].y - points1[1].y);
845 	 (*newlabel)->position.x += (points[3].x - points2[3].x);
846 	 (*newlabel)->position.y += (points[3].y - points2[3].y);
847 
848          redrawtext(*newlabel);
849          do_redraw = True;
850       }
851    }
852 
853    /* Write a font designator or other control into the string */
854 
855    else if (clientdata != NULL) {
856       oparamptr ops;
857       stringpart *newpart;
858       Boolean errcond = False;
859       TextLinesInfo tlinfo;
860 
861       /* erase first before redrawing unless the string is empty */
862       undrawtext(curlabel);
863 
864       /* Get text width first.  Don't back up over spaces;  this	*/
865       /* allows the margin width to be padded out with spaces.		*/
866 
867       if (keypressed == MARGINSTOP) {
868 	 tlinfo.dostop = areawin->textpos;
869 	 tlinfo.tbreak = NULL;
870 	 tlinfo.padding = NULL;
871 	 tmpext = ULength(curlabel, areawin->topinstance, &tlinfo);
872 	 if (tlinfo.padding != NULL) free(tlinfo.padding);
873       }
874 
875       if (locpos > 0) {
876 	 if (keypressed == MARGINSTOP) {
877 	    /* Move forward by any spaces; if we're at the text */
878 	    /* end, move to the next text part;  otherwise,	*/
879 	    /* split the string.				*/
880 
881 	    while (*(curpos->data.string + locpos) == ' ') locpos++;
882 	    if (*(curpos->data.string + locpos) == '\0') locpos = 0;
883 	 }
884 	 if (locpos > 0)
885             curpos = splitstring(areawin->textpos, &curlabel->string,
886 			areawin->topinstance);
887 	 curpos = curpos->nextpart;
888       }
889       newpart = makesegment(&curlabel->string, curpos);
890       newpart->type = keypressed;
891       switch (keypressed) {
892 	 case RETURN:
893 	    /* Identify this as an explicitly placed line break */
894 	    newpart->data.flags = 0;
895 	    break;
896 	 case FONT_SCALE:
897 	    newpart->data.scale = *((float *)clientdata);
898 	    break;
899 	 case KERN:
900 	    newpart->data.kern[0] = *((short *)clientdata);
901 	    newpart->data.kern[1] = *((short *)clientdata + 1);
902 	    break;
903 	 case FONT_COLOR:
904 	    newpart->data.color = *((int *)clientdata);
905 	    if (newpart->data.color >= number_colors) errcond = True;
906 	    break;
907 	 case FONT_NAME:
908 	    newpart->data.font = *((int *)clientdata);
909 	    if (newpart->data.font >= fontcount) errcond = True;
910 	    break;
911 	 case MARGINSTOP:
912 	    /* A margin of 1 or 0 is useless, so such a value	*/
913 	    /* indicates to take the margin from the current	*/
914 	    /* position.					*/
915 
916 	    if (*((int *)clientdata) <= 1)
917 	       newpart->data.width = (int)tmpext.width;
918 	    else
919 	       newpart->data.width = *((int *)clientdata);
920 	    CheckMarginStop(curlabel, areawin->topinstance, FALSE);
921 	    break;
922 	 case PARAM_START:
923 	    newpart->data.string = (char *)malloc(1 + strlen(clientdata));
924 	    strcpy(newpart->data.string, clientdata);
925 	    ops = match_param(topobject, clientdata);
926 	    if (ops == NULL) errcond = True;
927 	    else {
928 	       /* Forward edit cursor position to the end of the parameter */
929 	       do {
930 	          areawin->textpos++;
931 	          curpos = findstringpart(areawin->textpos, &locpos, curlabel->string,
932 	 		areawin->topinstance);
933 	       } while (curpos && (curpos->type != PARAM_END));
934 	    }
935 	    break;
936       }
937       if (errcond == True) {
938 	 Wprintf("Error in insertion.  Ignoring.");
939 	 deletestring(newpart, &curlabel->string, areawin->topinstance);
940 	 r = FALSE;
941       }
942       else {
943          areawin->textpos++;
944       }
945       do_redraw = True;
946    }
947 
948    /* Append the character to the string.  If the current label segment is	*/
949    /* not text, then create a text segment for it.				*/
950 
951    else if (keypressed > 0 && keypressed < 256) {
952       stringpart *lastpos;
953 
954       /* erase first. */
955       undrawtext(curlabel);
956 
957       /* Current position is not in a text string */
958       if (locpos < 0) {
959 
960          /* Find part of string which is immediately in front of areawin->textpos */
961          lastpos = findstringpart(areawin->textpos - 1, &locpos, curlabel->string,
962 		areawin->topinstance);
963 
964 	 /* No text on either side to attach to: make a new text segment */
965 	 if (locpos < 0) {
966 	    curpos = makesegment(&curlabel->string, curpos);
967 	    curpos->type = TEXT_STRING;
968 	    curpos->data.string = (u_char *) malloc(2);
969 	    curpos->data.string[0] = keypressed;
970 	    curpos->data.string[1] = '\0';
971 	 }
972 	 else {		/* append to end of lastpos text string */
973 	    int slen = strlen(lastpos->data.string);
974 	    lastpos->data.string = (u_char *) realloc(lastpos->data.string,
975 		2 +  slen);
976 	    *(lastpos->data.string + slen) = keypressed;
977 	    *(lastpos->data.string + slen + 1) = '\0';
978 	 }
979       }
980       else {	/* prepend to end of curpos text string */
981          curpos->data.string = (u_char *) realloc(curpos->data.string,
982 	     2 + strlen(curpos->data.string));
983 	 memmove(curpos->data.string + locpos + 1, curpos->data.string + locpos,
984 		strlen(curpos->data.string + locpos) + 1);
985          *(curpos->data.string + locpos) = keypressed;
986       }
987       areawin->textpos++;	/* move forward to next text position */
988       do_redraw = True;
989       r = TRUE;
990    }
991 
992    /* Redraw the label */
993 
994    if (do_redraw) {
995       /* Generate automatic line breaks if there is a MARGINSTOP directive */
996       CheckMarginStop(curlabel, areawin->topinstance, TRUE);
997 
998       redrawtext(curlabel);
999    }
1000 
1001    areawin->redraw_needed = False; /* ignore previous full redraw requests */
1002    text_mode_draw(xcDRAW_EDIT, curlabel);
1003 
1004    if (r || do_redraw) {
1005 
1006       /* Report on characters at the cursor position in the message window */
1007 
1008       charreport(curlabel);
1009 
1010       /* find current font and adjust menubuttons as necessary */
1011 
1012       cfont = findcurfont(areawin->textpos, curlabel->string, areawin->topinstance);
1013       if (cfont < 0) {
1014          Wprintf("Error:  Illegal label string");
1015          return r;
1016       }
1017       else
1018          setfontmarks(cfont, -1);
1019 
1020       areawin->textend = 0;
1021    }
1022    return r;
1023 }
1024 
1025 /*-------------------------------------*/
1026 /* Initiate return from text edit mode */
1027 /*-------------------------------------*/
1028 
textreturn()1029 void textreturn()
1030 {
1031    labeltext(TEXT_RETURN, (char *)1);
1032 }
1033 
1034 /*--------------------------------------*/
1035 /* Change the anchoring of a label	*/
1036 /*--------------------------------------*/
1037 
reanchor(short mode)1038 void reanchor(short mode)
1039 {
1040    labelptr curlabel = NULL;
1041    short    *tsel;
1042    short jsave;
1043    Boolean preselected = False, changed = False;
1044    static short transanchor[] = {15, 13, 12, 7, 5, 4, 3, 1, 0};
1045 
1046    if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) {
1047       curlabel = TOLABEL(EDITPART);
1048       UDrawTLine(curlabel);
1049       undrawtext(curlabel);
1050       jsave = curlabel->anchor;
1051       curlabel->anchor = transanchor[mode] |
1052 		(curlabel->anchor & NONANCHORFIELD);
1053       if (jsave != curlabel->anchor) {
1054 	 register_for_undo(XCF_Anchor, UNDO_MORE, areawin->topinstance,
1055 			(genericptr)curlabel, (int)jsave);
1056 	 changed = True;
1057       }
1058       redrawtext(curlabel);
1059       UDrawTLine(curlabel);
1060 
1061       setfontmarks(-1, curlabel->anchor);
1062    }
1063    else {
1064       if (areawin->selects == 0) {
1065 	 if (!checkselect(LABEL))
1066 	    return;
1067       }
1068       else preselected = TRUE;
1069 
1070       for (tsel = areawin->selectlist; tsel < areawin->selectlist +
1071 	      areawin->selects; tsel++) {
1072 	 if (SELECTTYPE(tsel) == LABEL) {
1073 	    curlabel = SELTOLABEL(tsel);
1074             jsave = curlabel->anchor;
1075 	    undrawtext(curlabel);
1076       	    curlabel->anchor = transanchor[mode] |
1077 		(curlabel->anchor & NONANCHORFIELD);
1078             if (jsave != curlabel->anchor) {
1079 	       register_for_undo(XCF_Anchor, UNDO_MORE, areawin->topinstance,
1080 			(genericptr)curlabel, (int)jsave);
1081 	       changed = True;
1082 	    }
1083 	 }
1084       }
1085       if (preselected == FALSE && eventmode != MOVE_MODE && eventmode != COPY_MODE)
1086 	 unselect_all();
1087       else
1088 	 draw_all_selected();
1089    }
1090    if (curlabel == NULL)
1091       Wprintf("No labels chosen to reanchor");
1092    else if (changed) {
1093       pwriteback(areawin->topinstance);
1094       calcbbox(areawin->topinstance);
1095       incr_changes(topobject);
1096    }
1097 }
1098 
1099 /*----------------------------------*/
1100 /* Sane default values for a spline */
1101 /*----------------------------------*/
1102 
splinedefaults(splineptr newspline,int x,int y)1103 void splinedefaults(splineptr newspline, int x, int y)
1104 {
1105    short j;
1106 
1107    for (j = 0; j < 4; j++) {
1108       newspline->ctrl[j].x = x;
1109       newspline->ctrl[j].y = y;
1110    }
1111    newspline->ctrl[1].x += (int)(xobjs.pagelist[areawin->page]->gridspace / 2);
1112    newspline->ctrl[2].x -= (int)(xobjs.pagelist[areawin->page]->gridspace / 2);
1113    newspline->width = areawin->linewidth;
1114    newspline->style = areawin->style;
1115    newspline->color = areawin->color;
1116    newspline->passed = NULL;
1117    newspline->cycle = NULL;
1118    calcspline(newspline);
1119 }
1120 
1121 /*-------------------------*/
1122 /* Start drawing a spline. */
1123 /*-------------------------*/
1124 
splinebutton(int x,int y)1125 void splinebutton(int x, int y)
1126 {
1127    splineptr *newspline;
1128    XPoint userpt;
1129    short *newselect;
1130 
1131    unselect_all();
1132    NEW_SPLINE(newspline, topobject);
1133    newselect = allocselect();
1134    *newselect = topobject->parts - 1;
1135 
1136    snap(x, y, &userpt);
1137    splinedefaults(*newspline, userpt.x, userpt.y);
1138    addcycle((genericptr *)newspline, 3, 0);
1139    makerefcycle((*newspline)->cycle, 3);
1140 
1141    spline_mode_draw(xcDRAW_EDIT, *newspline);
1142 
1143    xcAddEventHandler(areawin->area, PointerMotionMask, False,
1144         (xcEventHandler)trackelement, NULL);
1145 
1146    eventmode = SPLINE_MODE;
1147 }
1148 
1149 /*----------------------------------------------------------------------*/
1150 /* Generate cycles on a path where endpoints meet, so that the path	*/
1151 /* remains connected during an edit.  If the last point on any part	*/
1152 /* of the path is a cycle, then the first point on the next part of	*/
1153 /* the path should also be a cycle, with the same flags.		*/
1154 /*									*/
1155 /* If the global setting "tangents" is set, then the control points of	*/
1156 /* connecting splines set the corresponding control point to "ANTIXY"	*/
1157 /* so that the control points track angle and distance from the		*/
1158 /* endpoint.								*/
1159 /*----------------------------------------------------------------------*/
1160 
updatepath(pathptr thepath)1161 void updatepath(pathptr thepath)
1162 {
1163    genericptr *ggen, *ngen;
1164    short locparts, cycle;
1165    pointselect *cptr;
1166    polyptr thispoly;
1167    splineptr thisspline;
1168 
1169    for (ggen = thepath->plist; ggen < thepath->plist + thepath->parts; ggen++) {
1170       switch (ELEMENTTYPE(*ggen)) {
1171 	 case POLYGON:
1172 	    findconstrained(TOPOLY(ggen));
1173 	    break;
1174       }
1175    }
1176 
1177    locparts = (thepath->style & UNCLOSED) ? thepath->parts - 1 : thepath->parts;
1178    for (ggen = thepath->plist; ggen < thepath->plist + locparts; ggen++) {
1179       ngen = (ggen == thepath->plist + thepath->parts - 1) ? thepath->plist : ggen + 1;
1180 
1181       switch (ELEMENTTYPE(*ggen)) {
1182 	 case POLYGON:
1183 	    thispoly = TOPOLY(ggen);
1184 	    if (thispoly->cycle == NULL) continue;
1185 	    cycle = thispoly->number - 1;
1186 	    for (cptr = thispoly->cycle;; cptr++) {
1187 	       if (cptr->number == cycle) break;
1188 	       if (cptr->flags & LASTENTRY) break;
1189 	    }
1190 	    if (cptr->number != cycle) continue;
1191 	    break;
1192 	 case SPLINE:
1193 	    thisspline = TOSPLINE(ggen);
1194 	    if (thisspline->cycle == NULL) continue;
1195 	    cycle = 3;
1196 	    for (cptr = thisspline->cycle;; cptr++) {
1197 	       if (cptr->number == cycle) break;
1198 	       if (cptr->flags & LASTENTRY) break;
1199 	    }
1200 	    if (cptr->number != cycle) continue;
1201 	    break;
1202       }
1203       addcycle(ngen, 0, cptr->flags & (EDITX | EDITY));
1204       switch (ELEMENTTYPE(*ngen)) {
1205 	 case POLYGON:
1206 	    findconstrained(TOPOLY(ngen));
1207 	    break;
1208       }
1209    }
1210 
1211    /* Do the same thing in the other direction */
1212    locparts = (thepath->style & UNCLOSED) ? 1 : 0;
1213    for (ggen = thepath->plist + thepath->parts - 1; ggen >= thepath->plist + locparts;
1214 		ggen--) {
1215       ngen = (ggen == thepath->plist) ? thepath->plist + thepath->parts - 1 : ggen - 1;
1216 
1217       switch (ELEMENTTYPE(*ggen)) {
1218 	 case POLYGON:
1219 	    thispoly = TOPOLY(ggen);
1220 	    if (thispoly->cycle == NULL) continue;
1221 	    cycle = 0;
1222 	    for (cptr = thispoly->cycle;; cptr++) {
1223 	       if (cptr->number == cycle) break;
1224 	       if (cptr->flags & LASTENTRY) break;
1225 	    }
1226 	    if (cptr->number != cycle) continue;
1227 	    break;
1228 	 case SPLINE:
1229 	    thisspline = TOSPLINE(ggen);
1230 	    if (thisspline->cycle == NULL) continue;
1231 	    cycle = 0;
1232 	    for (cptr = thisspline->cycle;; cptr++) {
1233 	       if (cptr->number == cycle) break;
1234 	       if (cptr->flags & LASTENTRY) break;
1235 	    }
1236 	    if (cptr->number != cycle) continue;
1237 	    break;
1238       }
1239       switch (ELEMENTTYPE(*ngen)) {
1240 	 case POLYGON:
1241             addcycle(ngen, TOPOLY(ngen)->number - 1, cptr->flags & (EDITX | EDITY));
1242 	    break;
1243 	 case SPLINE:
1244             addcycle(ngen, 3, cptr->flags & (EDITX | EDITY));
1245 	    break;
1246       }
1247    }
1248 }
1249 
1250 /*--------------------------------------*/
1251 /* Set default values for an arc	*/
1252 /*--------------------------------------*/
1253 
arcdefaults(arcptr newarc,int x,int y)1254 void arcdefaults(arcptr newarc, int x, int y)
1255 {
1256    newarc->style = areawin->style;
1257    newarc->color = areawin->color;
1258    newarc->position.x = x;
1259    newarc->position.y = y;
1260    newarc->width = areawin->linewidth;
1261    newarc->radius = 0;
1262    newarc->yaxis = 0;
1263    newarc->angle1 = 0;
1264    newarc->angle2 = 360;
1265    newarc->passed = NULL;
1266    newarc->cycle = NULL;
1267    calcarc(newarc);
1268 }
1269 
1270 /*-------------------------------------*/
1271 /* Button handler when creating an arc */
1272 /*-------------------------------------*/
1273 
arcbutton(int x,int y)1274 void arcbutton(int x, int y)
1275 {
1276    arcptr *newarc;
1277    XPoint userpt;
1278    short *newselect;
1279 
1280    unselect_all();
1281    NEW_ARC(newarc, topobject);
1282    newselect = allocselect();
1283    *newselect = topobject->parts - 1;
1284    snap(x, y, &userpt);
1285    saveratio = 1.0;
1286    arcdefaults(*newarc, userpt.x, userpt.y);
1287    addcycle((genericptr *)newarc, 0, 0);
1288 
1289    arc_mode_draw(xcDRAW_EDIT, *newarc);
1290 
1291    xcAddEventHandler(areawin->area, PointerMotionMask, False,
1292         (xcEventHandler)trackarc, NULL);
1293 
1294    eventmode = ARC_MODE;
1295 }
1296 
1297 /*----------------------------------*/
1298 /* Track an arc during mouse motion */
1299 /*----------------------------------*/
1300 
trackarc(xcWidget w,caddr_t clientdata,caddr_t calldata)1301 void trackarc(xcWidget w, caddr_t clientdata, caddr_t calldata)
1302 {
1303    XPoint newpos;
1304    arcptr newarc;
1305    double adjrat;
1306    short  cycle;
1307    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1308 
1309    newarc = TOARC(EDITPART);
1310 
1311    newpos = UGetCursorPos();
1312    u2u_snap(&newpos);
1313    if (areawin->save.x == newpos.x && areawin->save.y == newpos.y) return;
1314 
1315    cycle = (newarc->cycle == NULL) ? -1 : newarc->cycle->number;
1316    if (cycle == 1 || cycle == 2) {
1317       float *angleptr, tmpang;
1318 
1319       adjrat = (newarc->yaxis == 0) ? 1 :
1320 		(double)(abs(newarc->radius)) / (double)newarc->yaxis;
1321       angleptr = (cycle == 1) ? &newarc->angle1 : &newarc->angle2;
1322       tmpang = (float)(atan2((double)(newpos.y - newarc->position.y) * adjrat,
1323 	   (double)(newpos.x - newarc->position.x)) / RADFAC);
1324       if (cycle == 1) {
1325 	 if (tmpang > newarc->angle2) tmpang -= 360;
1326 	 else if (newarc->angle2 - tmpang > 360) newarc->angle2 -= 360;
1327       }
1328       else {
1329          if (tmpang < newarc->angle1) tmpang += 360;
1330 	 else if (tmpang - newarc->angle1 > 360) newarc->angle1 += 360;
1331       }
1332       *angleptr = tmpang;
1333 
1334       if (newarc->angle2 <= 0) {
1335 	 newarc->angle2 += 360;
1336 	 newarc->angle1 += 360;
1337       }
1338       if (newarc->angle2 <= newarc->angle1)
1339 	 newarc->angle1 -= 360;
1340    }
1341    else if (cycle == 0) {
1342       short direc = (newarc->radius < 0);
1343       newarc->radius = wirelength(&newpos, &(newarc->position));
1344       newarc->yaxis = (short)((double)newarc->radius * saveratio);
1345       if (direc) newarc->radius = -newarc->radius;
1346    }
1347    else {
1348       newarc->yaxis = wirelength(&newpos, &(newarc->position));
1349       saveratio = (double)newarc->yaxis / (double)newarc->radius;
1350    }
1351 
1352    calcarc(newarc);
1353 
1354    areawin->save.x = newpos.x;
1355    areawin->save.y = newpos.y;
1356 
1357    arc_mode_draw(xcDRAW_EDIT, newarc);
1358    printpos(newpos.x, newpos.y);
1359 
1360    flusharea();
1361 }
1362 
1363 /*--------------------------------------*/
1364 /* Sane default values for a polygon	*/
1365 /*--------------------------------------*/
1366 
polydefaults(polyptr newpoly,int number,int x,int y)1367 void polydefaults(polyptr newpoly, int number, int x, int y)
1368 {
1369    pointlist pointptr;
1370 
1371    newpoly->style = areawin->style & ~UNCLOSED;
1372    newpoly->color = areawin->color;
1373    newpoly->width = areawin->linewidth;
1374    newpoly->number = number;
1375    newpoly->passed = NULL;
1376    newpoly->cycle = NULL;
1377    if (number == 0)
1378       newpoly->points = NULL;
1379    else {
1380       newpoly->points = (pointlist) malloc(number * sizeof(XPoint));
1381 
1382       for (pointptr = newpoly->points; pointptr < newpoly->points + number;
1383 		pointptr++) {
1384          pointptr->x = x;
1385          pointptr->y = y;
1386       }
1387    }
1388 }
1389 
1390 /*------------------------------------*/
1391 /* Button handler when creating a box */
1392 /*------------------------------------*/
1393 
boxbutton(int x,int y)1394 void boxbutton(int x, int y)
1395 {
1396    polyptr *newbox;
1397    XPoint userpt;
1398    short *newselect;
1399 
1400    unselect_all();
1401    NEW_POLY(newbox, topobject);
1402    newselect = allocselect();
1403    *newselect = topobject->parts - 1;
1404    snap(x, y, &userpt);
1405    polydefaults(*newbox, 4, userpt.x, userpt.y);
1406 
1407    poly_mode_draw(xcDRAW_EDIT, *newbox);
1408 
1409    xcAddEventHandler(areawin->area, PointerMotionMask, False,
1410         (xcEventHandler)trackbox, NULL);
1411 
1412    eventmode = BOX_MODE;
1413 }
1414 
1415 /*---------------------------------*/
1416 /* Track a box during mouse motion */
1417 /*---------------------------------*/
1418 
trackbox(xcWidget w,caddr_t clientdata,caddr_t calldata)1419 void trackbox(xcWidget w, caddr_t clientdata, caddr_t calldata)
1420 {
1421    XPoint 	newpos;
1422    polyptr      newbox;
1423    pointlist	pointptr;
1424    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1425 
1426    newbox = TOPOLY(EDITPART);
1427    newpos = UGetCursorPos();
1428    u2u_snap(&newpos);
1429 
1430    if (areawin->save.x == newpos.x && areawin->save.y == newpos.y) return;
1431 
1432    pointptr = newbox->points + 1; pointptr->y = newpos.y;
1433    pointptr++; pointptr->y = newpos.y; pointptr->x = newpos.x;
1434    pointptr++; pointptr->x = newpos.x;
1435 
1436    poly_mode_draw(xcDRAW_EDIT, newbox);
1437    printpos(newpos.x, newpos.y);
1438 
1439    areawin->save.x = newpos.x;
1440    areawin->save.y = newpos.y;
1441 
1442    flusharea();
1443 }
1444 
1445 /*----------------------------------------------------------------------*/
1446 /* Track a wire during mouse motion					*/
1447 /* Note:  The manhattanize algorithm will change the effective cursor	*/
1448 /* position to keep the wire manhattan if the wire is only 1 segment.	*/
1449 /* It will change the previous point's position if the wire has more	*/
1450 /* than one segment.  They are called at different times to ensure the	*/
1451 /* wire redraw is correct.						*/
1452 /*----------------------------------------------------------------------*/
1453 
trackwire(xcWidget w,caddr_t clientdata,caddr_t calldata)1454 void trackwire(xcWidget w, caddr_t clientdata, caddr_t calldata)
1455 {
1456    XPoint newpos, upos, *tpoint;
1457    polyptr	newwire;
1458    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1459 
1460    newwire = TOPOLY(EDITPART);
1461 
1462    if (areawin->attachto >= 0) {
1463       upos = UGetCursorPos();
1464       findattach(&newpos, NULL, &upos);
1465    }
1466    else {
1467       newpos = UGetCursorPos();
1468       u2u_snap(&newpos);
1469       if (areawin->manhatn && (newwire->number == 2))
1470 	 manhattanize(&newpos, newwire, -1, TRUE);
1471    }
1472 
1473    if (areawin->save.x != newpos.x || areawin->save.y != newpos.y) {
1474       tpoint = newwire->points + newwire->number - 1;
1475       if (areawin->manhatn && (newwire->number > 2))
1476 	 manhattanize(&newpos, newwire, -1, TRUE);
1477       tpoint->x = newpos.x;
1478       tpoint->y = newpos.y;
1479       XcTopSetForeground(newwire->color);
1480       poly_mode_draw(xcDRAW_EDIT, newwire);
1481       areawin->save.x = newpos.x;
1482       areawin->save.y = newpos.y;
1483       printpos(newpos.x, newpos.y);
1484    }
1485 
1486    flusharea();
1487 }
1488 
1489 /*--------------------------*/
1490 /* Start drawing a polygon. */
1491 /*--------------------------*/
1492 
startwire(XPoint * userpt)1493 void startwire(XPoint *userpt)
1494 {
1495    polyptr *newwire;
1496    pointlist pointptr;
1497    short *newselect;
1498 
1499    unselect_all();
1500    NEW_POLY(newwire, topobject);
1501    newselect = allocselect();
1502    *newselect = topobject->parts - 1;
1503 
1504    /* always start unfilled, unclosed; can fix on next button-push. */
1505 
1506    (*newwire)->style = UNCLOSED | (areawin->style & (DASHED | DOTTED));
1507    (*newwire)->color = areawin->color;
1508    (*newwire)->number = 2;
1509    (*newwire)->width = areawin->linewidth;
1510    (*newwire)->points = (pointlist) malloc(2 * sizeof(XPoint));
1511    (*newwire)->passed = NULL;
1512    (*newwire)->cycle = NULL;
1513    pointptr = (*newwire)->points;
1514    pointptr->x = (pointptr + 1)->x = areawin->save.x = userpt->x;
1515    pointptr->y = (pointptr + 1)->y = areawin->save.y = userpt->y;
1516 
1517    poly_mode_draw(xcDRAW_EDIT, *newwire);
1518 
1519    xcAddEventHandler(areawin->area, PointerMotionMask, False,
1520           (xcEventHandler)trackwire, NULL);
1521 }
1522 
1523 /*--------------------------------------------------------------*/
1524 /* Find which points should track along with the edit point in	*/
1525 /* in polygon RHOMBOID or MANHATTAN edit modes.		    	*/
1526 /* (point number is stored in lastpoly->cycle)	    	    	*/
1527 /*								*/
1528 /* NOTE:  This routine assumes that either the points have just	*/
1529 /* been selected, or that advancecycle() has been called to	*/
1530 /* remove all previously recorded tracking points.		*/
1531 /*--------------------------------------------------------------*/
1532 
findconstrained(polyptr lastpoly)1533 void findconstrained(polyptr lastpoly)
1534 {
1535    XPoint *savept, *npt, *lpt;
1536    short cycle;
1537    short lflags, nflags;
1538    short lcyc, ncyc;
1539    pointselect *cptr, *nptr;
1540 
1541    if (areawin->boxedit == NORMAL) return;
1542 
1543    if (lastpoly->cycle == NULL) return;
1544 
1545    /* Set "process" flags on all original points */
1546    for (cptr = lastpoly->cycle;; cptr++) {
1547       cptr->flags |= PROCESS;
1548       if (cptr->flags & LASTENTRY) break;
1549    }
1550 
1551    cptr = lastpoly->cycle;
1552    while (1) {
1553       if (cptr->flags & PROCESS) {
1554 	 cptr->flags &= ~PROCESS;
1555          cycle = cptr->number;
1556          savept = lastpoly->points + cycle;
1557 
1558          /* find points before and after the edit point */
1559 
1560          lcyc = (cycle == 0) ? ((lastpoly->style & UNCLOSED) ?
1561 		-1 : lastpoly->number - 1) : cycle - 1;
1562          ncyc = (cycle == lastpoly->number - 1) ?
1563 		((lastpoly->style & UNCLOSED) ? -1 : 0) : cycle + 1;
1564 
1565          lpt = (lcyc == -1) ? NULL : lastpoly->points + lcyc;
1566          npt = (ncyc == -1) ? NULL : lastpoly->points + ncyc;
1567 
1568 	 lflags = nflags = NONE;
1569 
1570 	 /* Avoid attempting to manipulate degenerate points. */
1571 	 if (lpt != NULL && lpt->x == savept->x && lpt->y == savept->y) return;
1572 	 if (npt != NULL && npt->x == savept->x && npt->y == savept->y) return;
1573 
1574          /* two-point polygons (lines) are a degenerate case in RHOMBOID edit mode */
1575 
1576          if (areawin->boxedit != MANHATTAN && lastpoly->number <= 2) return;
1577 
1578          /* This is complicated but logical:  in MANHATTAN mode, boxes maintain */
1579          /* box shape.  In RHOMBOID modes, parallelagrams maintain shape.  The  */
1580          /* "savedir" variable determines which coordinate(s) of which point(s) */
1581          /* should track along with the edit point.			     */
1582 
1583          if (areawin->boxedit != RHOMBOIDY) {
1584             if (lpt != NULL) {
1585                if (lpt->y == savept->y) {
1586 	          lflags |= EDITY;
1587 	          if (areawin->boxedit == RHOMBOIDX && lpt->x != savept->x)
1588 		     lflags |= EDITX;
1589 	          else if (areawin->boxedit == RHOMBOIDA && npt != NULL) {
1590 	             if (npt->y != savept->y) nflags |= EDITX;
1591 		  }
1592 	       }
1593 	    }
1594             if (npt != NULL) {
1595                if (npt->y == savept->y) {
1596 	          nflags |= EDITY;
1597 	          if (areawin->boxedit == RHOMBOIDX && npt->x != savept->x)
1598 		     nflags |= EDITX;
1599 	          else if (areawin->boxedit == RHOMBOIDA && lpt != NULL) {
1600 	             if (lpt->y != savept->y) lflags |= EDITX;
1601 	          }
1602 	       }
1603 	    }
1604          }
1605          if (areawin->boxedit != RHOMBOIDX) {
1606             if (lpt != NULL) {
1607                if (lpt->x == savept->x) {
1608 	          lflags |= EDITX;
1609 	          if (areawin->boxedit == RHOMBOIDY && lpt->y != savept->y)
1610 		     lflags |= EDITY;
1611 	          else if (areawin->boxedit == RHOMBOIDA && npt != NULL) {
1612 	             if (npt->x != savept->x) nflags |= EDITY;
1613 	          }
1614 	       }
1615 	    }
1616             if (npt != NULL) {
1617                if (npt->x == savept->x) {
1618 	          nflags |= EDITX;
1619 	          if (areawin->boxedit == RHOMBOIDY && npt->y != savept->y)
1620 		     nflags |= EDITY;
1621 	          else if (areawin->boxedit == RHOMBOIDA && lpt != NULL) {
1622 	             if (lpt->x != savept->x) lflags |= EDITY;
1623 	          }
1624 	       }
1625 	    }
1626          }
1627 	 nptr = cptr + 1;
1628          if (lpt != NULL && lflags != 0) {
1629 	    addcycle((genericptr *)(&lastpoly), lcyc, lflags);
1630 	    cptr = nptr = lastpoly->cycle;
1631 	 }
1632          if (npt != NULL && nflags != 0) {
1633 	    addcycle((genericptr *)(&lastpoly), ncyc, nflags);
1634 	    cptr = nptr = lastpoly->cycle;
1635 	 }
1636       }
1637       else
1638 	 nptr = cptr + 1;
1639       if (cptr->flags & LASTENTRY) break;
1640       cptr = nptr;
1641    }
1642 }
1643 
1644 /*------------------------------------------------------*/
1645 /* Track movement of arc, spline, or polygon segments	*/
1646 /* during edit mode					*/
1647 /*------------------------------------------------------*/
1648 
trackelement(xcWidget w,caddr_t clientdata,caddr_t calldata)1649 void trackelement(xcWidget w, caddr_t clientdata, caddr_t calldata)
1650 {
1651    XPoint newpos, *curpt;
1652    short	*selobj;
1653    pointselect	*cptr;
1654    int		deltax, deltay;
1655    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
1656 
1657    newpos = UGetCursorPos();
1658    u2u_snap(&newpos);
1659 
1660    /* force attachment if required */
1661    if (areawin->attachto >= 0) {
1662       XPoint apos;
1663       findattach(&apos, NULL, &newpos);
1664       newpos = apos;
1665    }
1666 
1667    if (areawin->save.x == newpos.x && areawin->save.y == newpos.y) return;
1668 
1669    /* Find the reference point */
1670 
1671    cptr = getrefpoint(TOGENERIC(EDITPART), &curpt);
1672    switch(ELEMENTTYPE(TOGENERIC(EDITPART))) {
1673       case POLYGON:
1674          if (cptr == NULL)
1675 	    curpt = TOPOLY(EDITPART)->points;
1676 	 break;
1677       case SPLINE:
1678          if (cptr == NULL)
1679 	    curpt = &(TOSPLINE(EDITPART)->ctrl[0]);
1680 	 break;
1681       case ARC:
1682 	 curpt = &(TOARC(EDITPART)->position);
1683 	 break;
1684       case OBJINST:
1685 	 curpt = &(TOOBJINST(EDITPART)->position);
1686 	 break;
1687       case GRAPHIC:
1688 	 curpt = &(TOGRAPHIC(EDITPART)->position);
1689 	 break;
1690    }
1691    deltax = newpos.x - curpt->x;
1692    deltay = newpos.y - curpt->y;
1693 
1694    /* Now adjust all edited elements relative to the reference point */
1695    for (selobj = areawin->selectlist; selobj < areawin->selectlist +
1696 	 areawin->selects; selobj++) {
1697       if (eventmode == ARC_MODE || eventmode == EARC_MODE)
1698          editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1699       else if (eventmode == SPLINE_MODE || eventmode == ESPLINE_MODE)
1700          editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1701       else if (eventmode == BOX_MODE || eventmode == EPOLY_MODE
1702    	    || eventmode == WIRE_MODE)
1703          editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1704       else if (eventmode == EPATH_MODE)
1705          editpoints(SELTOGENERICPTR(selobj), deltax, deltay);
1706    }
1707 
1708    if (eventmode == ARC_MODE || eventmode == EARC_MODE)
1709       arc_mode_draw(xcDRAW_EDIT, TOARC(EDITPART));
1710    else if (eventmode == SPLINE_MODE || eventmode == ESPLINE_MODE)
1711       spline_mode_draw(xcDRAW_EDIT, TOSPLINE(EDITPART));
1712    else if (eventmode == BOX_MODE || eventmode == EPOLY_MODE
1713 	 || eventmode == WIRE_MODE)
1714       poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1715    else if (eventmode == EPATH_MODE)
1716       path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1717 
1718    printpos(newpos.x, newpos.y);
1719    areawin->save.x = newpos.x;
1720    areawin->save.y = newpos.y;
1721 
1722    flusharea();
1723 }
1724 
1725 /*-------------------------------------------------*/
1726 /* Determine values of endpoints of an element	   */
1727 /*-------------------------------------------------*/
1728 
setendpoint(short * scnt,short direc,XPoint ** endpoint,XPoint * arcpoint)1729 void setendpoint(short *scnt, short direc, XPoint **endpoint, XPoint *arcpoint)
1730 {
1731    genericptr *sptr = topobject->plist + (*scnt);
1732 
1733    switch(ELEMENTTYPE(*sptr)) {
1734       case POLYGON:
1735 	 if (direc)
1736 	    *endpoint = TOPOLY(sptr)->points + TOPOLY(sptr)->number - 1;
1737 	 else
1738 	    *endpoint = TOPOLY(sptr)->points;
1739 	 break;
1740       case SPLINE:
1741 	 if (direc)
1742 	    *endpoint = &(TOSPLINE(sptr)->ctrl[3]);
1743 	 else
1744 	    *endpoint = &(TOSPLINE(sptr)->ctrl[0]);
1745 	 break;
1746       case ARC:
1747 	 if (direc) {
1748 	    arcpoint->x = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].x
1749 		+ 0.5);
1750 	    arcpoint->y = (short)(TOARC(sptr)->points[TOARC(sptr)->number - 1].y
1751 		+ 0.5);
1752 	 }
1753 	 else {
1754 	    arcpoint->x = (short)(TOARC(sptr)->points[0].x + 0.5);
1755 	    arcpoint->y = (short)(TOARC(sptr)->points[0].y + 0.5);
1756 	 }
1757 	 *endpoint = arcpoint;
1758 	 break;
1759    }
1760 }
1761 
1762 /*------------------------------------------------------------*/
1763 /* Reverse points in a point list			      */
1764 /*------------------------------------------------------------*/
1765 
reversepoints(XPoint * plist,short number)1766 void reversepoints(XPoint *plist, short number)
1767 {
1768    XPoint hold, *ppt;
1769    XPoint *pend = plist + number - 1;
1770    short hnum = number >> 1;
1771 
1772    for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
1773       hold.x = ppt->x;
1774       hold.y = ppt->y;
1775       ppt->x = pend->x;
1776       ppt->y = pend->y;
1777       pend->x = hold.x;
1778       pend->y = hold.y;
1779    }
1780 }
1781 
1782 /*------------------------------------------------------------*/
1783 /* Same as above for floating-point positions		      */
1784 /*------------------------------------------------------------*/
1785 
reversefpoints(XfPoint * plist,short number)1786 void reversefpoints(XfPoint *plist, short number)
1787 {
1788    XfPoint hold, *ppt;
1789    XfPoint *pend = plist + number - 1;
1790    short hnum = number >> 1;
1791 
1792    for (ppt = plist; ppt < plist + hnum; ppt++, pend--) {
1793       hold.x = ppt->x;
1794       hold.y = ppt->y;
1795       ppt->x = pend->x;
1796       ppt->y = pend->y;
1797       pend->x = hold.x;
1798       pend->y = hold.y;
1799    }
1800 }
1801 
1802 /*--------------------------------------------------------------*/
1803 /* Permanently remove an element from the topobject plist	*/
1804 /*	add = 1 if plist has (parts + 1) elements		*/
1805 /*--------------------------------------------------------------*/
1806 
freepathparts(short * selectobj,short add)1807 void freepathparts(short *selectobj, short add)
1808 {
1809    genericptr *oldelem = topobject->plist + (*selectobj);
1810    switch(ELEMENTTYPE(*oldelem)) {
1811       case POLYGON:
1812 	 free((TOPOLY(oldelem))->points);
1813 	 break;
1814    }
1815    free(*oldelem);
1816    removep(selectobj, add);
1817 }
1818 
1819 /*--------------------------------------------------------------*/
1820 /* Remove a part from an object					*/
1821 /* 	add = 1 if plist has (parts + 1) elements		*/
1822 /*--------------------------------------------------------------*/
1823 
removep(short * selectobj,short add)1824 void removep(short *selectobj, short add)
1825 {
1826    genericptr *oldelem = topobject->plist + (*selectobj);
1827 
1828    for (++oldelem; oldelem < topobject->plist + topobject->parts + add; oldelem++)
1829 	    *(oldelem - 1) = *oldelem;
1830 
1831    topobject->parts--;
1832 }
1833 
1834 /*-------------------------------------------------*/
1835 /* Break a path into its constituent components	   */
1836 /*-------------------------------------------------*/
1837 
unjoin()1838 void unjoin()
1839 {
1840    short *selectobj;
1841    genericptr *genp, *newg;
1842    pathptr oldpath;
1843    polyptr oldpoly, *newpoly;
1844    Boolean preselected;
1845    short i, cycle;
1846 
1847    if (areawin->selects == 0) {
1848       select_element(PATH | POLYGON);
1849       preselected = FALSE;
1850    }
1851    else preselected = TRUE;
1852 
1853    if (areawin->selects == 0) {
1854       Wprintf("No objects selected.");
1855       return;
1856    }
1857 
1858    /* for each selected path or polygon */
1859 
1860    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
1861         + areawin->selects; selectobj++) {
1862       SetForeground(dpy, areawin->gc, BACKGROUND);
1863       if (SELECTTYPE(selectobj) == PATH) {
1864          oldpath = SELTOPATH(selectobj);
1865 
1866          /* undraw the path */
1867 
1868          UDrawPath(oldpath, xobjs.pagelist[areawin->page]->wirewidth);
1869 
1870          /* move components to the top level */
1871 
1872 	 topobject->plist = (genericptr *)realloc(topobject->plist,
1873 		(topobject->parts + oldpath->parts) * sizeof(genericptr));
1874 	 newg = topobject->plist + topobject->parts;
1875 	 for (genp = oldpath->plist; genp < oldpath->plist +
1876 		oldpath->parts; genp++, newg++) {
1877 	    *newg = *genp;
1878 	 }
1879 	 topobject->parts += oldpath->parts;
1880 
1881          /* remove the path object and revise the selectlist */
1882 
1883          freepathparts(selectobj, 0);
1884          reviseselect(areawin->selectlist, areawin->selects, selectobj);
1885       }
1886       else if (SELECTTYPE(selectobj) == POLYGON) {
1887 	 /* Method to break a polygon, in lieu of the edit-mode	*/
1888 	 /* polygon break that was removed.			*/
1889          oldpoly = SELTOPOLY(selectobj);
1890          UDrawPolygon(oldpoly, xobjs.pagelist[areawin->page]->wirewidth);
1891 
1892 	 /* Get the point nearest the cursor, and break at that point */
1893          cycle = closepoint(oldpoly, &areawin->save);
1894 	 if (cycle > 0 && cycle < (oldpoly->number - 1)) {
1895             NEW_POLY(newpoly, topobject);
1896 	    polycopy(*newpoly, oldpoly);
1897 	    for (i = cycle; i < oldpoly->number; i++)
1898 	       (*newpoly)->points[i - cycle] = (*newpoly)->points[i];
1899 	    oldpoly->number = cycle + 1;
1900 	    (*newpoly)->number = (*newpoly)->number - cycle;
1901 	 }
1902       }
1903    }
1904    if (!preselected) clearselects();
1905    drawarea(NULL, NULL, NULL);
1906 }
1907 
1908 /*-------------------------------------------------*/
1909 /* Test if two points are near each other	   */
1910 /*-------------------------------------------------*/
1911 
neartest(XPoint * point1,XPoint * point2)1912 Boolean neartest(XPoint *point1, XPoint *point2)
1913 {
1914    short diff[2];
1915 
1916    diff[0] = point1->x - point2->x;
1917    diff[1] = point1->y - point2->y;
1918    diff[0] = abs(diff[0]);
1919    diff[1] = abs(diff[1]);
1920 
1921    if (diff[0] <= 2 && diff[1] <= 2) return True;
1922    else return False;
1923 }
1924 
1925 
1926 /*-------------------------------------------------*/
1927 /* Join stuff together 			   	   */
1928 /*-------------------------------------------------*/
1929 
join()1930 void join()
1931 {
1932    short     *selectobj;
1933    polyptr   *newpoly, nextwire;
1934    pathptr   *newpath;
1935    genericptr *pgen;
1936    short     *scount, *sptr, *sptr2, *direc, *order;
1937    short     ordered, startpt = 0;
1938    short     numpolys, numlabels, numpoints, polytype;
1939    int	     polycolor;
1940    float     polywidth;
1941    XPoint    *testpoint, *testpoint2, *begpoint, *endpoint, arcpoint[4];
1942    XPoint    *begpoint2, *endpoint2;
1943    Boolean   allpolys = True;
1944    objectptr delobj;
1945 
1946    numpolys = numlabels = 0;
1947    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
1948 	+ areawin->selects; selectobj++) {
1949       if (SELECTTYPE(selectobj) == POLYGON) {
1950 	 /* arbitrary:  keep style of last polygon in selectlist */
1951 	 polytype = SELTOPOLY(selectobj)->style;
1952 	 polywidth = SELTOPOLY(selectobj)->width;
1953 	 polycolor = SELTOPOLY(selectobj)->color;
1954 	 numpolys++;
1955       }
1956       else if (SELECTTYPE(selectobj) == SPLINE) {
1957 	 polytype = SELTOSPLINE(selectobj)->style;
1958 	 polywidth = SELTOSPLINE(selectobj)->width;
1959 	 polycolor = SELTOSPLINE(selectobj)->color;
1960 	 numpolys++;
1961 	 allpolys = False;
1962       }
1963       else if (SELECTTYPE(selectobj) == ARC) {
1964 	 polytype = SELTOARC(selectobj)->style;
1965 	 polywidth = SELTOARC(selectobj)->width;
1966 	 polycolor = SELTOARC(selectobj)->color;
1967 	 numpolys++;
1968 	 allpolys = False;
1969       }
1970       else if (SELECTTYPE(selectobj) == LABEL)
1971 	 numlabels++;
1972    }
1973    if ((numpolys == 0) && (numlabels == 0)) {
1974       Wprintf("No elements selected for joining.");
1975       return;
1976    }
1977    else if ((numpolys == 1) || (numlabels == 1)) {
1978       Wprintf("Only one element: nothing to join to.");
1979       return;
1980    }
1981    else if ((numpolys > 1) && (numlabels > 1)) {
1982       Wprintf("Selection mixes labels and line segments.  Ignoring.");
1983       return;
1984    }
1985    else if (numlabels > 0) {
1986       joinlabels();
1987       return;
1988    }
1989 
1990    /* scount is a table of element numbers 				*/
1991    /* order is an ordered table of end-to-end elements 			*/
1992    /* direc is an ordered table of path directions (0=same as element,	*/
1993    /* 	1=reverse from element definition)				*/
1994 
1995    scount = (short *) malloc(numpolys * sizeof(short));
1996    order  = (short *) malloc(numpolys * sizeof(short));
1997    direc  = (short *) malloc(numpolys * sizeof(short));
1998    sptr = scount;
1999    numpoints = 1;
2000 
2001    /* make a record of the element instances involved */
2002 
2003    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2004 	+ areawin->selects; selectobj++) {
2005       if (SELECTTYPE(selectobj) == POLYGON) {
2006 	  numpoints += SELTOPOLY(selectobj)->number - 1;
2007 	  *(sptr++) = *selectobj;
2008       }
2009       else if (SELECTTYPE(selectobj) == SPLINE || SELECTTYPE(selectobj) == ARC)
2010 	  *(sptr++) = *selectobj;
2011    }
2012 
2013    /* Sort the elements by sorting the scount array: 				*/
2014    /* Loop through each point as starting point in case of strangely connected 	*/
2015    /* structures. . . for normal structures it should break out on the first   	*/
2016    /* loop (startpt = 0).							*/
2017 
2018    for (startpt = 0; startpt < numpolys; startpt++) {
2019 
2020       /* set first in ordered list */
2021 
2022       direc[0] = 0;
2023       order[0] = *(scount + startpt);
2024 
2025       for (ordered = 0; ordered < numpolys - 1; ordered++) {
2026 
2027          setendpoint(order + ordered, (1 ^ direc[ordered]), &endpoint2, &arcpoint[0]);
2028          setendpoint(order, (0 ^ direc[0]), &begpoint2, &arcpoint[1]);
2029 
2030          for (sptr = scount; sptr < scount + numpolys; sptr++) {
2031 
2032 	    /* don't compare with things already in the list */
2033 	    for (sptr2 = order; sptr2 <= order + ordered; sptr2++)
2034 	       if (*sptr == *sptr2) break;
2035 	    if (sptr2 != order + ordered + 1) continue;
2036 
2037             setendpoint(sptr, 0, &begpoint, &arcpoint[2]);
2038             setendpoint(sptr, 1, &endpoint, &arcpoint[3]);
2039 
2040 	    /* four cases of matching endpoint of one element to another */
2041 
2042 	    if (neartest(begpoint, endpoint2)) {
2043 	       order[ordered + 1] = *sptr;
2044 	       direc[ordered + 1] = 0;
2045 	       break;
2046 	    }
2047 	    else if (neartest(endpoint, endpoint2)) {
2048 	       order[ordered + 1] = *sptr;
2049 	       direc[ordered + 1] = 1;
2050 	       break;
2051 	    }
2052 	    else if (neartest(begpoint, begpoint2)) {
2053 	       for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--)
2054 	          *sptr2 = *(sptr2 - 1);
2055 	       for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
2056 	          *sptr2 = *(sptr2 - 1);
2057 	       order[0] = *sptr;
2058 	       direc[0] = 1;
2059 	       break;
2060 	    }
2061 	    else if (neartest(endpoint, begpoint2)) {
2062 	       for (sptr2 = order + ordered + 1; sptr2 > order; sptr2--)
2063 	          *sptr2 = *(sptr2 - 1);
2064 	       for (sptr2 = direc + ordered + 1; sptr2 > direc; sptr2--)
2065 	          *sptr2 = *(sptr2 - 1);
2066 	       order[0] = *sptr;
2067 	       direc[0] = 0;
2068 	       break;
2069 	    }
2070          }
2071 	 if (sptr == scount + numpolys) break;
2072       }
2073       if (ordered == numpolys - 1) break;
2074    }
2075 
2076    if (startpt == numpolys) {
2077       Wprintf("Cannot join: Too many free endpoints");
2078       free(order);
2079       free(direc);
2080       free(scount);
2081       return;
2082    }
2083 
2084    /* create the new polygon or path */
2085 
2086    if (allpolys) {
2087       NEW_POLY(newpoly, topobject);
2088 
2089       (*newpoly)->number = numpoints;
2090       (*newpoly)->points = (pointlist) malloc(numpoints * sizeof(XPoint));
2091       (*newpoly)->width  = polywidth;
2092       (*newpoly)->style  = polytype;
2093       (*newpoly)->color  = polycolor;
2094       (*newpoly)->passed = NULL;
2095       (*newpoly)->cycle = NULL;
2096 
2097       /* insert the points into the new polygon */
2098 
2099       testpoint2 = (*newpoly)->points;
2100       for (sptr = order; sptr < order + numpolys; sptr++) {
2101          nextwire = SELTOPOLY(sptr);
2102 	 if (*(direc + (short)(sptr - order)) == 0) {
2103             for (testpoint = nextwire->points; testpoint < nextwire->points +
2104 		   nextwire->number - 1; testpoint++) {
2105 	       testpoint2->x = testpoint->x;
2106 	       testpoint2->y = testpoint->y;
2107 	       testpoint2++;
2108 	    }
2109          }
2110          else {
2111             for (testpoint = nextwire->points + nextwire->number - 1; testpoint
2112 		   > nextwire->points; testpoint--) {
2113 	       testpoint2->x = testpoint->x;
2114 	       testpoint2->y = testpoint->y;
2115 	       testpoint2++;
2116 	    }
2117 	 }
2118       }
2119       /* pick up the last point */
2120       testpoint2->x = testpoint->x;
2121       testpoint2->y = testpoint->y;
2122 
2123       /* delete the old elements from the list */
2124 
2125       register_for_undo(XCF_Wire, UNDO_MORE, areawin->topinstance, *newpoly);
2126 
2127       delobj = delete_element(areawin->topinstance, areawin->selectlist,
2128 		areawin->selects, NORMAL);
2129       register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
2130                 delobj, NORMAL);
2131 
2132    }
2133    else {	/* create a path */
2134 
2135       NEW_PATH(newpath, topobject);
2136       (*newpath)->style = polytype;
2137       (*newpath)->color = polycolor;
2138       (*newpath)->width = polywidth;
2139       (*newpath)->parts = 0;
2140       (*newpath)->plist = (genericptr *) malloc(sizeof(genericptr));
2141       (*newpath)->passed = NULL;
2142 
2143       /* copy the elements from the top level into the path structure */
2144 
2145       for (sptr = order; sptr < order + numpolys; sptr++) {
2146 	 genericptr *oldelem = topobject->plist + *sptr;
2147 	 genericptr *newelem;
2148 
2149 	 switch (ELEMENTTYPE(*oldelem)) {
2150 	    case POLYGON: {
2151 	       polyptr copypoly = TOPOLY(oldelem);
2152 	       polyptr *newpoly;
2153 	       NEW_POLY(newpoly, (*newpath));
2154 	       polycopy(*newpoly, copypoly);
2155 	    } break;
2156 	    case ARC: {
2157 	       arcptr copyarc = TOARC(oldelem);
2158 	       arcptr *newarc;
2159 	       NEW_ARC(newarc, (*newpath));
2160 	       arccopy(*newarc, copyarc);
2161 	    } break;
2162 	    case SPLINE: {
2163 	       splineptr copyspline = TOSPLINE(oldelem);
2164 	       splineptr *newspline;
2165 	       NEW_SPLINE(newspline, (*newpath));
2166 	       splinecopy(*newspline, copyspline);
2167 	    } break;
2168 	 }
2169 	 newelem = (*newpath)->plist + (*newpath)->parts - 1;
2170 
2171 	 /* reverse point order if necessary */
2172 
2173          if (*(direc + (short)(sptr - order)) == 1) {
2174 	    switch (ELEMENTTYPE(*newelem)) {
2175 	       case POLYGON:
2176 		  reversepoints(TOPOLY(newelem)->points, TOPOLY(newelem)->number);
2177 	          break;
2178 	       case ARC:
2179 		  TOARC(newelem)->radius = -TOARC(newelem)->radius;
2180 	          break;
2181 	       case SPLINE:
2182 		  reversepoints(TOSPLINE(newelem)->ctrl, 4);
2183 		  calcspline(TOSPLINE(newelem));
2184 	          break;
2185 	    }
2186 	 }
2187 
2188 	 /* decompose arcs into bezier curves */
2189 	 if (ELEMENTTYPE(*newelem) == ARC)
2190 	    decomposearc(*newpath, NULL);
2191       }
2192 
2193       /* delete the old elements from the list */
2194 
2195       register_for_undo(XCF_Join, UNDO_MORE, areawin->topinstance, *newpath);
2196 
2197       delobj = delete_element(areawin->topinstance, scount, numpolys, NORMAL);
2198 
2199       register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
2200                 delobj, NORMAL);
2201 
2202       /* Remove the path parts from the selection list and add the path */
2203       clearselects();
2204       selectobj = allocselect();
2205       for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
2206 		pgen++) {
2207 	 if ((TOPATH(pgen)) == (*newpath)) {
2208 	    *selectobj = (short)(pgen - topobject->plist);
2209 	    break;
2210 	 }
2211       }
2212    }
2213 
2214    /* clean up */
2215 
2216    incr_changes(topobject);
2217    /* Do not clear the selection, to be consistent with all the	*/
2218    /* other actions that clear only when something has not been	*/
2219    /* preselected before the action.  Elements must be selected	*/
2220    /* prior to the "join" action, by necessity.			*/
2221    free(scount);
2222    free(order);
2223    free(direc);
2224 }
2225 
2226 /*----------------------------------------------*/
2227 /* Add a new point to a polygon			*/
2228 /*----------------------------------------------*/
2229 
poly_add_point(polyptr thispoly,XPoint * newpoint)2230 void poly_add_point(polyptr thispoly, XPoint *newpoint) {
2231    XPoint *tpoint;
2232 
2233    thispoly->number++;
2234    thispoly->points = (XPoint *)realloc(thispoly->points,
2235 			thispoly->number * sizeof(XPoint));
2236    tpoint = thispoly->points + thispoly->number - 1;
2237    tpoint->x = newpoint->x;
2238    tpoint->y = newpoint->y;
2239 }
2240 
2241 /*-------------------------------------------------*/
2242 /* ButtonPress handler while a wire is being drawn */
2243 /*-------------------------------------------------*/
2244 
wire_op(int op,int x,int y)2245 void wire_op(int op, int x, int y)
2246 {
2247    XPoint userpt, *tpoint;
2248    polyptr newwire;
2249 
2250    snap(x, y, &userpt);
2251 
2252    newwire = TOPOLY(EDITPART);
2253 
2254    if (areawin->attachto >= 0) {
2255       XPoint apos;
2256       findattach(&apos, NULL, &userpt);
2257       userpt = apos;
2258       areawin->attachto = -1;
2259    }
2260    else {
2261       if (areawin->manhatn) manhattanize(&userpt, newwire, -1, TRUE);
2262    }
2263 
2264    tpoint = newwire->points + newwire->number - 1;
2265    tpoint->x = userpt.x;
2266    tpoint->y = userpt.y;
2267 
2268    /* cancel wire operation completely */
2269    if (op == XCF_Cancel) {
2270       free(newwire->points);
2271       free(newwire);
2272       newwire = NULL;
2273       eventmode = NORMAL_MODE;
2274       topobject->parts--;
2275    }
2276 
2277    /* back up one point; prevent length zero wires */
2278    else if ((op == XCF_Cancel_Last) || ((tpoint - 1)->x == userpt.x &&
2279 	   (tpoint - 1)->y == userpt.y)) {
2280       if (newwire->number <= 2) {
2281 	 free(newwire->points);
2282 	 free(newwire);
2283 	 newwire = NULL;
2284          eventmode = NORMAL_MODE;
2285          topobject->parts--;
2286       }
2287       else {
2288          if (--newwire->number == 2) newwire->style = UNCLOSED |
2289    		(areawin->style & (DASHED | DOTTED));
2290       }
2291    }
2292 
2293    if (newwire && (op == XCF_Wire || op == XCF_Continue_Element)) {
2294       if (newwire->number == 2)
2295 	 newwire->style = areawin->style;
2296       poly_add_point(newwire, &userpt);
2297    }
2298    else if ((newwire == NULL) || op == XCF_Finish_Element || op == XCF_Cancel) {
2299       xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
2300          (xcEventHandler)trackwire, NULL);
2301    }
2302 
2303    if (newwire) {
2304       if (op == XCF_Finish_Element) {
2305 
2306 	 /* If the last points are the same, remove all redundant ones.	*/
2307 	 /* This avoids the problem of extra points when people do left	*/
2308 	 /* click followed by middle click to finish (the redundant way	*/
2309 	 /* a lot of drawing programs work).				*/
2310 
2311 	 XPoint *t2pt;
2312 	 while (newwire->number > 2) {
2313 	    tpoint = newwire->points + newwire->number - 1;
2314 	    t2pt = newwire->points + newwire->number - 2;
2315 	    if (tpoint->x != t2pt->x || tpoint->y != t2pt->y)
2316 	       break;
2317 	    newwire->number--;
2318 	 }
2319 
2320 	 incr_changes(topobject);
2321 	 if (!nonnetwork(newwire)) invalidate_netlist(topobject);
2322 	 register_for_undo(XCF_Wire, UNDO_MORE, areawin->topinstance, newwire);
2323    	 poly_mode_draw(xcDRAW_FINAL, newwire);
2324       }
2325       else
2326    	 poly_mode_draw(xcDRAW_EDIT, newwire);
2327       if (op == XCF_Cancel_Last)
2328 	 checkwarp(newwire->points + newwire->number - 1);
2329    }
2330 
2331    if (op == XCF_Finish_Element) {
2332       eventmode = NORMAL_MODE;
2333       singlebbox(EDITPART);
2334    }
2335 }
2336 
2337 /*-------------------------------------------------------------------------*/
2338 /* Helper functions for the xxx_mode_draw functions			   */
2339 /* Functions to be used around the drawing of the edited element	   */
2340 /* begin_event_mode_drawing starts double buffering and copies the	   */
2341 /* fixed pixmap. end_event_mode stops the double buffering and displays	   */
2342 /* everything on screen.						   */
2343 /*-------------------------------------------------------------------------*/
2344 
2345 #ifndef HAVE_CAIRO
2346 static Window old_win;
2347 #endif /* !HAVE_CAIRO */
2348 
begin_event_mode_drawing(void)2349 static void begin_event_mode_drawing(void)
2350 {
2351    /* Start double buffering */
2352 #ifdef HAVE_CAIRO
2353    cairo_identity_matrix(areawin->cr);
2354    cairo_translate(areawin->cr, areawin->panx, areawin->pany);
2355    cairo_push_group(areawin->cr);
2356 #else /* HAVE_CAIRO */
2357    old_win = areawin->window;
2358    areawin->window = (Window) dbuf;
2359 #endif /* HAVE_CAIRO */
2360 
2361    /* Copy background pixmap with the element(s) not currently being edited */
2362 #ifdef HAVE_CAIRO
2363    if (areawin->panx || areawin->pany) {
2364       SetForeground(dpy, areawin->gc, BACKGROUND);
2365       cairo_paint(areawin->cr);
2366    }
2367    cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2368    cairo_paint(areawin->cr);
2369 #else /* HAVE_CAIRO */
2370    if (areawin->panx || areawin->pany) {
2371       int x = max(0, -areawin->panx);
2372       int y = max(0, -areawin->pany);
2373       unsigned int w = areawin->width - x;
2374       unsigned int h = areawin->height - y;
2375       SetForeground(dpy, areawin->gc, BACKGROUND);
2376       XSetFillStyle(dpy, areawin->gc, FillSolid);
2377       XFillRectangle(dpy, areawin->window, areawin->gc, 0, 0, areawin->width,
2378 	    areawin->height);
2379       XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc,
2380 	 x, y, w, h, max(0, areawin->panx), max(0, areawin->pany));
2381    }
2382    else
2383       XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc, 0, 0,
2384 	    areawin->width, areawin->height, 0, 0);
2385 #endif /* HAVE_CAIRO */
2386 
2387    areawin->redraw_ongoing = True;
2388    newmatrix();
2389 }
2390 
end_event_mode_drawing(void)2391 static void end_event_mode_drawing(void)
2392 {
2393    /* End double buffering */
2394 #ifdef HAVE_CAIRO
2395    cairo_pop_group_to_source(areawin->cr);
2396    cairo_paint(areawin->cr);
2397 #else /* HAVE_CAIRO */
2398    areawin->window = old_win;
2399    XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, areawin->width,
2400 	 areawin->height, 0, 0);
2401 #endif /* HAVE_CAIRO */
2402 
2403    areawin->redraw_ongoing = False;
2404 }
2405 
2406 /*-------------------------------------------------------------------------*/
2407 /* Helper functions for the xxx_mode_draw functions			   */
2408 /* Functions to be used when finishing the element. The final state is	   */
2409 /* drawn into the fixed pixmap, which is show when the			   */
2410 /* end_event_mode_drawing_final is called				   */
2411 /* (Sorry about the name :-) )						   */
2412 /*-------------------------------------------------------------------------*/
2413 
begin_event_mode_drawing_final(void)2414 static void begin_event_mode_drawing_final(void)
2415 {
2416 #ifdef HAVE_CAIRO
2417    cairo_identity_matrix(areawin->cr);
2418    cairo_push_group(areawin->cr);
2419    cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2420    cairo_paint(areawin->cr);
2421 #else /* HAVE_CAIRO */
2422    old_win = areawin->window;
2423    areawin->window = (Window) areawin->fixed_pixmap;
2424 #endif /* HAVE_CAIRO */
2425 
2426    areawin->redraw_ongoing = True;
2427    newmatrix();
2428 }
2429 
end_event_mode_drawing_final(void)2430 static void end_event_mode_drawing_final(void)
2431 {
2432 #ifdef HAVE_CAIRO
2433    cairo_pattern_destroy(areawin->fixed_pixmap);
2434    areawin->fixed_pixmap = cairo_pop_group(areawin->cr);
2435 #else /* HAVE_CAIRO */
2436    areawin->window = old_win;
2437 #endif /* HAVE_CAIRO */
2438 
2439    /* Show fixed pixmap */
2440 #ifdef HAVE_CAIRO
2441    cairo_identity_matrix(areawin->cr);
2442    cairo_set_source(areawin->cr, areawin->fixed_pixmap);
2443    cairo_paint(areawin->cr);
2444 #else /* HAVE_CAIRO */
2445    XCopyArea(dpy, areawin->fixed_pixmap, areawin->window, areawin->gc, 0, 0,
2446 	 areawin->width, areawin->height, 0, 0);
2447 #endif /* HAVE_CAIRO */
2448 
2449    areawin->redraw_ongoing = False;
2450 }
2451 
2452 /*-------------------------------------------------------------------------*/
2453 /* Helper function for the xxx_mode_draw functions			   */
2454 /* Hide all the selected elements and draw all the element not currently   */
2455 /* being edited to fixed_pixmap						   */
2456 /*-------------------------------------------------------------------------*/
2457 
draw_fixed_without_selection(void)2458 static void draw_fixed_without_selection(void)
2459 {
2460    int idx;
2461    for (idx = 0; idx < areawin->selects; idx++)
2462       SELTOGENERIC(&areawin->selectlist[idx])->type |= DRAW_HIDE;
2463    draw_fixed();
2464    for (idx = 0; idx < areawin->selects; idx++)
2465       SELTOGENERIC(&areawin->selectlist[idx])->type &= ~DRAW_HIDE;
2466 }
2467 
2468 /*-------------------------------------------------------------------------*/
2469 /* generic xxx_mode_draw function, that handles most of the thing needed   */
2470 /* for arcs, splines and paths.						   */
2471 /*-------------------------------------------------------------------------*/
2472 
generic_mode_draw(xcDrawType type,generic * newgen,void (* decorations)(generic * newgen))2473 static void generic_mode_draw(xcDrawType type, generic *newgen,
2474       void (*decorations)(generic *newgen))
2475 {
2476    int idx;
2477 
2478    switch (type) {
2479       case xcDRAW_INIT:
2480       case xcREDRAW_FORCED:
2481 	 draw_fixed_without_selection();
2482 	 /* fallthrough */
2483 
2484       case xcDRAW_EDIT:
2485 	 begin_event_mode_drawing();
2486    	 for (idx = 0; idx < areawin->selects; idx++) {
2487 	    int scolor = SELTOCOLOR(&areawin->selectlist[idx]);
2488 	    XcTopSetForeground(scolor);
2489       	    easydraw(areawin->selectlist[idx], scolor);
2490 	 }
2491 	 if (decorations)
2492 	    (*decorations)(newgen);
2493 	 end_event_mode_drawing();
2494 	 break;
2495 
2496       case xcDRAW_FINAL:
2497 	 begin_event_mode_drawing_final();
2498 	 for (idx = 0; idx < areawin->selects; idx++) {
2499 	    int scolor = SELTOCOLOR(&areawin->selectlist[idx]);
2500 	    XcTopSetForeground(scolor);
2501 	    easydraw(areawin->selectlist[idx], scolor);
2502 	 }
2503 	 end_event_mode_drawing_final();
2504 	 if (areawin->selects > 1) /* FIXME: Temp. fix for multiple selects */
2505 	    areawin->redraw_needed = True; /* Will be removed later on */
2506 	 break;
2507 
2508       case xcDRAW_EMPTY:
2509 	 /* Do not remove the empty begin/end. For cairo, this renders the */
2510 	 /* background with the fixed elements */
2511 	 begin_event_mode_drawing_final();
2512 	 end_event_mode_drawing_final();
2513 	 break;
2514    }
2515 }
2516 
2517 /*-------------------------------------------------------------------------*/
2518 /* Drawing function for ARC_MODE and EARC_MODE				   */
2519 /*-------------------------------------------------------------------------*/
2520 
arc_mode_decorations(generic * newgen)2521 static void arc_mode_decorations(generic *newgen)
2522 {
2523    UDrawXLine(((arc*) newgen)->position, areawin->save);
2524 }
2525 
arc_mode_draw(xcDrawType type,arc * newarc)2526 void arc_mode_draw(xcDrawType type, arc *newarc)
2527 {
2528    generic_mode_draw(type, (generic*) newarc, arc_mode_decorations);
2529 }
2530 
2531 /*-------------------------------------------------------------------------*/
2532 /* Drawing function for SPLINE_MODE and ESPLINE_MODE			   */
2533 /*-------------------------------------------------------------------------*/
2534 
spline_mode_decorations(generic * newgen)2535 static void spline_mode_decorations(generic *newgen)
2536 {
2537    spline *newspline = (spline*) newgen;
2538    UDrawXLine(newspline->ctrl[0], newspline->ctrl[1]);
2539    UDrawXLine(newspline->ctrl[3], newspline->ctrl[2]);
2540 }
2541 
spline_mode_draw(xcDrawType type,spline * newspline)2542 void spline_mode_draw(xcDrawType type, spline *newspline)
2543 {
2544    generic_mode_draw(type, (generic*) newspline, spline_mode_decorations);
2545 }
2546 
2547 /*-------------------------------------------------------------------------*/
2548 /* Drawing function for WIRE_MODE, BOX_MODE and EPOLY_MODE		   */
2549 /*-------------------------------------------------------------------------*/
2550 
poly_mode_draw(xcDrawType type,polygon * newpoly)2551 void poly_mode_draw(xcDrawType type, polygon *newpoly)
2552 {
2553    generic_mode_draw(type, (generic*) newpoly, NULL);
2554 }
2555 
2556 /*-------------------------------------------------------------------------*/
2557 /* Drawing function for EPATH_MODE					   */
2558 /*-------------------------------------------------------------------------*/
2559 
path_mode_decorations(generic * newgen)2560 static void path_mode_decorations(generic *newgen)
2561 {
2562    genericptr *ggen;
2563    path *newpath = (path*) newgen;
2564    for (ggen = newpath->plist; ggen < newpath->plist + newpath->parts; ggen++) {
2565       if (ELEMENTTYPE(*ggen) == SPLINE) {
2566 	 spline *lastspline = TOSPLINE(ggen);
2567 	 UDrawXLine(lastspline->ctrl[0], lastspline->ctrl[1]);
2568 	 UDrawXLine(lastspline->ctrl[3], lastspline->ctrl[2]);
2569       }
2570    }
2571 }
2572 
path_mode_draw(xcDrawType type,path * newpath)2573 void path_mode_draw(xcDrawType type, path *newpath)
2574 {
2575    generic_mode_draw(type, (generic*) newpath, path_mode_decorations);
2576 }
2577 
2578 /*-------------------------------------------------------------------------*/
2579 /* Drawing function for TEXT_MODE, CATTEXT_MODE and ETEXT_MODE		   */
2580 /*-------------------------------------------------------------------------*/
2581 
text_mode_decorations(generic * newgen)2582 static void text_mode_decorations(generic *newgen)
2583 {
2584    UDrawTLine((label*) newgen);
2585 }
2586 
text_mode_draw(xcDrawType type,label * newlabel)2587 void text_mode_draw(xcDrawType type, label *newlabel)
2588 {
2589    generic_mode_draw(type, (generic*) newlabel, text_mode_decorations);
2590 }
2591 
2592 /*-------------------------------------------------------------------------*/
2593 /* Drawing function for SELAREA_MODE					   */
2594 /*-------------------------------------------------------------------------*/
2595 
selarea_mode_draw(xcDrawType type,void * unused)2596 void selarea_mode_draw(xcDrawType type, void *unused)
2597 {
2598    UNUSED(unused);
2599 
2600    switch (type) {
2601       case xcREDRAW_FORCED:
2602 	 draw_fixed();
2603 	 /* fallthrough */
2604 
2605       case xcDRAW_INIT:
2606       case xcDRAW_EDIT:
2607 	 begin_event_mode_drawing();
2608 	 draw_all_selected();
2609 	 UDrawBox(areawin->origin, areawin->save);
2610 	 end_event_mode_drawing();
2611 	 break;
2612 
2613       case xcDRAW_FINAL:
2614       case xcDRAW_EMPTY:
2615 	 /* No need for rendering the background, since it will be */
2616 	 /* overwritten by the select_area() function anyway */
2617 	 break;
2618    }
2619 }
2620 
2621 /*-------------------------------------------------------------------------*/
2622 /* Drawing function for RESCALE_MODE					   */
2623 /*-------------------------------------------------------------------------*/
2624 
rescale_mode_draw(xcDrawType type,void * unused)2625 void rescale_mode_draw(xcDrawType type, void *unused)
2626 {
2627    UNUSED(unused);
2628 
2629    switch (type) {
2630       case xcREDRAW_FORCED:
2631 	 draw_fixed();
2632 	 /* fallthrough */
2633 
2634       case xcDRAW_INIT:
2635       case xcDRAW_EDIT:
2636 	 begin_event_mode_drawing();
2637 	 UDrawRescaleBox(&areawin->save);
2638 	 end_event_mode_drawing();
2639 	 break;
2640 
2641       case xcDRAW_FINAL:
2642       case xcDRAW_EMPTY:
2643 	 /* No need for rendering the background, since it will be */
2644 	 /* overwritten by the select_area() function anyway */
2645 	 break;
2646    }
2647 }
2648 
2649 /*-------------------------------------------------------------------------*/
2650 /* Drawing function for CATMOVE_MODE, MOVE_MODE and COPY_MODE		   */
2651 /*-------------------------------------------------------------------------*/
2652 
move_mode_draw(xcDrawType type,void * unused)2653 void move_mode_draw(xcDrawType type, void *unused)
2654 {
2655    float wirewidth = xobjs.pagelist[areawin->page]->wirewidth;
2656    short *selectobj;
2657    genericptr *pgen;
2658    int idx;
2659    UNUSED(unused);
2660 
2661    switch (type) {
2662       case xcREDRAW_FORCED:
2663       case xcDRAW_INIT:
2664 	 draw_fixed_without_selection();
2665 	 /* fallthrough */
2666 
2667       case xcDRAW_EDIT:
2668 	 begin_event_mode_drawing();
2669 	 XTopSetForeground(SELECTCOLOR);
2670    	 for (idx = 0; idx < areawin->selects; idx++)
2671 	    easydraw(areawin->selectlist[idx], DOFORALL);
2672 	 for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2673                 + areawin->selects; selectobj++) {
2674 	    if (SELECTTYPE(selectobj) == LABEL) {
2675 	       label *labelobj = SELTOLABEL(selectobj);
2676 	       if (labelobj->pin == False)
2677 		  UDrawX(labelobj);
2678 	    }
2679 	 }
2680 	 if (areawin->pinattach) {
2681 	    for (pgen = topobject->plist; pgen < topobject->plist +
2682 		  topobject->parts; pgen++) {
2683 	       if (ELEMENTTYPE(*pgen) == POLYGON) {
2684 		  polygon *cpoly = TOPOLY(pgen);
2685 		  if (cpoly->cycle != NULL)
2686 		     UDrawPolygon(cpoly, wirewidth);
2687 	       }
2688 	    }
2689 	 }
2690 	 end_event_mode_drawing();
2691 	 break;
2692 
2693       case xcDRAW_FINAL:
2694 	 begin_event_mode_drawing_final();
2695 	 for (selectobj = areawin->selectlist; selectobj
2696 	       < areawin->selectlist + areawin->selects; selectobj++) {
2697 	    XTopSetForeground(SELTOCOLOR(selectobj));
2698 	    easydraw(*selectobj, DOFORALL);
2699 	 }
2700 	 end_event_mode_drawing_final();
2701 	 break;
2702 
2703       case xcDRAW_EMPTY:
2704 	 /* Do not remove the empty begin/end. For cairo, this renders the */
2705 	 /* background with the fixed elements */
2706 	 begin_event_mode_drawing_final();
2707 	 end_event_mode_drawing_final();
2708 	 break;
2709    }
2710 }
2711 
2712 /*-------------------------------------------------------------------------*/
2713 /* Drawing function for ASSOC_MODE, EINST_MODE, (E)FONTCAT_MODE, PAN_MODE, */
2714 /* NORMAL_MODE, UNDO_MODE and CATALOG_MODE				   */
2715 /*-------------------------------------------------------------------------*/
2716 
normal_mode_draw(xcDrawType type,void * unused)2717 void normal_mode_draw(xcDrawType type, void *unused)
2718 {
2719    UNUSED(unused);
2720 
2721    switch (type) {
2722       case xcDRAW_INIT:
2723       case xcREDRAW_FORCED:
2724 	 draw_fixed_without_selection();
2725 	 /* fallthrough */
2726 
2727       case xcDRAW_EDIT:
2728 	 begin_event_mode_drawing();
2729 
2730          /* draw the highlighted netlist, if any */
2731          if (checkvalid(topobject) != -1)
2732             if (topobject->highlight.netlist != NULL)
2733                highlightnetlist(topobject, areawin->topinstance, 1);
2734 
2735          if ((areawin->selects == 1) && SELECTTYPE(areawin->selectlist) == LABEL
2736                && areawin->textend > 0 && areawin->textpos > areawin->textend) {
2737             labelptr drawlabel = SELTOLABEL(areawin->selectlist);
2738             UDrawString(drawlabel, DOSUBSTRING, areawin->topinstance);
2739          }
2740          else if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
2741             draw_all_selected();
2742 	 end_event_mode_drawing();
2743 	 break;
2744 
2745       case xcDRAW_FINAL:
2746 	 break;
2747 
2748       case xcDRAW_EMPTY:
2749 	 break;
2750    }
2751 }
2752 
2753 
2754