1 /*-------------------------------------------------------------------------*/
2 /* events.c --- xcircuit routines handling Xevents and Callbacks	   */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	   */
4 /*-------------------------------------------------------------------------*/
5 
6 /*-------------------------------------------------------------------------*/
7 /*      written by Tim Edwards, 8/13/93    				   */
8 /*-------------------------------------------------------------------------*/
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 #include <ctype.h>
15 
16 #ifndef XC_WIN32
17 #include <X11/Intrinsic.h>
18 #include <X11/StringDefs.h>
19 #define  XK_MISCELLANY
20 #define  XK_LATIN1
21 #include <X11/keysymdef.h>
22 #else
23 #ifdef TCL_WRAPPER
24 #define  XK_MISCELLANY
25 #define  XK_LATIN1
26 #include <X11/keysymdef.h>
27 #endif
28 #endif
29 
30 #ifdef HAVE_CAIRO
31 #include <cairo/cairo-xlib.h>
32 #endif
33 
34 /*-------------------------------------------------------------------------*/
35 /* Local includes							   */
36 /*-------------------------------------------------------------------------*/
37 
38 #ifdef TCL_WRAPPER
39 #include <tk.h>
40 #endif
41 
42 #include "xcircuit.h"
43 #include "colordefs.h"
44 
45 #define HOLD_MASK	(Mod4Mask << 16)
46 
47 /*----------------------------------------------------------------------*/
48 /* Function prototype declarations                                      */
49 /*----------------------------------------------------------------------*/
50 #include "prototypes.h"
51 
52 /*-------------------------------------------------------------------------*/
53 /* Global Variable definitions						   */
54 /*-------------------------------------------------------------------------*/
55 
56 extern XtAppContext app;
57 extern Display	*dpy;
58 extern Cursor	appcursors[NUM_CURSORS];
59 extern Globaldata xobjs;
60 extern XCWindowData *areawin;
61 extern ApplicationData appdata;
62 extern colorindex *colorlist;
63 extern short popups;
64 extern int pressmode;
65 extern xcWidget message2, top;
66 extern char  _STR[150], _STR2[250];
67 extern short beeper;
68 extern double saveratio;
69 extern u_char texttype;
70 extern aliasptr aliastop;
71 
72 #ifdef TCL_WRAPPER
73 extern Tcl_Interp *xcinterp;
74 #else
75 extern short help_up;
76 #endif
77 
78 /* double buffer */
79 #if !defined(HAVE_CAIRO)
80 Pixmap dbuf = (Pixmap)NULL;
81 #endif
82 
83 Boolean was_preselected;
84 
85 /*----------------------------------------------------------------------------*/
86 /* Edit Object pushing and popping.					      */
87 /*----------------------------------------------------------------------------*/
88 
recursefind(objectptr parent,objectptr suspect)89 Boolean recursefind(objectptr parent, objectptr suspect)
90 {
91    genericptr *shell;
92 
93    if (parent == suspect) return True;
94 
95    for (shell = parent->plist; shell < parent->plist + parent->parts; shell++)
96       if (IS_OBJINST(*shell))
97          if (recursefind(TOOBJINST(shell)->thisobject, suspect)) return True;
98 
99    return False;
100 }
101 
102 /*--------------------------------------------------------------*/
103 /* Transfer objects in the select list to the current object	*/
104 /* (but disallow infinitely recursive loops!)			*/
105 /*--------------------------------------------------------------*/
106 /* IMPORTANT:  delete_for_xfer() MUST be executed prior to	*/
107 /* calling transferselects(), so that the deleted elements are	*/
108 /* in an object saved in areawin->editstack.			*/
109 /*--------------------------------------------------------------*/
110 
transferselects()111 void transferselects()
112 {
113    short locselects;
114    objinstptr tobj;
115    XPoint newpos;
116 
117    if (areawin->editstack->parts == 0) return;
118 
119    if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
120 		eventmode == UNDO_MODE || eventmode == CATMOVE_MODE) {
121       short ps = topobject->parts;
122 
123       freeselects();
124 
125       locselects = areawin->editstack->parts;
126       areawin->selectlist = xc_undelete(areawin->topinstance,
127 		areawin->editstack, (short)NORMAL, (short *)NULL);
128       areawin->selects = locselects;
129 
130       /* Move all selected items to the cursor position	*/
131       newpos = UGetCursor();
132       drag((int)newpos.x, (int)newpos.y);
133 
134       /* check to make sure this object is not the current object	*/
135       /* or one of its direct ancestors, else an infinite loop results. */
136 
137       for (ps = 0; ps < topobject->parts; ps++) {
138 	 if (IS_OBJINST(*(topobject->plist + ps))) {
139 	    tobj = TOOBJINST(topobject->plist + ps);
140 	    if (recursefind(tobj->thisobject, topobject)) {
141 	       Wprintf("Attempt to place object inside of itself");
142 	       delete_noundo(NORMAL);
143 	       break;
144 	    }
145 	 }
146       }
147    }
148 }
149 
150 /*-------------------------------------------------------------------*/
151 /* Make a new matrix corresponding to the current position and scale */
152 /*-------------------------------------------------------------------*/
153 
newmatrix()154 void newmatrix()
155 {
156    if (DCTM == NULL) {
157       DCTM = (Matrixptr)malloc(sizeof(Matrix));
158       DCTM->nextmatrix = NULL;
159    }
160    UResetCTM(DCTM);
161    UMakeWCTM(DCTM);
162 }
163 
164 /*-------------------------------------------------------*/
165 /* set the viewscale variable to the proper address	 */
166 /*-------------------------------------------------------*/
167 
setpage(Boolean killselects)168 void setpage(Boolean killselects)
169 {
170    areawin->vscale = topobject->viewscale;
171    areawin->pcorner = topobject->pcorner;
172    newmatrix();
173 
174    if (killselects) clearselects();
175 
176 #ifdef TCL_WRAPPER
177    if (xobjs.suspend < 0)
178       XcInternalTagCall(xcinterp, 2, "page", "goto");
179 #endif
180 }
181 
182 /*-------------------------------------------------------*/
183 /* switch to a new page					 */
184 /*-------------------------------------------------------*/
185 
changepage(short pagenumber)186 int changepage(short pagenumber)
187 {
188    short npage;
189    objectptr pageobj;
190    u_char undo_type;
191 
192    /* to add to existing number of top level pages. . . */
193 
194    if (pagenumber == 255) {
195       if (xobjs.pages == 255) {
196 	 Wprintf("Out of available pages!");
197 	 return -1;
198       }
199       else pagenumber = xobjs.pages;
200    }
201 
202    if (pagenumber >= xobjs.pages) {
203 
204       xobjs.pagelist = (Pagedata **)realloc(xobjs.pagelist, (pagenumber + 1)
205 		* sizeof(Pagedata *));
206       xobjs.pagelist[pagenumber] = (Pagedata *)malloc(sizeof(Pagedata));
207       xobjs.pagelist[pagenumber]->filename = NULL;
208       xobjs.pagelist[pagenumber]->background.name = NULL;
209       xobjs.pagelist[pagenumber]->pageinst = NULL;
210 
211       /* If we skipped ahead to pagenumber, fill in the pages in between */
212       for (npage = xobjs.pages; npage < pagenumber; npage++) {
213          xobjs.pagelist[npage] = (Pagedata *)malloc(sizeof(Pagedata));
214          xobjs.pagelist[npage]->pageinst = NULL;
215       }
216 
217       xobjs.pages = pagenumber + 1;
218       makepagebutton();
219    }
220 
221    if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) {
222       delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
223       undo_type = UNDO_MORE;
224    }
225    else {
226       clearselects();
227       undo_type = UNDO_DONE;
228    }
229    if (areawin->page != pagenumber)
230       register_for_undo(XCF_Page, undo_type, areawin->topinstance,
231 	  areawin->page, pagenumber);
232 
233    if (eventmode != ASSOC_MODE) {
234       areawin->page = pagenumber;
235       free_stack(&areawin->stack);
236    }
237    if (xobjs.pagelist[pagenumber]->pageinst == NULL) {
238 
239       /* initialize a new page */
240 
241       pageobj = (objectptr) malloc (sizeof(object));
242       initmem(pageobj);
243       sprintf(pageobj->name, "Page %d", pagenumber + 1);
244 
245       xobjs.pagelist[pagenumber]->pageinst = newpageinst(pageobj);
246       xobjs.pagelist[pagenumber]->filename = NULL;
247       xobjs.pagelist[pagenumber]->background.name = NULL;
248 
249       pagereset(pagenumber);
250    }
251 
252    /* Write back the current view parameters */
253    if (areawin->topinstance != NULL) {
254       topobject->viewscale = areawin->vscale;
255       topobject->pcorner = areawin->pcorner;
256    }
257 
258    areawin->topinstance = xobjs.pagelist[pagenumber]->pageinst;
259 
260    setpage(TRUE);
261 
262    return 0;
263 }
264 
265 /*-------------------------------------------------------*/
266 /* switch to a new page and redisplay			 */
267 /*-------------------------------------------------------*/
268 
newpage(short pagenumber)269 void newpage(short pagenumber)
270 {
271    switch (eventmode) {
272       case CATALOG_MODE:
273          eventmode = NORMAL_MODE;
274 	 catreturn();
275 	 break;
276 
277       case NORMAL_MODE: case COPY_MODE: case MOVE_MODE: case UNDO_MODE:
278 	 if (changepage(pagenumber) >= 0) {
279 	    transferselects();
280 	    renderbackground();
281 	    refresh(NULL, NULL, NULL);
282 
283 	    togglegrid((u_short)xobjs.pagelist[areawin->page]->coordstyle);
284 	    setsymschem();
285 	 }
286 	 break;
287 
288       default:
289          Wprintf("Cannot switch pages from this mode");
290 	 break;
291    }
292 }
293 
294 /*---------------------------------------*/
295 /* Stack structure push and pop routines */
296 /*---------------------------------------*/
297 
push_stack(pushlistptr * stackroot,objinstptr thisinst,char * clientdata)298 void push_stack(pushlistptr *stackroot, objinstptr thisinst, char *clientdata)
299 {
300    pushlistptr newpush;
301 
302    newpush = (pushlistptr)malloc(sizeof(pushlist));
303    newpush->next = *stackroot;
304    newpush->clientdata = clientdata;
305    newpush->thisinst = thisinst;
306    *stackroot = newpush;
307 }
308 
309 /*----------------------------------------------------------*/
310 
pop_stack(pushlistptr * stackroot)311 void pop_stack(pushlistptr *stackroot)
312 {
313    pushlistptr lastpush;
314 
315    if (!(*stackroot)) {
316       Fprintf(stderr, "pop_genstack() Error: NULL instance stack!\n");
317       return;
318    }
319 
320    lastpush = (*stackroot)->next;
321    free(*stackroot);
322    *stackroot = lastpush;
323 }
324 
325 /*----------------------------------------------------------*/
326 
free_stack(pushlistptr * stackroot)327 void free_stack(pushlistptr *stackroot)
328 {
329    while ((*stackroot) != NULL)
330       pop_stack(stackroot);
331 }
332 
333 /*------------------------------------------*/
334 /* Push object onto hierarchy stack to edit */
335 /*------------------------------------------*/
336 
pushobject(objinstptr thisinst)337 void pushobject(objinstptr thisinst)
338 {
339   short *selectobj, *savelist;
340    int saves;
341    u_char undo_type = UNDO_DONE;
342    objinstptr pushinst = thisinst;
343 
344    savelist = NULL;
345    saves = 0;
346    if (eventmode == MOVE_MODE || eventmode == COPY_MODE) {
347       savelist = areawin->selectlist;
348       saves = areawin->selects;
349       areawin->selectlist = NULL;
350       areawin->selects = 0;
351       undo_type = UNDO_MORE;
352    }
353 
354    if (pushinst == NULL) {
355       selectobj = areawin->selectlist;
356       if (areawin->selects == 0) {
357          disable_selects(topobject, savelist, saves);
358 	 selectobj = select_element(OBJINST);
359          enable_selects(topobject, savelist, saves);
360       }
361       if (areawin->selects == 0) {
362          Wprintf("No objects selected.");
363          return;
364       }
365       else if (areawin->selects > 1) {
366          Wprintf("Choose only one object.");
367          return;
368       }
369       else if (SELECTTYPE(selectobj) != OBJINST) {
370          Wprintf("Element to push must be an object.");
371          return;
372       }
373       else pushinst = SELTOOBJINST(selectobj);
374    }
375 
376    if (savelist != NULL) {
377       delete_for_xfer(NORMAL, savelist, saves);
378       free(savelist);
379    }
380 
381    register_for_undo(XCF_Push, undo_type, areawin->topinstance, pushinst);
382 
383    /* save the address of the current object to the push stack */
384 
385    push_stack(&areawin->stack, areawin->topinstance, NULL);
386 
387    topobject->viewscale = areawin->vscale;
388    topobject->pcorner = areawin->pcorner;
389    areawin->topinstance = pushinst;
390 
391    /* move selected items to the new object */
392 
393    setpage(TRUE);
394    transferselects();
395    refresh(NULL, NULL, NULL);
396    setsymschem();
397 }
398 
399 /*--------------------------*/
400 /* Pop edit hierarchy stack */
401 /*--------------------------*/
402 
popobject(xcWidget w,pointertype no_undo,caddr_t calldata)403 void popobject(xcWidget w, pointertype no_undo, caddr_t calldata)
404 {
405    u_char undo_type = UNDO_DONE;
406    UNUSED(w); UNUSED(calldata);
407 
408    if (areawin->stack == NULL || (eventmode != NORMAL_MODE && eventmode != MOVE_MODE
409 	&& eventmode != COPY_MODE && eventmode != FONTCAT_MODE &&
410 	eventmode != ASSOC_MODE && eventmode != UNDO_MODE &&
411 	eventmode != EFONTCAT_MODE)) return;
412 
413    if ((eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE)
414 	&& ((areawin->stack->thisinst == xobjs.libtop[LIBRARY]) ||
415        (areawin->stack->thisinst == xobjs.libtop[USERLIB]))) return;
416 
417    /* remove any selected items from the current object */
418 
419    if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) {
420       undo_type = UNDO_MORE;
421       delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
422    }
423    else if (eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE)
424       unselect_all();
425 
426    /* If coming from the library, don't register an undo action, because */
427    /* it has already been registered as type XCF_Library_Pop.		 */
428 
429    if (no_undo == (pointertype)0)
430       register_for_undo(XCF_Pop, undo_type, areawin->topinstance);
431 
432    topobject->viewscale = areawin->vscale;
433    topobject->pcorner = areawin->pcorner;
434    areawin->topinstance = areawin->stack->thisinst;
435    pop_stack(&areawin->stack);
436 
437    /* if new object is a library or PAGELIB, put back into CATALOG_MODE */
438 
439    if (is_library(topobject) >= 0) eventmode = CATALOG_MODE;
440 
441    /* move selected items to the new object */
442 
443    if (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE)
444       setpage(False);
445    else {
446       setpage(True);
447       setsymschem();
448       if (eventmode != ASSOC_MODE)
449          transferselects();
450    }
451    refresh(NULL, NULL, NULL);
452 }
453 
454 /*-------------------------------------------------------------------------*/
455 /* Destructive reset of entire object		 			   */
456 /*-------------------------------------------------------------------------*/
457 
resetbutton(xcWidget button,pointertype pageno,caddr_t calldata)458 void resetbutton(xcWidget button, pointertype pageno, caddr_t calldata)
459 {
460    short page;
461    objectptr pageobj;
462    objinstptr pageinst;
463    UNUSED(button); UNUSED(calldata);
464 
465    if (eventmode != NORMAL_MODE) return;
466 
467    page = (pageno == (pointertype)0) ? areawin->page : (short)(pageno - 1);
468 
469    pageinst = xobjs.pagelist[page]->pageinst;
470 
471    if (pageinst == NULL) return; /* page already cleared */
472 
473    pageobj = pageinst->thisobject;
474 
475    /* Make sure this is a real top-level page */
476 
477    if (is_page(topobject) < 0) {
478       if (pageno == (pointertype)0) {
479 	 Wprintf("Can only clear top-level pages!");
480 	 return;
481       }
482       else {
483 	 /* Make sure that we're not in the hierarchy of the page being deleted */
484 	 pushlistptr slist;
485 	 for (slist = areawin->stack; slist != NULL; slist = slist->next)
486 	    if (slist->thisinst->thisobject == pageobj) {
487 	       Wprintf("Can't delete the page while you're in its hierarchy!");
488 	       return;
489 	    }
490       }
491    }
492 
493    /* Watch for pages which are linked by schematic/symbol. */
494 
495    if (pageobj->symschem != NULL) {
496       Wprintf("Schematic association to object %s", pageobj->symschem->name);
497       return;
498    }
499 
500    sprintf(pageobj->name, "Page %d", page + 1);
501    xobjs.pagelist[page]->filename = (char *)realloc(xobjs.pagelist[page]->filename,
502 		(strlen(pageobj->name) + 1) * sizeof(char));
503    strcpy(xobjs.pagelist[page]->filename, pageobj->name);
504    reset(pageobj, NORMAL);
505    flush_undo_stack();
506 
507    if (page == areawin->page) {
508       areawin->redraw_needed = True;
509       drawarea(areawin->area, NULL, NULL);
510       printname(pageobj);
511       renamepage(page);
512       Wprintf("Page cleared.");
513    }
514 }
515 
516 /*------------------------------------------------------*/
517 /* Redraw the horizontal scrollbar			*/
518 /*------------------------------------------------------*/
519 
drawhbar(xcWidget bar,caddr_t clientdata,caddr_t calldata)520 void drawhbar(xcWidget bar, caddr_t clientdata, caddr_t calldata)
521 {
522    Window bwin;
523    float frac;
524    long rleft, rright, rmid;
525    UNUSED(clientdata); UNUSED(calldata);
526 
527    if (!xcIsRealized(bar)) return;
528    if (xobjs.suspend >= 0) return;
529 
530    bwin = xcWindow(bar);
531 
532    if (topobject->bbox.width > 0) {
533       frac = (float) areawin->width / (float) topobject->bbox.width;
534       rleft = (long)(frac * (float)(areawin->pcorner.x
535 		- topobject->bbox.lowerleft.x));
536       rright = rleft + (long)(frac * (float)areawin->width / areawin->vscale);
537    }
538    else {
539       rleft = 0L;
540       rright = (long)areawin->width;
541    }
542    rmid = (rright + rleft) >> 1;
543 
544    if (rleft < 0) rleft = 0;
545    if (rright > areawin->width) rright = areawin->width;
546 
547    XSetFunction(dpy, areawin->gc, GXcopy);
548    XSetForeground(dpy, areawin->gc, colorlist[BARCOLOR].color.pixel);
549    if (rmid > 0 && rleft > 0)
550       XClearArea(dpy, bwin, 0, 0, (int)rleft, SBARSIZE, FALSE);
551    XFillRectangle(dpy, bwin, areawin->gc, (int)rleft + 1, 1,
552 	  (int)(rright - rleft), SBARSIZE - 1);
553    if (rright > rmid)
554       XClearArea(dpy, bwin, (int)rright + 1, 0, areawin->width
555 	  - (int)rright, SBARSIZE, FALSE);
556    XClearArea(dpy, bwin, (int)rmid - 1, 1, 3, SBARSIZE, FALSE);
557 
558    XSetForeground(dpy, areawin->gc, colorlist[areawin->gccolor].color.pixel);
559 }
560 
561 /*------------------------------------------------------*/
562 /* Redraw the vertical scrollbar			*/
563 /*------------------------------------------------------*/
564 
drawvbar(xcWidget bar,caddr_t clientdata,caddr_t calldata)565 void drawvbar(xcWidget bar, caddr_t clientdata, caddr_t calldata)
566 {
567    Window bwin = xcWindow(bar);
568    float frac;
569    long rtop, rbot, rmid;
570    UNUSED(clientdata); UNUSED(calldata);
571 
572    if (!xcIsRealized(bar)) return;
573    if (xobjs.suspend >= 0) return;
574 
575    if (topobject->bbox.height > 0) {
576       frac = (float)areawin->height / (float)topobject->bbox.height;
577       rbot = (long)(frac * (float)(topobject->bbox.lowerleft.y
578 		- areawin->pcorner.y + topobject->bbox.height));
579       rtop = rbot - (long)(frac * (float)areawin->height / areawin->vscale);
580    }
581    else {
582       rbot = areawin->height;
583       rtop = 0;
584    }
585    rmid = (rtop + rbot) >> 1;
586 
587    if (rtop < 0) rtop = 0;
588    if (rbot > areawin->height) rbot = areawin->height;
589 
590    XSetFunction(dpy, areawin->gc, GXcopy);
591    XSetForeground(dpy, areawin->gc, colorlist[BARCOLOR].color.pixel);
592    if (rmid > 0 && rtop > 0)
593       XClearArea(dpy, bwin, 0, 0, SBARSIZE, (int)rtop, FALSE);
594    XFillRectangle(dpy, bwin, areawin->gc, 0, (int)rtop + 2, SBARSIZE,
595 	     (int)(rbot - rtop));
596    if (rbot > rmid)
597       XClearArea(dpy, bwin, 0, (int)rbot + 1, SBARSIZE, areawin->height
598 		- (int)rbot, FALSE);
599    XClearArea(dpy, bwin, 0, (int)rmid - 1, SBARSIZE, 3, FALSE);
600 
601    XSetForeground(dpy, areawin->gc, colorlist[areawin->gccolor].color.pixel);
602 }
603 
604 /*------------------------------------------------------*/
605 /* Simultaneously scroll the screen and horizontal	*/
606 /* bar when dragging the mouse in the scrollbar area 	*/
607 /*------------------------------------------------------*/
608 
panhbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)609 void panhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
610 {
611    long  newx, newpx;
612    short savex = areawin->pcorner.x;
613    UNUSED(clientdata);
614 
615    if (eventmode == SELAREA_MODE) return;
616 
617    newx = (long)(event->x * ((float)topobject->bbox.width /
618 	areawin->width) + topobject->bbox.lowerleft.x - 0.5 *
619 	((float)areawin->width / areawin->vscale));
620    areawin->pcorner.x = (short)newx;
621    drawhbar(bar, NULL, NULL);
622    areawin->pcorner.x = savex;
623 
624    if ((newpx = (long)(newx - savex) * areawin->vscale) == 0)
625       return;
626 
627    areawin->panx = -newpx;
628    drawarea(NULL, NULL, NULL);
629 }
630 
631 /*------------------------------------------------------*/
632 /* End the horizontal scroll and refresh entire screen	*/
633 /*------------------------------------------------------*/
634 
endhbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)635 void endhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
636 {
637    long  newx;
638    short savex = areawin->pcorner.x;
639    UNUSED(clientdata);
640 
641    areawin->panx = 0;
642 
643    newx = (long)(event->x * ((float)topobject->bbox.width /
644 	areawin->width) + topobject->bbox.lowerleft.x - 0.5 *
645 	((float)areawin->width / areawin->vscale));
646 
647    areawin->pcorner.x = (short)newx;
648 
649    if ((newx << 1) != (long)((short)(newx << 1)) || checkbounds() == -1) {
650       areawin->pcorner.x = savex;
651       Wprintf("Reached boundary:  cannot pan further");
652    }
653    else
654       W3printf(" ");
655 
656    areawin->redraw_needed = True;
657    areawin->lastbackground = NULL;
658    renderbackground();
659    drawhbar(bar, NULL, NULL);
660    drawarea(bar, NULL, NULL);
661 }
662 
663 /*------------------------------------------------------*/
664 /* Simultaneously scroll the screen and vertical	*/
665 /* bar when dragging the mouse in the scrollbar area 	*/
666 /*------------------------------------------------------*/
667 
panvbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)668 void panvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
669 {
670    long  newy, newpy;
671    short savey = areawin->pcorner.y;
672    UNUSED(clientdata);
673 
674    if (eventmode == SELAREA_MODE) return;
675 
676    newy = (int)((areawin->height - event->y) *
677 	((float)topobject->bbox.height / areawin->height) +
678 	topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height /
679 	     areawin->vscale));
680    areawin->pcorner.y = (short)newy;
681    drawvbar(bar, NULL, NULL);
682    areawin->pcorner.y = savey;
683 
684    if ((newpy = (long)(newy - savey) * areawin->vscale) == 0)
685       return;
686 
687    areawin->pany = newpy;
688    drawarea(NULL, NULL, NULL);
689 }
690 
691 /*------------------------------------------------------*/
692 /* Pan the screen to follow the cursor position 	*/
693 /*------------------------------------------------------*/
694 
trackpan(int x,int y)695 void trackpan(int x, int y)
696 {
697    XPoint newpos;
698    short savey = areawin->pcorner.y;
699    short savex = areawin->pcorner.x;
700 
701    newpos.x = areawin->origin.x - x;
702    newpos.y = y - areawin->origin.y;
703 
704    areawin->pcorner.x += newpos.x / areawin->vscale;
705    areawin->pcorner.y += newpos.y / areawin->vscale;
706 
707    drawhbar(areawin->scrollbarh, NULL, NULL);
708    drawvbar(areawin->scrollbarv, NULL, NULL);
709 
710    areawin->panx = -newpos.x;
711    areawin->pany = newpos.y;
712 
713    drawarea(NULL, NULL, NULL);
714 
715    areawin->pcorner.x = savex;
716    areawin->pcorner.y = savey;
717 
718 }
719 
720 /*------------------------------------------------------*/
721 /* End the vertical scroll and refresh entire screen	*/
722 /*------------------------------------------------------*/
723 
endvbar(xcWidget bar,caddr_t clientdata,XButtonEvent * event)724 void endvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event)
725 {
726    long  newy;
727    short savey = areawin->pcorner.y;
728    UNUSED(clientdata);
729 
730    areawin->pany = 0;
731 
732    newy = (int)((areawin->height - event->y) *
733 	((float)topobject->bbox.height / areawin->height) +
734 	topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height /
735 	     areawin->vscale));
736 
737    areawin->pcorner.y = (short)newy;
738 
739    if ((newy << 1) != (long)((short)(newy << 1)) || checkbounds() == -1) {
740       areawin->pcorner.y = savey;
741       Wprintf("Reached boundary:  cannot pan further");
742    }
743    else
744       W3printf(" ");
745 
746    areawin->redraw_needed = True;
747    areawin->lastbackground = NULL;
748    renderbackground();
749    drawvbar(bar, NULL, NULL);
750    drawarea(bar, NULL, NULL);
751 }
752 
753 /*--------------------------------------------------------------------*/
754 /* Zoom functions-- zoom box, zoom in, zoom out, and pan.	      */
755 /*--------------------------------------------------------------------*/
756 
postzoom()757 void postzoom()
758 {
759    W3printf(" ");
760    areawin->lastbackground = NULL;
761    renderbackground();
762    newmatrix();
763 }
764 
765 /*--------------------------------------------------------------------*/
766 
zoominbox(int x,int y)767 void zoominbox(int x, int y)
768 {
769    float savescale;
770    float delxscale, delyscale;
771    XPoint savell; /* ucenter, ncenter, (jdk)*/
772    UNUSED(x); UNUSED(y);
773 
774    savescale = areawin->vscale;
775    savell.x = areawin->pcorner.x;
776    savell.y = areawin->pcorner.y;
777 
778    /* zoom-box function: corners are in areawin->save and areawin->origin */
779    /* select box has lower-left corner in .origin, upper-right in .save */
780    /* ignore if zoom box is size zero */
781 
782    if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->origin.y) {
783       Wprintf("Zoom box of size zero: Ignoring.");
784       eventmode = NORMAL_MODE;
785       return;
786    }
787 
788    /* determine whether x or y is limiting factor in zoom */
789    delxscale = (areawin->width / areawin->vscale) /
790 	   abs(areawin->save.x - areawin->origin.x);
791    delyscale = (areawin->height / areawin->vscale) /
792 	   abs(areawin->save.y - areawin->origin.y);
793    areawin->vscale *= min(delxscale, delyscale);
794 
795    areawin->pcorner.x = min(areawin->origin.x, areawin->save.x) -
796 	 (areawin->width / areawin->vscale -
797 	 abs(areawin->save.x - areawin->origin.x)) / 2;
798    areawin->pcorner.y = min(areawin->origin.y, areawin->save.y) -
799 	 (areawin->height / areawin->vscale -
800 	 abs(areawin->save.y - areawin->origin.y)) / 2;
801    eventmode = NORMAL_MODE;
802 
803    /* check for minimum scale */
804 
805    if (checkbounds() == -1) {
806       areawin->pcorner.x = savell.x;
807       areawin->pcorner.y = savell.y;
808       areawin->vscale = savescale;
809       Wprintf("At minimum scale: cannot scale further");
810 
811       /* this is a rare case where an object gets out-of-bounds */
812 
813       if (checkbounds() == -1) {
814 	 if (beeper) XBell(dpy, 100);
815 	 Wprintf("Unable to scale: Delete out-of-bounds object!");
816       }
817       return;
818    }
819    postzoom();
820 }
821 
822 /*--------------------------------------------------------------------*/
823 
zoomin(int x,int y)824 void zoomin(int x, int y)
825 {
826    float savescale;
827    XPoint ucenter, ncenter, savell;
828 
829    savescale = areawin->vscale;
830    savell.x = areawin->pcorner.x;
831    savell.y = areawin->pcorner.y;
832 
833    window_to_user(areawin->width / 2, areawin->height / 2, &ucenter);
834    areawin->vscale *= areawin->zoomfactor;
835    window_to_user(areawin->width / 2, areawin->height / 2, &ncenter);
836    areawin->pcorner.x += (ucenter.x - ncenter.x);
837    areawin->pcorner.y += (ucenter.y - ncenter.y);
838 
839    /* check for minimum scale */
840 
841    if (checkbounds() == -1) {
842       areawin->pcorner.x = savell.x;
843       areawin->pcorner.y = savell.y;
844       areawin->vscale = savescale;
845       Wprintf("At minimum scale: cannot scale further");
846 
847       /* this is a rare case where an object gets out-of-bounds */
848 
849       if (checkbounds() == -1) {
850 	 if (beeper) XBell(dpy, 100);
851 	 Wprintf("Unable to scale: Delete out-of-bounds object!");
852       }
853       return;
854    }
855    else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
856 		eventmode == CATMOVE_MODE)
857       drag(x, y);
858 
859    postzoom();
860 }
861 
862 /*--------------------------------------------------------------------*/
863 
zoominrefresh(int x,int y)864 void zoominrefresh(int x, int y)
865 {
866    if (eventmode == SELAREA_MODE)
867       zoominbox(x, y);
868    else
869       zoomin(x, y);
870    refresh(NULL, NULL, NULL);
871 }
872 
873 /*--------------------------------------------------------------------*/
874 
zoomoutbox(int x,int y)875 void zoomoutbox(int x, int y)
876 {
877    float savescale;
878    float delxscale, delyscale, scalefac;
879    XPoint savell; /* ucenter, ncenter, (jdk)*/
880    XlPoint newll;
881    UNUSED(x); UNUSED(y);
882 
883    savescale = areawin->vscale;
884    savell.x = areawin->pcorner.x;
885    savell.y = areawin->pcorner.y;
886 
887    /* zoom-box function, analogous to that for zoom-in */
888    /* ignore if zoom box is size zero */
889 
890    if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->origin.y) {
891       Wprintf("Zoom box of size zero: Ignoring.");
892       eventmode = NORMAL_MODE;
893       return;
894    }
895 
896    /* determine whether x or y is limiting factor in zoom */
897    delxscale = abs(areawin->save.x - areawin->origin.x) /
898 	   (areawin->width / areawin->vscale);
899    delyscale = abs(areawin->save.y - areawin->origin.y) /
900 	   (areawin->height / areawin->vscale);
901    scalefac = min(delxscale, delyscale);
902    areawin->vscale *= scalefac;
903 
904    /* compute lower-left corner of (reshaped) select box */
905    if (delxscale < delyscale) {
906       newll.y = min(areawin->save.y, areawin->origin.y);
907       newll.x = (areawin->save.x + areawin->origin.x
908 		- (abs(areawin->save.y - areawin->origin.y) *
909 		areawin->width / areawin->height)) / 2;
910    }
911    else {
912       newll.x = min(areawin->save.x, areawin->origin.x);
913       newll.y = (areawin->save.y + areawin->origin.y
914 		- (abs(areawin->save.x - areawin->origin.x) *
915 		areawin->height / areawin->width)) / 2;
916    }
917 
918    /* extrapolate to find new lower-left corner of screen */
919    newll.x = areawin->pcorner.x - (int)((float)(newll.x -
920 		areawin->pcorner.x) / scalefac);
921    newll.y = areawin->pcorner.y - (int)((float)(newll.y -
922 		areawin->pcorner.y) / scalefac);
923 
924    eventmode = NORMAL_MODE;
925    areawin->pcorner.x = (short)newll.x;
926    areawin->pcorner.y = (short)newll.y;
927 
928    if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1)
929 	 != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
930       areawin->vscale = savescale;
931       areawin->pcorner.x = savell.x;
932       areawin->pcorner.y = savell.y;
933       Wprintf("At maximum scale: cannot scale further.");
934       return;
935    }
936    postzoom();
937 }
938 
939 /*--------------------------------------------------------------------*/
940 
zoomout(int x,int y)941 void zoomout(int x, int y)
942 {
943    float savescale;
944    XPoint ucenter, ncenter, savell;
945    XlPoint newll;
946 
947    savescale = areawin->vscale;
948    savell.x = areawin->pcorner.x;
949    savell.y = areawin->pcorner.y;
950 
951    window_to_user(areawin->width / 2, areawin->height / 2, &ucenter);
952    areawin->vscale /= areawin->zoomfactor;
953    window_to_user(areawin->width / 2, areawin->height / 2, &ncenter);
954    newll.x = (long)areawin->pcorner.x + (long)(ucenter.x - ncenter.x);
955    newll.y = (long)areawin->pcorner.y + (long)(ucenter.y - ncenter.y);
956    areawin->pcorner.x = (short)newll.x;
957    areawin->pcorner.y = (short)newll.y;
958 
959    if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1)
960 	 != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
961       areawin->vscale = savescale;
962       areawin->pcorner.x = savell.x;
963       areawin->pcorner.y = savell.y;
964       Wprintf("At maximum scale: cannot scale further.");
965       return;
966    }
967    else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
968 		eventmode == CATMOVE_MODE)
969       drag(x, y);
970 
971    postzoom();
972 }
973 
974 /*--------------------------------------------------------------------*/
975 
zoomoutrefresh(int x,int y)976 void zoomoutrefresh(int x, int y)
977 {
978    if (eventmode == SELAREA_MODE)
979       zoomoutbox(x, y);
980    else
981       zoomout(x, y);
982    refresh(NULL, NULL, NULL);
983 }
984 
985 /*--------------------------------------*/
986 /* Call to XWarpPointer			*/
987 /*--------------------------------------*/
988 
warppointer(int x,int y)989 void warppointer(int x, int y)
990 {
991    XWarpPointer(dpy, None, areawin->window, 0, 0, 0, 0, x, y);
992 }
993 
994 /*--------------------------------------------------------------*/
995 /* ButtonPress handler during center pan 			*/
996 /* x and y are cursor coordinates.				*/
997 /* If ptype is 1-4 (directional), then "value" is a fraction of	*/
998 /* 	the screen to scroll.					*/
999 /*--------------------------------------------------------------*/
1000 
panbutton(u_int ptype,int x,int y,float value)1001 void panbutton(u_int ptype, int x, int y, float value)
1002 {
1003   /* Window pwin; (jdk) */
1004    int  xpos, ypos, newllx, newlly;
1005    XPoint savell; /* , newpos; (jdk)*/
1006    Dimension hwidth = areawin->width >> 1, hheight = areawin->height >> 1;
1007 
1008    savell.x = areawin->pcorner.x;
1009    savell.y = areawin->pcorner.y;
1010 
1011    switch(ptype) {
1012       case 1:
1013          xpos = hwidth - (hwidth * 2 * value);
1014          ypos = hheight;
1015 	 break;
1016       case 2:
1017          xpos = hwidth + (hwidth * 2 * value);
1018          ypos = hheight;
1019 	 break;
1020       case 3:
1021          xpos = hwidth;
1022          ypos = hheight - (hheight * 2 * value);
1023 	 break;
1024       case 4:
1025          xpos = hwidth;
1026          ypos = hheight + (hheight * 2 * value);
1027 	 break;
1028       case 5:
1029          xpos = x;
1030          ypos = y;
1031 	 break;
1032       case 6:	/* "pan follow" */
1033 	 if (eventmode == PAN_MODE)
1034 	    finish_op(XCF_Finish, x, y);
1035 	 else if (eventmode == NORMAL_MODE) {
1036 	    eventmode = PAN_MODE;
1037 	    areawin->save.x = x;
1038 	    areawin->save.y = y;
1039 	    u2u_snap(&areawin->save);
1040 	    areawin->origin = areawin->save;
1041 #ifdef TCL_WRAPPER
1042 	    Tk_CreateEventHandler(areawin->area, PointerMotionMask |
1043 			ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
1044 #else
1045 	    xcAddEventHandler(areawin->area, PointerMotionMask |
1046 			ButtonMotionMask , False, (xcEventHandler)xlib_drag,
1047 			NULL);
1048 #endif
1049 	 }
1050 	 return;
1051 	 break;
1052       default:	/* "pan here" */
1053 	 xpos = x;
1054 	 ypos = y;
1055          warppointer(hwidth, hheight);
1056 	 break;
1057    }
1058 
1059    xpos -= hwidth;
1060    ypos = hheight - ypos;
1061 
1062    newllx = (int)areawin->pcorner.x + (int)((float)xpos / areawin->vscale);
1063    newlly = (int)areawin->pcorner.y + (int)((float)ypos / areawin->vscale);
1064 
1065    areawin->pcorner.x = (short) newllx;
1066    areawin->pcorner.y = (short) newlly;
1067 
1068    if ((newllx << 1) != (long)(areawin->pcorner.x << 1) || (newlly << 1)
1069 	   != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) {
1070       areawin->pcorner.x = savell.x;
1071       areawin->pcorner.x = savell.y;
1072       Wprintf("Reached bounds:  cannot pan further.");
1073       return;
1074    }
1075    else if (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1076 		eventmode == CATMOVE_MODE)
1077       drag(x, y);
1078 
1079    postzoom();
1080 }
1081 
1082 /*--------------------------------------------------------------*/
1083 
panrefresh(u_int ptype,int x,int y,float value)1084 void panrefresh(u_int ptype, int x, int y, float value)
1085 {
1086    panbutton(ptype, x, y, value);
1087    refresh(NULL, NULL, NULL);
1088 }
1089 
1090 /*----------------------------------------------------------------*/
1091 /* Check for out-of-bounds before warping pointer, and pan window */
1092 /* if necessary.                                                  */
1093 /*----------------------------------------------------------------*/
1094 
checkwarp(XPoint * userpt)1095 void checkwarp(XPoint *userpt)
1096 {
1097   XPoint wpoint;
1098 
1099   user_to_window(*userpt, &wpoint);
1100 
1101   if (wpoint.x < 0 || wpoint.y < 0 || wpoint.x > areawin->width ||
1102         wpoint.y > areawin->height) {
1103      panrefresh(5, wpoint.x, wpoint.y, 0);
1104      wpoint.x = areawin->width >> 1;
1105      wpoint.y = areawin->height >> 1;
1106      /* snap(wpoint.x, wpoint.y, userpt); */
1107   }
1108   warppointer(wpoint.x, wpoint.y);
1109 }
1110 
1111 /*--------------------------------------------------------------*/
1112 /* Return a pointer to the element containing a reference point	*/
1113 /*--------------------------------------------------------------*/
1114 
getsubpart(pathptr editpath,int * idx)1115 genericptr getsubpart(pathptr editpath, int *idx)
1116 {
1117    pointselect *tmpptr = NULL;
1118    genericptr *pgen;
1119 
1120    if (idx) *idx = 0;
1121 
1122    for (pgen = editpath->plist; pgen < editpath->plist + editpath->parts; pgen++) {
1123       switch (ELEMENTTYPE(*pgen)) {
1124          case POLYGON:
1125 	    if (TOPOLY(pgen)->cycle != NULL) {
1126 	       for (tmpptr = TOPOLY(pgen)->cycle;; tmpptr++) {
1127 	          if (tmpptr->flags & REFERENCE) break;
1128 	          if (tmpptr->flags & LASTENTRY) break;
1129 	       }
1130 	       if (tmpptr->flags & REFERENCE) return *pgen;
1131 	    }
1132 	    break;
1133          case SPLINE:
1134 	    if (TOSPLINE(pgen)->cycle != NULL) {
1135 	       for (tmpptr = TOSPLINE(pgen)->cycle;; tmpptr++) {
1136 	          if (tmpptr->flags & REFERENCE) break;
1137 	          if (tmpptr->flags & LASTENTRY) break;
1138 	       }
1139 	       if (tmpptr->flags & REFERENCE) return *pgen;
1140 	    }
1141 	    break;
1142       }
1143       if (idx) (*idx)++;
1144    }
1145    return NULL;
1146 }
1147 
1148 /*--------------------------------------------------------------*/
1149 /* Return a pointer to the current reference point of an	*/
1150 /* edited element (polygon, spline, or path)			*/
1151 /*--------------------------------------------------------------*/
1152 
getrefpoint(genericptr genptr,XPoint ** refpt)1153 pointselect *getrefpoint(genericptr genptr, XPoint **refpt)
1154 {
1155    pointselect *tmpptr = NULL;
1156    genericptr *pgen;
1157 
1158    if (refpt) *refpt = NULL;
1159    switch (genptr->type) {
1160       case POLYGON:
1161 	 if (((polyptr)genptr)->cycle != NULL) {
1162 	    for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) {
1163 	       if (tmpptr->flags & REFERENCE) break;
1164 	       if (tmpptr->flags & LASTENTRY) break;
1165 	    }
1166 	    if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL;
1167 	    else if (refpt) *refpt = ((polyptr)genptr)->points + tmpptr->number;
1168 	 }
1169 	 break;
1170       case SPLINE:
1171 	 if (((splineptr)genptr)->cycle != NULL) {
1172 	    for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) {
1173 	       if (tmpptr->flags & REFERENCE) break;
1174 	       if (tmpptr->flags & LASTENTRY) break;
1175 	    }
1176 	    if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL;
1177 	    else if (refpt) *refpt = &((splineptr)genptr)->ctrl[tmpptr->number];
1178 	 }
1179 	 break;
1180       case PATH:
1181 	 for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist +
1182 		((pathptr)genptr)->parts; pgen++) {
1183 	    if ((tmpptr = getrefpoint(*pgen, refpt)) != NULL)
1184 	       return tmpptr;
1185 	 }
1186 	 break;
1187       default:
1188 	 tmpptr = NULL;
1189 	 break;
1190    }
1191    return tmpptr;
1192 }
1193 
1194 /*--------------------------------------------------------------*/
1195 /* Return next edit point on a polygon, arc, or spline.  Do not	*/
1196 /* update the cycle of the element.				*/
1197 /*--------------------------------------------------------------*/
1198 
checkcycle(genericptr genptr,short dir)1199 int checkcycle(genericptr genptr, short dir)
1200 {
1201    pointselect *tmpptr;
1202    short tmppt, points;
1203    genericptr *pgen;
1204 
1205    switch (genptr->type) {
1206       case POLYGON:
1207 	 if (((polyptr)genptr)->cycle == NULL)
1208 	    tmpptr = NULL;
1209 	 else {
1210 	    for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) {
1211 	       if (tmpptr->flags & REFERENCE) break;
1212 	       if (tmpptr->flags & LASTENTRY) break;
1213 	    }
1214 	    if (!(tmpptr->flags & REFERENCE)) tmpptr = ((polyptr)genptr)->cycle;
1215 	 }
1216 	 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1217 	 points = ((polyptr)genptr)->number;
1218 	 break;
1219       case SPLINE:
1220 	 if (((splineptr)genptr)->cycle == NULL)
1221 	    tmpptr = NULL;
1222 	 else {
1223 	    for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) {
1224 	       if (tmpptr->flags & REFERENCE) break;
1225 	       if (tmpptr->flags & LASTENTRY) break;
1226 	    }
1227 	    if (!(tmpptr->flags & REFERENCE)) tmpptr = ((splineptr)genptr)->cycle;
1228 	 }
1229 	 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1230 	 points = 4;
1231 	 break;
1232       case ARC:
1233 	 tmpptr = ((arcptr)genptr)->cycle;
1234 	 tmppt = (tmpptr == NULL) ? -1 : tmpptr->number;
1235 	 points = 4;
1236 	 break;
1237       case PATH:
1238 	 for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist +
1239 		((pathptr)genptr)->parts; pgen++) {
1240 	    if ((tmppt = checkcycle(*pgen, dir)) >= 0)
1241 	       return tmppt;
1242 	 }
1243 	 break;
1244       default:
1245 	 tmppt = -1;
1246 	 break;
1247    }
1248    if (tmppt >= 0) {	/* Ignore nonexistent cycles */
1249       tmppt += dir;
1250       if (tmppt < 0) tmppt += points;
1251       tmppt %= points;
1252    }
1253    return tmppt;
1254 }
1255 
1256 /*--------------------------------------------------------------*/
1257 /* Change to the next part of a path for editing		*/
1258 /* For now, |dir| is treated as 1 regardless of its value.	*/
1259 /*--------------------------------------------------------------*/
1260 
nextpathcycle(pathptr nextpath,short dir)1261 void nextpathcycle(pathptr nextpath, short dir)
1262 {
1263    genericptr ppart = getsubpart(nextpath, NULL);
1264    genericptr *ggen;
1265    XPoint *curpt;
1266    polyptr thispoly;
1267    splineptr thisspline;
1268    pointselect *cptr;
1269    short cycle, newcycle;
1270 
1271    /* Simple cases---don't need to switch elements */
1272 
1273    switch (ELEMENTTYPE(ppart)) {
1274       case POLYGON:
1275 	 thispoly = (polyptr)ppart;
1276 	 cptr = thispoly->cycle;
1277 	 if (cptr == NULL) return;
1278 	 curpt = thispoly->points + cptr->number;
1279          newcycle = checkcycle(ppart, dir);
1280          advancecycle(&ppart, newcycle);
1281 	 if (cptr->number < thispoly->number && cptr->number > 0) {
1282 	    checkwarp(thispoly->points + cptr->number);
1283 	    removeothercycles(nextpath, ppart);
1284 	    updatepath(nextpath);
1285 	    return;
1286 	 }
1287 	 break;
1288       case SPLINE:
1289 	 thisspline = (splineptr)ppart;
1290 	 cptr = ((splineptr)ppart)->cycle;
1291 	 if (cptr == NULL) return;
1292 	 curpt = &thisspline->ctrl[cptr->number];
1293          newcycle = checkcycle(ppart, dir);
1294          advancecycle(&ppart, newcycle);
1295 	 if (cptr->number < 4 && cptr->number > 0) {
1296 	    checkwarp(&thisspline->ctrl[cptr->number]);
1297 	    removeothercycles(nextpath, ppart);
1298 	    updatepath(nextpath);
1299 	    if (newcycle == 1 || newcycle == 2)
1300 	       addanticycle(nextpath, thisspline, newcycle);
1301 	    return;
1302 	 }
1303 	 break;
1304    }
1305 
1306    /* Moving on to the next element. . . */
1307 
1308    /* If dir < 0, go to the penultimate cycle of the last part	*/
1309    /* If dir > 0, go to the second cycle of the next part 	*/
1310 
1311    for (ggen = nextpath->plist; (*ggen != ppart) &&
1312 		(ggen < nextpath->plist + nextpath->parts); ggen++);
1313 
1314    if (ggen == nextpath->plist + nextpath->parts) return;  /* shouldn't happen! */
1315 
1316    if (dir > 0)
1317       ggen++;
1318    else
1319       ggen--;
1320 
1321    if (ggen < nextpath->plist)
1322       ggen = nextpath->plist + nextpath->parts - 1;
1323    else if (ggen == nextpath->plist + nextpath->parts)
1324       ggen = nextpath->plist;
1325 
1326    removecycle((genericptr *)(&nextpath));
1327 
1328    /* The next point to edit is the first point in the next segment	*/
1329    /* that is not at the same position as the one we were last editing.	*/
1330 
1331    switch (ELEMENTTYPE(*ggen)) {
1332       case POLYGON:
1333 	 thispoly = TOPOLY(ggen);
1334 	 cycle = (dir > 0) ? 0 : thispoly->number - 1;
1335 	 addcycle(ggen, cycle, 0);
1336 	 makerefcycle(thispoly->cycle, cycle);
1337 	 if ((thispoly->points + cycle)->x == curpt->x &&
1338 		(thispoly->points + cycle)->y == curpt->y) {
1339             newcycle = checkcycle((genericptr)thispoly, 1);
1340 	    advancecycle(ggen, newcycle);
1341 	    cycle = newcycle;
1342 	 }
1343          checkwarp(thispoly->points + cycle);
1344 	 break;
1345       case SPLINE:
1346 	 thisspline = TOSPLINE(ggen);
1347 	 cycle = (dir > 0) ? 0 : 3;
1348 	 addcycle(ggen, cycle, 0);
1349 	 makerefcycle(thisspline->cycle, cycle);
1350 	 if (thisspline->ctrl[cycle].x == curpt->x &&
1351 		thisspline->ctrl[cycle].y == curpt->y) {
1352             newcycle = checkcycle((genericptr)thisspline, 1);
1353 	    advancecycle(ggen, newcycle);
1354 	    cycle = newcycle;
1355 	    if (cycle == 1 || cycle == 2)
1356 	       addanticycle(nextpath, thisspline, cycle);
1357 	 }
1358 	 checkwarp(&(thisspline->ctrl[cycle]));
1359 	 break;
1360    }
1361    updatepath(nextpath);
1362 }
1363 
1364 /*--------------------------------------------------------------*/
1365 /* Change to next edit point on a polygon			*/
1366 /*--------------------------------------------------------------*/
1367 
nextpolycycle(polyptr * nextpoly,short dir)1368 void nextpolycycle(polyptr *nextpoly, short dir)
1369 {
1370    short newcycle;
1371 
1372    newcycle = checkcycle((genericptr)(*nextpoly), dir);
1373    advancecycle((genericptr *)nextpoly, newcycle);
1374    findconstrained(*nextpoly);
1375    printeditbindings();
1376 
1377    newcycle = (*nextpoly)->cycle->number;
1378    checkwarp((*nextpoly)->points + newcycle);
1379 }
1380 
1381 /*--------------------------------------------------------------*/
1382 /* Change to next edit cycle on a spline			*/
1383 /*--------------------------------------------------------------*/
1384 
nextsplinecycle(splineptr * nextspline,short dir)1385 void nextsplinecycle(splineptr *nextspline, short dir)
1386 {
1387    short newcycle;
1388    newcycle = checkcycle((genericptr)(*nextspline), dir);
1389    advancecycle((genericptr *)nextspline, newcycle);
1390 
1391    if (newcycle == 1 || newcycle == 2)
1392       Wprintf("Adjust control point");
1393    else
1394       Wprintf("Adjust endpoint position");
1395 
1396    checkwarp(&(*nextspline)->ctrl[newcycle]);
1397 }
1398 
1399 /*--------------------------------------------------------------*/
1400 /* Warp pointer to the edit point on an arc.			*/
1401 /*--------------------------------------------------------------*/
1402 
warparccycle(arcptr nextarc,short cycle)1403 void warparccycle(arcptr nextarc, short cycle)
1404 {
1405    XPoint curang;
1406    double rad;
1407 
1408    switch(cycle) {
1409       case 0:
1410 	 curang.x = nextarc->position.x + abs(nextarc->radius);
1411 	 curang.y = nextarc->position.y;
1412 	 if (abs(nextarc->radius) != nextarc->yaxis)
1413 	    Wprintf("Adjust ellipse size");
1414 	 else
1415 	    Wprintf("Adjust arc radius");
1416 	 break;
1417       case 1:
1418          rad = (double)(nextarc->angle1 * RADFAC);
1419          curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad);
1420          curang.y = nextarc->position.y + nextarc->yaxis * sin(rad);
1421 	 Wprintf("Adjust arc endpoint");
1422   	 break;
1423       case 2:
1424          rad = (double)(nextarc->angle2 * RADFAC);
1425          curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad);
1426          curang.y = nextarc->position.y + nextarc->yaxis * sin(rad);
1427 	 Wprintf("Adjust arc endpoint");
1428 	 break;
1429       case 3:
1430 	 curang.x = nextarc->position.x;
1431 	 curang.y = nextarc->position.y + nextarc->yaxis;
1432 	 Wprintf("Adjust ellipse minor axis");
1433 	 break;
1434    }
1435    checkwarp(&curang);
1436 }
1437 
1438 /*--------------------------------------------------------------*/
1439 /* Change to next edit cycle on an arc				*/
1440 /*--------------------------------------------------------------*/
1441 
nextarccycle(arcptr * nextarc,short dir)1442 void nextarccycle(arcptr *nextarc, short dir)
1443 {
1444    short newcycle;
1445 
1446    newcycle = checkcycle((genericptr)(*nextarc), dir);
1447    advancecycle((genericptr *)nextarc, newcycle);
1448    warparccycle(*nextarc, newcycle);
1449 }
1450 
1451 /*------------------------------------------------------*/
1452 /* Get a numerical response from the keyboard (0-9)     */
1453 /*------------------------------------------------------*/
1454 
1455 #ifndef TCL_WRAPPER
1456 
getkeynum()1457 short getkeynum()
1458 {
1459    XEvent event;
1460    XKeyEvent *keyevent = (XKeyEvent *)(&event);
1461    KeySym keypressed;
1462 
1463    for (;;) {
1464       XNextEvent(dpy, &event);
1465       if (event.type == KeyPress) break;
1466       else xcDispatchEvent(&event);
1467    }
1468    XLookupString(keyevent, _STR, 150, &keypressed, NULL);
1469    if (keypressed > XK_0 && keypressed <= XK_9)
1470       return (short)(keypressed - XK_1);
1471    else
1472       return -1;
1473 }
1474 
1475 #endif
1476 
1477 /*--------------------------*/
1478 /* Register a "press" event */
1479 /*--------------------------*/
1480 
1481 #ifdef TCL_WRAPPER
makepress(ClientData clientdata)1482 void makepress(ClientData clientdata)
1483 #else
1484 void makepress(XtPointer clientdata, xcIntervalId *id)
1485 #endif
1486 {
1487    int keywstate = (int)((pointertype)clientdata);
1488 #ifndef TCL_WRAPPER
1489    UNUSED(id);
1490 #endif
1491 
1492    /* Button/Key was pressed long enough to make a "press", not a "tap" */
1493 
1494    areawin->time_id = 0;
1495    pressmode = keywstate;
1496    eventdispatch(keywstate | HOLD_MASK, areawin->save.x, areawin->save.y);
1497 }
1498 
1499 /*------------------------------------------------------*/
1500 /* Handle button events as if they were keyboard events */
1501 /*------------------------------------------------------*/
1502 
buttonhandler(xcWidget w,caddr_t clientdata,XButtonEvent * event)1503 void buttonhandler(xcWidget w, caddr_t clientdata, XButtonEvent *event)
1504 {
1505    XKeyEvent *kevent = (XKeyEvent *)event;
1506 
1507    if (event->type == ButtonPress)
1508       kevent->type = KeyPress;
1509    else
1510       kevent->type = KeyRelease;
1511 
1512    switch (event->button) {
1513       case Button1:
1514 	 kevent->state |= Button1Mask;
1515          break;
1516       case Button2:
1517 	 kevent->state |= Button2Mask;
1518          break;
1519       case Button3:
1520 	 kevent->state |= Button3Mask;
1521          break;
1522       case Button4:
1523 	 kevent->state |= Button4Mask;
1524          break;
1525       case Button5:
1526 	 kevent->state |= Button5Mask;
1527          break;
1528    }
1529    keyhandler(w, clientdata, kevent);
1530 }
1531 
1532 /*--------------------------------------------------------------*/
1533 /* Edit operations specific to polygons (point manipulation)	*/
1534 /*--------------------------------------------------------------*/
1535 
poly_edit_op(int op)1536 void poly_edit_op(int op)
1537 {
1538    genericptr keygen = *(EDITPART);
1539    polyptr lwire;
1540    XPoint *lpoint;
1541    short cycle;
1542 
1543    if (IS_PATH(keygen))
1544       keygen = getsubpart((pathptr)keygen, NULL);
1545 
1546    switch(ELEMENTTYPE(keygen)) {
1547       case POLYGON: {
1548    	 lwire = (polyptr)keygen;
1549 
1550          /* Remove a point from the polygon */
1551 	 if (op == XCF_Edit_Delete) {
1552 	    if (lwire->number < 3) return;
1553 	    if (lwire->number == 3 && !(lwire->style & UNCLOSED))
1554 	       lwire->style |= UNCLOSED;
1555 	    cycle = checkcycle((genericptr)lwire, 0);
1556 	    lwire->number--;
1557 	    for (lpoint = lwire->points + cycle; lpoint <
1558 			lwire->points + lwire->number; lpoint++)
1559 	       *lpoint = *(lpoint + 1);
1560 	    if (eventmode == EPOLY_MODE)
1561 	       poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1562 	    else
1563 	       path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1564 	    nextpolycycle(&lwire, -1);
1565          }
1566 
1567          /* Add a point to the polygon */
1568 	 else if (op == XCF_Edit_Insert || op == XCF_Edit_Append) {
1569 	    lwire->number++;
1570 	    lwire->points = (XPoint *)realloc(lwire->points, lwire->number
1571 		      * sizeof(XPoint));
1572 	    cycle = checkcycle((genericptr)lwire, 0);
1573 	    for (lpoint = lwire->points + lwire->number - 1; lpoint > lwire->
1574 		         points + cycle; lpoint--)
1575 	       *lpoint = *(lpoint - 1);
1576 	    if (eventmode == EPOLY_MODE)
1577 	       poly_mode_draw(xcDRAW_EDIT, TOPOLY(EDITPART));
1578 	    else
1579 	       path_mode_draw(xcDRAW_EDIT, TOPATH(EDITPART));
1580 	    if (op == XCF_Edit_Append)
1581 	       nextpolycycle(&lwire, 1);
1582          }
1583 
1584 	 /* Parameterize the position of a polygon point */
1585 	 else if (op == XCF_Edit_Param) {
1586 	    cycle = checkcycle((genericptr)lwire, 0);
1587 	    makenumericalp(&keygen, P_POSITION_X, NULL, cycle);
1588 	    makenumericalp(&keygen, P_POSITION_Y, NULL, cycle);
1589 	 }
1590       }
1591    }
1592 }
1593 
1594 /*----------------------------------------------------------------------*/
1595 /* Handle attachment of edited elements to nearby elements		*/
1596 /*----------------------------------------------------------------------*/
1597 
attach_to()1598 void attach_to()
1599 {
1600    /* Conditions: One element is selected, key "A" is pressed.	*/
1601    /* Then there must exist a spline, polygon, arc, or label	*/
1602    /* to attach to.						*/
1603 
1604    if (areawin->selects <= 1) {
1605       short *refsel;
1606 
1607       if (areawin->attachto >= 0) {
1608 	 areawin->attachto = -1;	/* default value---no attachments */
1609 	 Wprintf("Unconstrained moving");
1610       }
1611       else {
1612 	 int select_prev;
1613 
1614 	 select_prev = areawin->selects;
1615 	 refsel = select_add_element(SPLINE|ARC|POLYGON|LABEL|OBJINST);
1616 	 if ((refsel != NULL) && (areawin->selects > select_prev)) {
1617 
1618 	    /* transfer refsel over to attachto */
1619 
1620 	    areawin->attachto = *(refsel + areawin->selects - 1);
1621 	    areawin->selects--;
1622 	    if (areawin->selects == 0) freeselects();
1623 	    XTopSetForeground(SELTOCOLOR(refsel));
1624 	    easydraw(areawin->attachto, DEFAULTCOLOR);
1625 
1626 	    /* restore graphics state */
1627 	    SetForeground(dpy, areawin->gc, areawin->gccolor);
1628 
1629 	    Wprintf("Constrained attach");
1630 
1631 	    /* Starting a new wire? */
1632 	    if (eventmode == NORMAL_MODE) {
1633 	       XPoint newpos, userpt;
1634 	       userpt = UGetCursorPos();
1635 	       findattach(&newpos, NULL, &userpt);
1636 	       startwire(&newpos);
1637 	       eventmode = WIRE_MODE;
1638 	       areawin->attachto = -1;
1639 	    }
1640 	 }
1641 	 else {
1642 	    Wprintf("Nothing found to attach to");
1643 	 }
1644       }
1645    }
1646 }
1647 
1648 /*--------------------------------------------------------------*/
1649 /* This function returns TRUE if the indicated function is	*/
1650 /* compatible with the current eventmode;  that is, whether	*/
1651 /* the function could ever be called from eventdispatch()	*/
1652 /* given the existing eventmode.				*/
1653 /*								*/
1654 /* Note that this function has to be carefully written or the	*/
1655 /* function dispatch mechanism can put xcircuit into a bad	*/
1656 /* state.							*/
1657 /*--------------------------------------------------------------*/
1658 
compatible_function(int function)1659 Boolean compatible_function(int function)
1660 {
1661    int r = FALSE;
1662    char *funcname;
1663 
1664    switch(function) {
1665       case XCF_Text_Left: case XCF_Text_Right:
1666       case XCF_Text_Home: case XCF_Text_End:
1667       case XCF_Text_Return: case XCF_Text_Delete:
1668       case XCF_Text_Delete_Param:
1669 	 r = (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE ||
1670 		eventmode == ETEXT_MODE) ?
1671 		TRUE : FALSE;
1672 	 break;
1673 
1674       case XCF_Linebreak: case XCF_Halfspace:
1675       case XCF_Quarterspace: case XCF_TabStop:
1676       case XCF_TabForward: case XCF_TabBackward:
1677       case XCF_Superscript: case XCF_Subscript:
1678       case XCF_Normalscript: case XCF_Underline:
1679       case XCF_Overline: case XCF_Font:
1680       case XCF_Boldfont: case XCF_Italicfont:
1681       case XCF_Normalfont: case XCF_ISO_Encoding:
1682       case XCF_Special: case XCF_Text_Split:
1683       case XCF_Text_Up: case XCF_Text_Down:
1684       case XCF_Parameter:
1685 	 r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) ?
1686 		TRUE : FALSE;
1687 	 break;
1688 
1689       case XCF_Anchor:
1690 	 r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE ||
1691 		eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1692 		eventmode == NORMAL_MODE) ?
1693 		TRUE : FALSE;
1694 	 break;
1695 
1696       case XCF_Edit_Delete: case XCF_Edit_Insert: case XCF_Edit_Append:
1697       case XCF_Edit_Param:
1698 	 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE) ?
1699 		TRUE : FALSE;
1700 	 break;
1701 
1702       case XCF_Edit_Next:
1703 	 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE ||
1704 		eventmode == EINST_MODE || eventmode == EARC_MODE ||
1705 		eventmode == ESPLINE_MODE) ?
1706 		TRUE : FALSE;
1707 	 break;
1708 
1709       case XCF_Attach:
1710 	 r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE ||
1711 		eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1712 		eventmode == WIRE_MODE || eventmode == NORMAL_MODE) ?
1713 		TRUE : FALSE;
1714 	 break;
1715 
1716       case XCF_Rotate: case XCF_Flip_X:
1717       case XCF_Flip_Y:
1718 	 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1719 		eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) ?
1720 		TRUE : FALSE;
1721 	 break;
1722 
1723       case XCF_Snap: case XCF_Swap:
1724 	 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1725 		eventmode == NORMAL_MODE) ?
1726 		TRUE : FALSE;
1727 	 break;
1728 
1729       case XCF_Double_Snap: case XCF_Halve_Snap:
1730       case XCF_SnapTo:
1731 	 r = (eventmode == CATALOG_MODE || eventmode == CATTEXT_MODE ||
1732 		eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ?
1733 		FALSE : TRUE;
1734 	 break;
1735 
1736       case XCF_Library_Pop:
1737 	 r = (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) ?
1738 		TRUE : FALSE;
1739 	 break;
1740 
1741       case XCF_Library_Edit: case XCF_Library_Delete:
1742       case XCF_Library_Duplicate: case XCF_Library_Hide:
1743       case XCF_Library_Virtual: case XCF_Library_Move:
1744       case XCF_Library_Copy:
1745 	 r = (eventmode == CATALOG_MODE) ?
1746 		TRUE : FALSE;
1747 	 break;
1748 
1749       case XCF_Library_Directory:
1750 	 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1751 		eventmode == ASSOC_MODE) ?
1752 		TRUE : FALSE;
1753 	 break;
1754 
1755       case XCF_Next_Library:
1756 	 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1757 		eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ?
1758 		TRUE : FALSE;
1759 	 break;
1760 
1761       case XCF_Select: case XCF_Exit:
1762 	 r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ?
1763 		TRUE : FALSE;
1764 	 break;
1765 
1766       case XCF_Pop:
1767 	 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1768 		eventmode == CATALOG_MODE || eventmode == NORMAL_MODE ||
1769 		eventmode == ASSOC_MODE) ?
1770 		TRUE : FALSE;
1771 	 break;
1772 
1773       case XCF_Push:
1774 	 r = (eventmode == MOVE_MODE || eventmode == COPY_MODE ||
1775 		eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ?
1776 		TRUE : FALSE;
1777 	 break;
1778 
1779       case XCF_SelectBox: case XCF_Wire:
1780       case XCF_Delete: case XCF_Rescale:
1781       case XCF_Pin_Label: case XCF_Pin_Global:
1782       case XCF_Info_Label: case XCF_Connectivity:
1783       case XCF_Box: case XCF_Arc:
1784       case XCF_Text: case XCF_Exchange:
1785       case XCF_Copy: case XCF_Virtual:
1786       case XCF_Page_Directory: case XCF_Join:
1787       case XCF_Unjoin: case XCF_Spline:
1788       case XCF_Edit: case XCF_Undo:
1789       case XCF_Redo: case XCF_Select_Save:
1790       case XCF_Unselect: case XCF_Dashed:
1791       case XCF_Dotted: case XCF_Solid:
1792       case XCF_Dot: case XCF_Write:
1793       case XCF_Netlist: case XCF_Sim:
1794       case XCF_SPICE: case XCF_SPICEflat:
1795       case XCF_PCB: case XCF_Move:
1796 	 r = (eventmode == NORMAL_MODE) ?
1797 		TRUE : FALSE;
1798 	 break;
1799 
1800       case XCF_Nothing: case XCF_View:
1801       case XCF_Redraw: case XCF_Zoom_In:
1802       case XCF_Zoom_Out: case XCF_Pan:
1803       case XCF_Page: case XCF_Help:
1804       case XCF_Cancel: case XCF_Prompt:
1805 	 r = TRUE;
1806 	 break;
1807 
1808       case XCF_Continue_Copy:
1809       case XCF_Finish_Copy:
1810 	 r = (eventmode == COPY_MODE) ?
1811 		TRUE : FALSE;
1812 	 break;
1813 
1814       case XCF_Continue_Element:
1815       case XCF_Finish_Element:
1816 	 r = (eventmode == WIRE_MODE || eventmode == BOX_MODE ||
1817 		eventmode == ARC_MODE || eventmode == SPLINE_MODE ||
1818 		eventmode == EPATH_MODE || eventmode == EPOLY_MODE ||
1819 		eventmode == EARC_MODE || eventmode == ESPLINE_MODE ||
1820 		eventmode == MOVE_MODE || eventmode == CATMOVE_MODE ||
1821 		eventmode == EINST_MODE || eventmode == RESCALE_MODE) ?
1822 		TRUE : FALSE;
1823 	 break;
1824 
1825       case XCF_Cancel_Last:
1826 	 r = (eventmode == WIRE_MODE || eventmode == ARC_MODE ||
1827 		eventmode == SPLINE_MODE || eventmode == EPATH_MODE ||
1828 		eventmode == EPOLY_MODE || eventmode == EARC_MODE ||
1829 		eventmode == EINST_MODE || eventmode == ESPLINE_MODE) ?
1830 		TRUE : FALSE;
1831 	 break;
1832 
1833       case XCF_Finish:
1834 	 r = (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE ||
1835 		eventmode == ASSOC_MODE || eventmode == CATALOG_MODE ||
1836 		eventmode == CATTEXT_MODE || eventmode == MOVE_MODE ||
1837 		eventmode == RESCALE_MODE || eventmode == SELAREA_MODE ||
1838 		eventmode == PAN_MODE || eventmode == NORMAL_MODE ||
1839 		eventmode == CATMOVE_MODE) ?
1840 		TRUE : FALSE;
1841 	 break;
1842 
1843       default:	/* Function type was not handled. */
1844 	 funcname = func_to_string(function);
1845 	 if (funcname == NULL)
1846 	    Wprintf("Error:  \"%s\" is not a known function!");
1847 	 else
1848 	    Wprintf("Error:  Function type \"%s\" (%d) not handled by "
1849 		"compatible_function()", func_to_string(function),
1850 		function);
1851 	 break;
1852    }
1853    return r;
1854 }
1855 
1856 /*----------------------------------------------------------------------*/
1857 /* Main event dispatch routine.  Call one of the known routines based	*/
1858 /* on the key binding.  Some handling is done by secondary dispatch	*/
1859 /* routines in other files;  when this is done, the key binding is	*/
1860 /* determined here and the bound operation type passed to the secondary	*/
1861 /* dispatch routine.							*/
1862 /*									*/
1863 /* Return value:  0 if event was handled, -1 if not.			*/
1864 /*----------------------------------------------------------------------*/
1865 
eventdispatch(int keywstate,int x,int y)1866 int eventdispatch(int keywstate, int x, int y)
1867 {
1868    short value;		/* For return values from boundfunction() */
1869    int function;	/* What function should be invoked	  */
1870    int handled = -1;    /* event was handled */
1871 
1872    /* Invalid key state returned from getkeysignature(); usually this	*/
1873    /* means a modifier key pressed by itself.				*/
1874 
1875    if (keywstate == -1) return -1;
1876    function = boundfunction(areawin->area, keywstate, &value);
1877 
1878    /* Check for ASCII or ISO-Latin1-9 characters in keywstate while in	*/
1879    /* a text-entry state.  Only the function XCF_Special is allowed in	*/
1880    /* text-entry mode, because XCF_Special can be used to enter the	*/
1881    /* character bound to the XCF_Special function.			*/
1882 
1883    if (keywstate >= 32 && keywstate < 256) {
1884       if (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE ||
1885 		eventmode == ETEXT_MODE) {
1886 	 if (function != XCF_Special)
1887 	    handled = labeltext(keywstate, NULL);
1888 	 else if (eventmode != CATTEXT_MODE) {
1889 	    labelptr elabel = TOLABEL(EDITPART);
1890 	    if (elabel->anchor & LATEXLABEL)
1891 	       handled = labeltext(keywstate, NULL);
1892 	 }
1893       }
1894    }
1895 
1896    if (handled == -1) {
1897       if (function > -1)
1898          handled = functiondispatch(function, value, x, y);
1899       else {
1900          char *keystring = key_to_string(keywstate);
1901 #ifdef HAVE_PYTHON
1902          if (python_key_command(keywstate) < 0)
1903 #endif
1904          Wprintf("Key \'%s\' is not bound to a macro", keystring);
1905          free(keystring);
1906       }
1907    }
1908 
1909    if (areawin->redraw_needed)
1910       drawarea(NULL, NULL, NULL);
1911 
1912    return handled;
1913 }
1914 
1915 /*----------------------------------------------------------------------*/
1916 /* Dispatch actions by function number.  Note that the structure of	*/
1917 /* this function is closely tied to the routine compatible_function().	*/
1918 /*----------------------------------------------------------------------*/
1919 
functiondispatch(int function,short value,int x,int y)1920 int functiondispatch(int function, short value, int x, int y)
1921 {
1922    int result = 0;
1923 
1924    switch (eventmode) {
1925       case MOVE_MODE:
1926       case COPY_MODE:
1927          snap(x, y, &areawin->save);
1928 	 break;
1929       case NORMAL_MODE:
1930 	 window_to_user(x, y, &areawin->save);
1931 	 break;
1932       default:
1933 	 break;
1934    }
1935 
1936    switch(function) {
1937       case XCF_Page:
1938 	 if (value < 0 || value > xobjs.pages)
1939 	    Wprintf("Page %d out of range.", (int)value);
1940 	 else
1941 	    newpage(value - 1);
1942 	 break;
1943       case XCF_Anchor:
1944 	 reanchor(value);
1945 	 break;
1946       case XCF_Superscript:
1947 	 labeltext(SUPERSCRIPT, (char *)1);
1948 	 break;
1949       case XCF_Subscript:
1950 	 labeltext(SUBSCRIPT, (char *)1);
1951 	 break;
1952       case XCF_Normalscript:
1953 	 labeltext(NORMALSCRIPT, (char *)1);
1954 	 break;
1955       case XCF_Font:
1956 	 setfont(NULL, 1000, NULL);
1957 	 break;
1958       case XCF_Boldfont:
1959 	 fontstyle(NULL, 1, NULL);
1960 	 break;
1961       case XCF_Italicfont:
1962 	 fontstyle(NULL, 2, NULL);
1963 	 break;
1964       case XCF_Normalfont:
1965 	 fontstyle(NULL, 0, NULL);
1966 	 break;
1967       case XCF_Underline:
1968 	 labeltext(UNDERLINE, (char *)1);
1969 	 break;
1970       case XCF_Overline:
1971 	 labeltext(OVERLINE, (char *)1);
1972 	 break;
1973       case XCF_ISO_Encoding:
1974 	 fontencoding(NULL, 2, NULL);
1975 	 break;
1976       case XCF_Halfspace:
1977 	 labeltext(HALFSPACE, (char *)1);
1978 	 break;
1979       case XCF_Quarterspace:
1980 	 labeltext(QTRSPACE, (char *)1);
1981 	 break;
1982       case XCF_Special:
1983 	 result = dospecial();
1984 	 break;
1985 #ifndef TCL_WRAPPER
1986       case XCF_Parameter:
1987 	 insertparam();
1988 	 break;
1989 #endif
1990       case XCF_TabStop:
1991 	 labeltext(TABSTOP, (char *)1);
1992 	 break;
1993       case XCF_TabForward:
1994 	 labeltext(TABFORWARD, (char *)1);
1995 	 break;
1996       case XCF_TabBackward:
1997 	 labeltext(TABBACKWARD, (char *)1);
1998 	 break;
1999       case XCF_Text_Return:
2000 	 labeltext(TEXT_RETURN, (char *)1);
2001 	 break;
2002       case XCF_Text_Delete:
2003 	 labeltext(TEXT_DELETE, (char *)1);
2004 	 break;
2005       case XCF_Text_Delete_Param:
2006 	 labeltext(TEXT_DEL_PARAM, (char *)1);
2007 	 break;
2008       case XCF_Text_Right:
2009 	 labeltext(TEXT_RIGHT, (char *)1);
2010 	 break;
2011       case XCF_Text_Left:
2012 	 labeltext(TEXT_LEFT, (char *)1);
2013 	 break;
2014       case XCF_Text_Up:
2015 	 labeltext(TEXT_UP, (char *)1);
2016 	 break;
2017       case XCF_Text_Down:
2018 	 labeltext(TEXT_DOWN, (char *)1);
2019 	 break;
2020       case XCF_Text_Split:
2021 	 labeltext(TEXT_SPLIT, (char *)1);
2022 	 break;
2023       case XCF_Text_Home:
2024 	 labeltext(TEXT_HOME, (char *)1);
2025 	 break;
2026       case XCF_Text_End:
2027 	 labeltext(TEXT_END, (char *)1);
2028 	 break;
2029       case XCF_Linebreak:
2030 	 labeltext(RETURN, (char *)1);
2031 	 break;
2032       case XCF_Edit_Param:
2033       case XCF_Edit_Delete:
2034       case XCF_Edit_Insert:
2035       case XCF_Edit_Append:
2036 	 poly_edit_op(function);
2037 	 break;
2038       case XCF_Edit_Next:
2039 	 path_op(*(EDITPART), XCF_Continue_Element, x, y);
2040 	 break;
2041       case XCF_Attach:
2042 	 attach_to();
2043 	 break;
2044       case XCF_Next_Library:
2045 	 changecat();
2046 	 break;
2047       case XCF_Library_Directory:
2048 	 startcatalog(NULL, LIBLIB, NULL);
2049 	 break;
2050       case XCF_Library_Edit:
2051 	 window_to_user(x, y, &areawin->save);
2052 	 unselect_all();
2053 	 select_element(LABEL);
2054 	 if (areawin->selects == 1)
2055 	    edit(x, y);
2056 	 break;
2057       case XCF_Library_Delete:
2058 	 catalog_op(XCF_Select, x, y);
2059 	 catdelete();
2060 	 break;
2061       case XCF_Library_Duplicate:
2062 	 catalog_op(XCF_Select, x, y);
2063 	 copycat();
2064 	 break;
2065       case XCF_Library_Hide:
2066 	 catalog_op(XCF_Select, x, y);
2067 	 cathide();
2068 	 break;
2069       case XCF_Library_Virtual:
2070 	 catalog_op(XCF_Select, x, y);
2071 	 catvirtualcopy();
2072 	 break;
2073       case XCF_Page_Directory:
2074 	 startcatalog(NULL, PAGELIB, NULL);
2075 	 break;
2076       case XCF_Library_Copy:
2077       case XCF_Library_Pop:
2078 	 catalog_op(function, x, y);
2079 	 break;
2080       case XCF_Virtual:
2081 	 copyvirtual();
2082 	 break;
2083       case XCF_Help:
2084 	 starthelp(NULL, NULL, NULL);
2085 	 break;
2086       case XCF_Redraw:
2087    	 areawin->redraw_needed = True;
2088 	 break;
2089       case XCF_View:
2090 	 zoomview(NULL, NULL, NULL);
2091 	 break;
2092       case XCF_Zoom_In:
2093 	 zoominrefresh(x, y);
2094 	 break;
2095       case XCF_Zoom_Out:
2096 	 zoomoutrefresh(x, y);
2097 	 break;
2098       case XCF_Pan:
2099 	 panrefresh(value, x, y, 0.3);
2100 	 break;
2101       case XCF_Double_Snap:
2102 	 setsnap(1);
2103 	 break;
2104       case XCF_Halve_Snap:
2105 	 setsnap(-1);
2106 	 break;
2107       case XCF_Write:
2108 #ifdef TCL_WRAPPER
2109 	 Tcl_Eval(xcinterp, "xcircuit::promptsavepage");
2110 #else
2111 	 outputpopup(NULL, NULL, NULL);
2112 #endif
2113 	 break;
2114       case XCF_Rotate:
2115 	 elementrotate(value, &areawin->save);
2116 	 break;
2117       case XCF_Flip_X:
2118 	 elementflip(&areawin->save);
2119 	 break;
2120       case XCF_Flip_Y:
2121 	 elementvflip(&areawin->save);
2122 	 break;
2123       case XCF_Snap:
2124 	 snapelement();
2125 	 break;
2126       case XCF_SnapTo:
2127 	 if (areawin->snapto) {
2128 	    areawin->snapto = False;
2129 	    Wprintf("Snap-to off");
2130 	 }
2131 	 else {
2132 	    areawin->snapto = True;
2133 	    Wprintf("Snap-to on");
2134 	 }
2135 	 break;
2136       case XCF_Pop:
2137 	 if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) {
2138 	    eventmode = NORMAL_MODE;
2139 	    catreturn();
2140 	 }
2141 	 else
2142 	    popobject(NULL, 0, NULL);
2143 	 break;
2144       case XCF_Push:
2145 	 if (eventmode == CATALOG_MODE) {
2146 	    /* Don't allow push from library directory */
2147 	    if ((areawin->topinstance != xobjs.libtop[LIBLIB])
2148                 	&& (areawin->topinstance != xobjs.libtop[PAGELIB])) {
2149 	       window_to_user(x, y, &areawin->save);
2150 	       eventmode = NORMAL_MODE;
2151 	       pushobject(NULL);
2152 	    }
2153 	 }
2154 	 else
2155 	    pushobject(NULL);
2156 	 break;
2157       case XCF_Delete:
2158 	 deletebutton(x, y);
2159 	 break;
2160       case XCF_Select:
2161 	 if (eventmode == CATALOG_MODE)
2162 	    catalog_op(function, x, y);
2163 	 else
2164 	    select_add_element(ALL_TYPES);
2165 	 break;
2166       case XCF_Box:
2167 	 boxbutton(x, y);
2168 	 break;
2169       case XCF_Arc:
2170 	 arcbutton(x, y);
2171 	 break;
2172       case XCF_Text:
2173 	 eventmode = TEXT_MODE;
2174 	 textbutton(NORMAL, x, y);
2175 	 break;
2176       case XCF_Exchange:
2177 	 exchange();
2178 	 break;
2179       case XCF_Library_Move:
2180 	 /* Don't allow from library directory.  Then fall through to XCF_Move */
2181 	 if (areawin->topinstance == xobjs.libtop[LIBLIB]) break;
2182       case XCF_Move:
2183 	 if (areawin->selects == 0) {
2184 	    was_preselected = FALSE;
2185 	    if (eventmode == CATALOG_MODE)
2186 	       catalog_op(XCF_Select, x, y);
2187 	    else
2188 	       select_element(ALL_TYPES);
2189 	 }
2190 	 else was_preselected = TRUE;
2191 
2192 	 if (areawin->selects > 0) {
2193 	    eventmode = (eventmode == CATALOG_MODE) ? CATMOVE_MODE : MOVE_MODE;
2194 	    u2u_snap(&areawin->save);
2195 	    areawin->origin = areawin->save;
2196 	    reset_cycles();
2197 	    select_connected_pins();
2198 	    XDefineCursor(dpy, areawin->window, ARROW);
2199 	    move_mode_draw(xcDRAW_INIT, NULL);
2200 #ifdef TCL_WRAPPER
2201 	    Tk_CreateEventHandler(areawin->area, ButtonMotionMask |
2202 			PointerMotionMask, (Tk_EventProc *)xctk_drag,
2203 			NULL);
2204 #endif
2205 	 }
2206 	 break;
2207       case XCF_Join:
2208 	 join();
2209 	 break;
2210       case XCF_Unjoin:
2211 	 unjoin();
2212 	 break;
2213       case XCF_Spline:
2214 	 splinebutton(x, y);
2215 	 break;
2216       case XCF_Edit:
2217 	 edit(x, y);
2218 	 break;
2219       case XCF_Undo:
2220 	 undo_action();
2221 	 break;
2222       case XCF_Redo:
2223 	 redo_action();
2224 	 break;
2225       case XCF_Select_Save:
2226 #ifdef TCL_WRAPPER
2227 	 Tcl_Eval(xcinterp, "xcircuit::promptmakeobject");
2228 #else
2229 	 selectsave(NULL, NULL, NULL);
2230 #endif
2231 	 break;
2232       case XCF_Unselect:
2233 	 select_add_element(-ALL_TYPES);
2234 	 break;
2235       case XCF_Dashed:
2236 	 setelementstyle(NULL, DASHED, NOBORDER | DOTTED | DASHED);
2237 	 break;
2238       case XCF_Dotted:
2239 	 setelementstyle(NULL, DOTTED, NOBORDER | DOTTED | DASHED);
2240 	 break;
2241       case XCF_Solid:
2242 	 setelementstyle(NULL, NORMAL, NOBORDER | DOTTED | DASHED);
2243 	 break;
2244       case XCF_Prompt:
2245 	 docommand();
2246 	 break;
2247       case XCF_Dot:
2248 	 snap(x, y, &areawin->save);
2249 	 drawdot(areawin->save.x, areawin->save.y);
2250 	 areawin->redraw_needed = True;
2251 	 drawarea(NULL, NULL, NULL);
2252 	 break;
2253       case XCF_Wire:
2254 	 u2u_snap(&areawin->save);
2255 	 startwire(&areawin->save);
2256 	 eventmode = WIRE_MODE;
2257 	 break;
2258       case XCF_Nothing:
2259 	 DoNothing(NULL, NULL, NULL);
2260 	 break;
2261       case XCF_Exit:
2262 	 quitcheck(areawin->area, NULL, NULL);
2263 	 break;
2264       case XCF_Netlist:
2265 	 callwritenet(NULL, 0, NULL);
2266 	 break;
2267       case XCF_Swap:
2268 	 swapschem(0, -1, NULL);
2269 	 break;
2270       case XCF_Pin_Label:
2271 	 eventmode = TEXT_MODE;
2272 	 textbutton(LOCAL, x, y);
2273 	 break;
2274       case XCF_Pin_Global:
2275 	 eventmode = TEXT_MODE;
2276 	 textbutton(GLOBAL, x, y);
2277 	 break;
2278       case XCF_Info_Label:
2279 	 eventmode = TEXT_MODE;
2280 	 textbutton(INFO, x, y);
2281 	 break;
2282       case XCF_Rescale:
2283 	 if (checkselect(LABEL | OBJINST | GRAPHIC) == TRUE) {
2284 	    eventmode = RESCALE_MODE;
2285 	    rescale_mode_draw(xcDRAW_INIT, NULL);
2286 #ifdef TCL_WRAPPER
2287 	    Tk_CreateEventHandler(areawin->area, PointerMotionMask |
2288 		ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
2289 #else
2290 	    xcAddEventHandler(areawin->area, PointerMotionMask |
2291 		ButtonMotionMask, False, (xcEventHandler)xlib_drag,
2292 		NULL);
2293 #endif
2294 	 }
2295 	 break;
2296       case XCF_SelectBox:
2297 	 startselect();
2298 	 break;
2299       case XCF_Connectivity:
2300 	 connectivity(NULL, NULL, NULL);
2301 	 break;
2302       case XCF_Copy:
2303       case XCF_Continue_Copy:
2304       case XCF_Finish_Copy:
2305 	 copy_op(function, x, y);
2306 	 break;
2307       case XCF_Continue_Element:
2308 	 if (eventmode == CATMOVE_MODE || eventmode == MOVE_MODE ||
2309 		eventmode == RESCALE_MODE)
2310 	    finish_op(XCF_Finish, x, y);
2311 	 else
2312 	    continue_op(function, x, y);
2313 	 break;
2314       case XCF_Finish_Element:
2315       case XCF_Cancel_Last:
2316       case XCF_Cancel:
2317 	 finish_op(function, x, y);
2318 	 break;
2319       case XCF_Finish:
2320 	 if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE)
2321 	    catalog_op(XCF_Library_Pop, x, y);
2322 	 else
2323 	    finish_op(function, x, y);
2324 	 break;
2325       case XCF_Sim:
2326 	 writenet(topobject, "flatsim", "sim");
2327 	 break;
2328       case XCF_SPICE:
2329 	 writenet(topobject, "spice", "spc");
2330 	 break;
2331       case XCF_PCB:
2332 	 writenet(topobject, "pcb", "pcbnet");
2333 	 break;
2334       case XCF_SPICEflat:
2335 	 writenet(topobject, "flatspice", "fspc");
2336 	 break;
2337       case XCF_Graphic:
2338 	 Wprintf("Action not handled");
2339 	 result = -1;
2340 	 break;
2341       case XCF_ChangeStyle:
2342 	 Wprintf("Action not handled");
2343 	 result = -1;
2344 	 break;
2345    }
2346 
2347    /* Ensure that we do not get stuck in suspend mode	*/
2348    /* by removing the suspend state whenever a key is	*/
2349    /* pressed.						*/
2350 
2351    if (xobjs.suspend == 1) {
2352       xobjs.suspend = -1;
2353       refresh(NULL, NULL, NULL);
2354    }
2355    else if (xobjs.suspend != 2)
2356       xobjs.suspend = -1;
2357 
2358    return result;
2359 }
2360 
2361 /* forward declaration */
2362 extern int utf8_reverse_lookup(char *);
2363 
2364 /*------------------------------------------------------*/
2365 /* Get a canonical signature for a button/key event	*/
2366 /*------------------------------------------------------*/
2367 
getkeysignature(XKeyEvent * event)2368 int getkeysignature(XKeyEvent *event)
2369 {
2370    KeySym keypressed;
2371    int keywstate;	/* KeySym with prepended state information	*/
2372 
2373    int utf8enc;		/* 8-bit value bound to UTF-8 encoding */
2374    char buffer[16];
2375    KeySym keysym;
2376    Status status;
2377    static XIM xim = NULL;
2378    static XIC xic = NULL;
2379 
2380 #ifdef _MSC_VER
2381    if (event->keycode == 0 && event->state == 0)
2382 	   return -1;
2383 #endif
2384    XLookupString(event, _STR, 150, &keypressed, NULL);
2385 
2386    /* Ignore Shift, Control, Caps Lock, and Meta (Alt) keys 	*/
2387    /* when pressed alone.					*/
2388 
2389    if (keypressed == XK_Control_L || keypressed == XK_Control_R ||
2390         keypressed == XK_Alt_L || keypressed == XK_Alt_R ||
2391         keypressed == XK_Caps_Lock || keypressed == XK_Shift_L ||
2392 	keypressed == XK_Shift_R)
2393       return -1;
2394 
2395    /* Only keep key state information pertaining to Shift, Caps Lock,	*/
2396    /* Control, and Alt (Meta)						*/
2397 
2398    keywstate = (keypressed & 0xffff);
2399 
2400    /* Convert codes outside the character (0 - 255) range but within	*/
2401    /* the ISO-Latin1,...,9 encoding scheme (256-5120).  X11 has unique	*/
2402    /* keysyms for each character, but the ISO-Latin encodings define	*/
2403    /* mappings to the 8-bit (256) character set.			*/
2404 
2405    if (keywstate >= 256 && keywstate < 5120)
2406       keywstate = XKeysymToKeycode(dpy, (KeySym)keywstate);
2407 
2408    if (event->keycode != 0) {		/* Only for actual key events */
2409 
2410 	/* Get keyboard input method */
2411 	if (xim == NULL) {
2412 	    xim = XOpenIM(dpy, 0, 0, 0);
2413 	    xic = XCreateIC(xim,
2414 			XNInputStyle,   XIMPreeditNothing | XIMStatusNothing,
2415 			XNClientWindow, areawin->window,
2416 			XNFocusWindow,  areawin->window,
2417 			NULL);
2418 	    XSetICFocus(xic);
2419 	}
2420 	if (xic) {
2421 	    /* Do a UTF-8 code lookup */
2422 	    Xutf8LookupString(xic, event, buffer, 15, &keysym, &status);
2423 	    /* Convert a UTF-8 code to a known encoding */
2424 	    utf8enc = utf8_reverse_lookup(buffer);
2425 	    if ((utf8enc != -1) && (utf8enc != (keywstate & 0xff))) keywstate = utf8enc;
2426 	}
2427    }
2428 
2429    /* ASCII values already come upper/lowercase; we only want to register  */
2430    /* a Shift key if it's a non-ASCII key or another modifier is in effect */
2431 
2432    keywstate |= (((LockMask | ControlMask | Mod1Mask) & event->state) << 16);
2433    if (keywstate > 255) keywstate |= ((ShiftMask & event->state) << 16);
2434 
2435    /* Treat button events and key events in the same way by setting	*/
2436    /* a key state for buttons						*/
2437 
2438    if (keypressed == 0)
2439       keywstate |= (((Button1Mask | Button2Mask | Button3Mask | Button4Mask |
2440 		Button5Mask | ShiftMask)
2441 		& event->state) << 16);
2442 
2443    return keywstate;
2444 }
2445 
2446 /*------------------------*/
2447 /* Handle keyboard inputs */
2448 /*------------------------*/
2449 
keyhandler(xcWidget w,caddr_t clientdata,XKeyEvent * event)2450 void keyhandler(xcWidget w, caddr_t clientdata, XKeyEvent *event)
2451 {
2452    int keywstate;	/* KeySym with prepended state information	*/
2453    int func;
2454    UNUSED(w); UNUSED(clientdata);
2455 
2456 #ifdef TCL_WRAPPER
2457    if (popups > 0) return;
2458 #else
2459    if (popups > 0 && help_up == 0) return;
2460 #endif
2461 
2462    if ((event->type == KeyRelease) || (event->type == ButtonRelease)) {
2463 
2464       /* Register a "tap" event if a key or button was released	*/
2465       /* while a timeout event is pending.			*/
2466 
2467       if (areawin->time_id != 0) {
2468          xcRemoveTimeOut(areawin->time_id);
2469          areawin->time_id = 0;
2470          keywstate = getkeysignature(event);
2471          eventdispatch(keywstate, areawin->save.x, areawin->save.y);
2472       }
2473       else {
2474          keywstate = getkeysignature(event);
2475 	 if ((pressmode != 0) && (keywstate == pressmode)) {
2476             /* Events that require hold & drag (namely, MOVE_MODE)	*/
2477 	    /* must be resolved here.  Call finish_op() to ensure	*/
2478 	    /* that we restore xcircuit to	a state of sanity.	*/
2479 
2480 	    finish_op(XCF_Finish, event->x, event->y);
2481 	    pressmode = 0;
2482    	    if (areawin->redraw_needed)
2483                drawarea(NULL, NULL, NULL);
2484          }
2485 	 return;	/* Ignore all other release events */
2486       }
2487    }
2488 
2489    /* Check if any bindings match key/button "hold".  If so, then start	*/
2490    /* the timer and wait for key release or timeout.			*/
2491 
2492    else {
2493       keywstate = getkeysignature(event);
2494       if ((keywstate != -1) && (xobjs.hold == TRUE)) {
2495 
2496 	 /* Establish whether a HOLD modifier binding would apply in	*/
2497 	 /* the current eventmode.  If so, set the HOLD timer.		*/
2498 
2499 	 func = boundfunction(areawin->area, keywstate | HOLD_MASK, NULL);
2500 	 if (func != -1) {
2501             areawin->save.x = event->x;
2502             areawin->save.y = event->y;
2503             areawin->time_id = xcAddTimeOut(app, PRESSTIME,
2504 			makepress, (ClientData)((pointertype)keywstate));
2505             return;
2506 	 }
2507 
2508       }
2509       eventdispatch(keywstate, event->x, event->y);
2510    }
2511 }
2512 
2513 /*--------------------------------*/
2514 /* Set snap spacing from keyboard */
2515 /*--------------------------------*/
2516 
setsnap(short direction)2517 void setsnap(short direction)
2518 {
2519    float oldsnap = xobjs.pagelist[areawin->page]->snapspace;
2520    char buffer[50];
2521 
2522    if (direction > 0) xobjs.pagelist[areawin->page]->snapspace *= 2;
2523    else {
2524       if (oldsnap >= 2.0)
2525          xobjs.pagelist[areawin->page]->snapspace /= 2;
2526       else {
2527 	 measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer);
2528 	 Wprintf("Snap space at minimum value of %s", buffer);
2529       }
2530    }
2531    if (xobjs.pagelist[areawin->page]->snapspace != oldsnap) {
2532       measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer);
2533       Wprintf("Snap spacing set to %s", buffer);
2534       areawin->redraw_needed = True;
2535       drawarea(NULL, NULL, NULL);
2536    }
2537 }
2538 
2539 /*-----------------------------------------*/
2540 /* Reposition an object onto the snap grid */
2541 /*-----------------------------------------*/
2542 
snapelement()2543 void snapelement()
2544 {
2545    short *selectobj;
2546    Boolean preselected;
2547 
2548    preselected = (areawin->selects > 0) ? TRUE : FALSE;
2549    if (!checkselect(ALL_TYPES)) return;
2550    SetForeground(dpy, areawin->gc, BACKGROUND);
2551    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
2552 	+ areawin->selects; selectobj++) {
2553       easydraw(*selectobj, DOFORALL);
2554       switch(SELECTTYPE(selectobj)) {
2555          case OBJINST: {
2556 	    objinstptr snapobj = SELTOOBJINST(selectobj);
2557 
2558             u2u_snap(&snapobj->position);
2559 	    } break;
2560          case GRAPHIC: {
2561 	    graphicptr snapg = SELTOGRAPHIC(selectobj);
2562 
2563             u2u_snap(&snapg->position);
2564 	    } break;
2565          case LABEL: {
2566 	    labelptr snaplabel = SELTOLABEL(selectobj);
2567 
2568 	    u2u_snap(&snaplabel->position);
2569 	    } break;
2570          case POLYGON: {
2571 	    polyptr snappoly = SELTOPOLY(selectobj);
2572 	    pointlist snappoint;
2573 
2574 	    for (snappoint = snappoly->points; snappoint < snappoly->points +
2575 	         snappoly->number; snappoint++)
2576 	       u2u_snap(snappoint);
2577 	    } break;
2578          case ARC: {
2579 	    arcptr snaparc = SELTOARC(selectobj);
2580 
2581 	    u2u_snap(&snaparc->position);
2582 	    if (areawin->snapto) {
2583 	       snaparc->radius = (snaparc->radius /
2584 		    xobjs.pagelist[areawin->page]->snapspace) *
2585 		    xobjs.pagelist[areawin->page]->snapspace;
2586 	       snaparc->yaxis = (snaparc->yaxis /
2587 		    xobjs.pagelist[areawin->page]->snapspace) *
2588 	            xobjs.pagelist[areawin->page]->snapspace;
2589 	    }
2590 	    calcarc(snaparc);
2591 	    } break;
2592 	 case SPLINE: {
2593 	    splineptr snapspline = SELTOSPLINE(selectobj);
2594 	    short i;
2595 
2596 	    for (i = 0; i < 4; i++)
2597 	       u2u_snap(&snapspline->ctrl[i]);
2598 	    calcspline(snapspline);
2599 	    } break;
2600       }
2601       if (preselected || (eventmode != NORMAL_MODE)) {
2602          SetForeground(dpy, areawin->gc, SELECTCOLOR);
2603 	 easydraw(*selectobj, DOFORALL);
2604       }
2605    }
2606    select_invalidate_netlist();
2607    if (eventmode == NORMAL_MODE)
2608       if (!preselected)
2609          unselect_all();
2610 }
2611 
2612 /*----------------------------------------------*/
2613 /* Routines to print the cursor position	*/
2614 /*----------------------------------------------*/
2615 
2616 /*----------------------------------------------*/
2617 /* fast integer power-of-10 routine 		*/
2618 /*----------------------------------------------*/
2619 
ipow10(int a)2620 int ipow10(int a)
2621 {
2622    int i;
2623    char istr[12];
2624 
2625    switch (a) {
2626       case 0: return 1;     break;
2627       case 1: return 10;    break;
2628       case 2: return 100;   break;
2629       case 3: return 1000;  break;
2630       default:
2631          istr[0] = '1';
2632 	 for (i = 1; i < a + 1; i++) istr[i] = '0';
2633 	 istr[i] = '\0';
2634 	 return atoi(istr);
2635 	 break;
2636    }
2637 }
2638 
2639 /*--------------------------------------------------*/
2640 /* find greatest common factor between two integers */
2641 /*--------------------------------------------------*/
2642 
calcgcf(int a,int b)2643 int calcgcf(int a, int b)
2644 {
2645    register int mod;
2646 
2647    if ((mod = a % b) == 0) return (b);
2648    else return (calcgcf(b, mod));
2649 }
2650 
2651 /*--------------------------------------------------------------*/
2652 /* generate a fraction from a float, if possible 		*/
2653 /* fraction returned as a string (must be allocated beforehand)	*/
2654 /*--------------------------------------------------------------*/
2655 
fraccalc(float xyval,char * fstr)2656 void fraccalc(float xyval, char *fstr)
2657 {
2658    short i, t, rept;
2659    int ip, mant, divisor, denom, numer, rpart;
2660    double fp;
2661    char num[10], *nptr = &num[2], *sptr;
2662 
2663    ip = (int)xyval;
2664    fp = fabs(xyval - ip);
2665 
2666    /* write fractional part and grab mantissa as integer */
2667 
2668    sprintf(num, "%1.7f", fp);
2669    num[8] = '\0';		/* no rounding up! */
2670    sscanf(nptr, "%d", &mant);
2671 
2672    if (mant != 0) {    /* search for repeating substrings */
2673       for (i = 1; i <= 3; i++) {
2674          rept = 1;
2675 	 nptr = &num[8] - i;
2676 	 while ((sptr = nptr - rept * i) >= &num[2]) {
2677             for (t = 0; t < i; t++)
2678 	       if (*(sptr + t) != *(nptr + t)) break;
2679 	    if (t != i) break;
2680 	    else rept++;
2681 	 }
2682 	 if (rept > 1) break;
2683       }
2684       nptr = &num[8] - i;
2685       sscanf(nptr, "%d", &rpart);  /* rpart is repeating part of mantissa */
2686       if (i > 3 || rpart == 0) { /* no repeat */
2687 	 divisor = calcgcf(1000000, mant);
2688 	 denom = 1000000 / divisor;
2689       }
2690       else { /* repeat */
2691 	 int z, p, fd;
2692 
2693 	 *nptr = '\0';
2694 	 sscanf(&num[2], "%d", &z);
2695 	 p = ipow10(i) - 1;
2696 	 mant = z * p + rpart;
2697 	 fd = ipow10(nptr - &num[2]) * p;
2698 
2699 	 divisor = calcgcf(fd, mant);
2700 	 denom = fd / divisor;
2701       }
2702       numer = mant / divisor;
2703       if (denom > 1024)
2704 	 sprintf(fstr, "%5.3f", xyval);
2705       else if (ip == 0)
2706          sprintf(fstr, "%hd/%hd", (xyval > 0) ? numer : -numer, denom);
2707       else
2708          sprintf(fstr, "%hd %hd/%hd", ip, numer, denom);
2709    }
2710    else sprintf(fstr, "%hd", ip);
2711 }
2712 
2713 /*------------------------------------------------------------------------------*/
2714 /* Print the position of the cursor in the upper right-hand message window	*/
2715 /*------------------------------------------------------------------------------*/
2716 
printpos(short xval,short yval)2717 void printpos(short xval, short yval)
2718 {
2719    float f1, f2;
2720    float oscale, iscale = (float)xobjs.pagelist[areawin->page]->drawingscale.y /
2721 	(float)xobjs.pagelist[areawin->page]->drawingscale.x;
2722    int llen, lwid;
2723    u_char wlflag = 0;
2724    XPoint *tpoint, *npoint;
2725    char *sptr;
2726    short cycle;
2727 
2728    /* For polygons, print the length (last line of a wire or polygon) or  */
2729    /* length and width (box only)					  */
2730 
2731    if (eventmode == BOX_MODE || eventmode == EPOLY_MODE || eventmode == WIRE_MODE) {
2732       polyptr lwire = (eventmode == BOX_MODE) ?  TOPOLY(ENDPART) : TOPOLY(EDITPART);
2733       if ((eventmode == EPOLY_MODE) && (lwire->number > 2)) {
2734 	 /* sanity check on edit cycle */
2735 	 cycle = (lwire->cycle) ? lwire->cycle->number : -1;
2736 	 if (cycle < 0 || cycle >= lwire->number) {
2737 	    advancecycle((genericptr *)(&lwire), 0);
2738 	    cycle = 0;
2739 	 }
2740 	 tpoint = lwire->points + cycle;
2741 	 npoint = lwire->points + checkcycle((genericptr)lwire, 1);
2742          llen = wirelength(tpoint, npoint);
2743 	 npoint = lwire->points + checkcycle((genericptr)lwire, -1);
2744          lwid = wirelength(tpoint, npoint);
2745          wlflag = 3;
2746 	 if (lwire->style & UNCLOSED) {   /* unclosed polys */
2747 	    if (cycle == 0)
2748 	       wlflag = 1;
2749 	    else if (cycle == lwire->number - 1) {
2750 	       wlflag = 1;
2751 	       llen = lwid;
2752 	    }
2753 	 }
2754 	 if ((npoint->y - tpoint->y) == 0) {	/* swap width and length */
2755 	    int tmp = lwid;
2756 	    lwid = llen;
2757 	    llen = tmp;
2758 	 }
2759       }
2760       else if (eventmode == BOX_MODE) {
2761          tpoint = lwire->points;
2762          npoint = lwire->points + 1;
2763          llen = wirelength(tpoint, npoint);
2764          npoint = lwire->points + 3;
2765          lwid = wirelength(tpoint, npoint);
2766 	 if ((npoint->y - tpoint->y) == 0) {	/* swap width and length */
2767 	    int tmp = lwid;
2768 	    lwid = llen;
2769 	    llen = tmp;
2770 	 }
2771 	 wlflag = 3;
2772       }
2773       else {
2774          tpoint = lwire->points + lwire->number - 1;
2775          llen = wirelength(tpoint - 1, tpoint);
2776          wlflag = 1;
2777       }
2778    }
2779    else if (eventmode == ARC_MODE || eventmode == EARC_MODE) {
2780       arcptr larc = (eventmode == ARC_MODE) ?  TOARC(ENDPART) : TOARC(EDITPART);
2781       llen = larc->radius;
2782       if (abs(larc->radius) != larc->yaxis) {
2783 	 lwid = larc->yaxis;
2784 	 wlflag = 3;
2785       }
2786       else
2787          wlflag = 1;
2788    }
2789 
2790    switch (xobjs.pagelist[areawin->page]->coordstyle) {
2791       case INTERNAL:
2792 	 sprintf(_STR, "%g, %g", xval * iscale, yval * iscale);
2793 	 sptr = _STR + strlen(_STR);
2794 	 if (wlflag) {
2795 	    if (wlflag & 2)
2796 	       sprintf(sptr, " (%g x %g)", llen * iscale, lwid * iscale);
2797 	    else
2798 	       sprintf(sptr, " (length %g)", llen * iscale);
2799 	 }
2800 	 break;
2801       case DEC_INCH:
2802          oscale = xobjs.pagelist[areawin->page]->outscale * INCHSCALE;
2803          f1 = ((float)(xval) * iscale * oscale) / 72.0;
2804          f2 = ((float)(yval) * iscale * oscale) / 72.0;
2805    	 sprintf(_STR, "%5.3f, %5.3f in", f1, f2);
2806 	 sptr = _STR + strlen(_STR);
2807 	 if (wlflag) {
2808             f1 = ((float)(llen) * iscale * oscale) / 72.0;
2809 	    if (wlflag & 2) {
2810                f2 = ((float)(lwid) * iscale * oscale) / 72.0;
2811 	       sprintf(sptr, " (%5.3f x %5.3f in)", f1, f2);
2812 	    }
2813 	    else
2814 	       sprintf(sptr, " (length %5.3f in)", f1);
2815 	 }
2816 	 break;
2817       case FRAC_INCH: {
2818 	 char fstr1[30], fstr2[30];
2819 
2820          oscale = xobjs.pagelist[areawin->page]->outscale * INCHSCALE;
2821 	 fraccalc((((float)(xval) * iscale * oscale) / 72.0), fstr1);
2822          fraccalc((((float)(yval) * iscale * oscale) / 72.0), fstr2);
2823    	 sprintf(_STR, "%s, %s in", fstr1, fstr2);
2824 	 sptr = _STR + strlen(_STR);
2825 	 if (wlflag) {
2826 	    fraccalc((((float)(llen) * iscale * oscale) / 72.0), fstr1);
2827 	    if (wlflag & 2) {
2828 	       fraccalc((((float)(lwid) * iscale * oscale) / 72.0), fstr2);
2829 	       sprintf(sptr, " (%s x %s in)", fstr1, fstr2);
2830 	    }
2831 	    else
2832 	       sprintf(sptr, " (length %s in)", fstr1);
2833 	 }
2834 	 } break;
2835       case CM:
2836          oscale = xobjs.pagelist[areawin->page]->outscale * CMSCALE;
2837          f1 = ((float)(xval) * iscale * oscale) / IN_CM_CONVERT;
2838          f2 = ((float)(yval) * iscale * oscale) / IN_CM_CONVERT;
2839    	 sprintf(_STR, "%5.3f, %5.3f cm", f1, f2);
2840 	 sptr = _STR + strlen(_STR);
2841 	 if (wlflag) {
2842             f1 = ((float)(llen) * iscale * oscale) / IN_CM_CONVERT;
2843 	    if (wlflag & 2) {
2844                f2 = ((float)(lwid) * iscale * oscale) / IN_CM_CONVERT;
2845 	       sprintf(sptr, " (%5.3f x %5.3f cm)", f1, f2);
2846 	    }
2847    	    else
2848    	       sprintf(sptr, " (length %5.3f cm)", f1);
2849 	 }
2850 	 break;
2851    }
2852    W1printf(_STR);
2853 }
2854 
2855 /*---------------------------------------------------*/
2856 /* Find nearest point of intersection of the cursor  */
2857 /* position to a wire and move there.		     */
2858 /*---------------------------------------------------*/
2859 
findwirex(XPoint * endpt1,XPoint * endpt2,XPoint * userpt,XPoint * newpos,float * rot)2860 void findwirex(XPoint *endpt1, XPoint *endpt2, XPoint *userpt,
2861 		XPoint *newpos, float *rot)
2862 {
2863    long xsq, ysq, zsq;
2864    float frac;
2865 
2866    xsq = sqwirelen(endpt1, endpt2);
2867    ysq = sqwirelen(endpt1, userpt);
2868    zsq = sqwirelen(endpt2, userpt);
2869    frac = 0.5 + (float)(ysq - zsq) / (float)(xsq << 1);
2870    if (frac > 1) frac = 1;
2871    else if (frac < 0) frac = 0;
2872    newpos->x = endpt1->x + (int)((endpt2->x - endpt1->x) * frac);
2873    newpos->y = endpt1->y + (int)((endpt2->y - endpt1->y) * frac);
2874 
2875    *rot = 180.0 + INVRFAC * atan2((double)(endpt1->x -
2876 	  endpt2->x), (double)(endpt1->y - endpt2->y));
2877 }
2878 
2879 /*----------------------------------------------------------------*/
2880 /* Find the closest point of attachment from the pointer position */
2881 /* to the "attachto" element.					  */
2882 /*----------------------------------------------------------------*/
2883 
findattach(XPoint * newpos,float * rot,XPoint * userpt)2884 void findattach(XPoint *newpos, float *rot, XPoint *userpt)
2885 {
2886    XPoint *endpt1, *endpt2;
2887    float frac;
2888    double tmpang;
2889    float locrot = 0.0;
2890 
2891    if (rot) locrot = *rot;
2892 
2893    /* find point of intersection and slope */
2894 
2895    if (SELECTTYPE(&areawin->attachto) == ARC) {
2896       arcptr aarc = SELTOARC(&areawin->attachto);
2897       float tmpdeg;
2898       tmpang = atan2((double)(userpt->y - aarc->position.y) * (double)
2899 		(abs(aarc->radius)), (double)(userpt->x - aarc->position.x) *
2900 		(double)aarc->yaxis);
2901 
2902       /* don't follow the arc beyond its endpoints */
2903 
2904       tmpdeg = (float)(tmpang * INVRFAC);
2905       if (tmpdeg < 0) tmpdeg += 360;
2906       if (((aarc->angle2 > 360) && (tmpdeg > aarc->angle2 - 360) &&
2907 		(tmpdeg < aarc->angle1)) ||
2908 	  	((aarc->angle1 < 0) && (tmpdeg > aarc->angle2) &&
2909 		(tmpdeg < aarc->angle1 + 360)) ||
2910 	  	((aarc->angle1 >= 0) && (aarc->angle2 <= 360) && ((tmpdeg
2911 		> aarc->angle2) || (tmpdeg < aarc->angle1)))) {
2912 	 float testd1 = aarc->angle1 - tmpdeg;
2913 	 float testd2 = tmpdeg - aarc->angle2;
2914 	 if (testd1 < 0) testd1 += 360;
2915 	 if (testd2 < 0) testd2 += 360;
2916 
2917 	 /* if arc is closed, attach to the line between the endpoints */
2918 
2919 	 if (!(aarc->style & UNCLOSED)) {
2920 	    XPoint end1, end2;
2921 	    tmpang = (double) aarc->angle1 / INVRFAC;
2922 	    end1.x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2923 	    end1.y = aarc->position.y + aarc->yaxis  * sin(tmpang);
2924 	    tmpang = (double) aarc->angle2 / INVRFAC;
2925 	    end2.x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2926 	    end2.y = aarc->position.y + aarc->yaxis  * sin(tmpang);
2927             findwirex(&end1, &end2, userpt, newpos, &locrot);
2928 	    if (rot) *rot = locrot;
2929 	    return;
2930 	 }
2931 	 else
2932 	    tmpang = (double)((testd1 < testd2) ? aarc->angle1 : aarc->angle2)
2933 			/ INVRFAC;
2934       }
2935 
2936       /* get position in user coordinates nearest to the intersect pt */
2937 
2938       newpos->x = aarc->position.x + abs(aarc->radius) * cos(tmpang);
2939       newpos->y = aarc->position.y + aarc->yaxis  * sin(tmpang);
2940 
2941       /* rotation of object is normal to the curve of the ellipse */
2942 
2943       if (rot) {
2944          *rot = 90.0 - INVRFAC * tmpang;
2945          if (*rot < 0.0) *rot += 360.0;
2946       }
2947    }
2948    else if (SELECTTYPE(&areawin->attachto) == SPLINE) {
2949        splineptr aspline = SELTOSPLINE(&areawin->attachto);
2950        frac = findsplinemin(aspline, userpt);
2951        findsplinepos(aspline, frac, newpos, &locrot);
2952        if (rot) *rot = locrot;
2953    }
2954    else if (SELECTTYPE(&areawin->attachto) == OBJINST) {
2955 
2956       objinstptr ainst = SELTOOBJINST(&areawin->attachto);
2957       objectptr aobj = ainst->thisobject;
2958       genericptr *ggen;
2959       long testdist, mindist = 1e8;
2960       XPoint mdpoint;
2961 
2962       /* In case instance has no pin labels, we will attach to	*/
2963       /* the instance's own origin.				*/
2964 
2965       mdpoint.x = mdpoint.y = 0;
2966       ReferencePosition(ainst, &mdpoint, newpos);
2967 
2968       /* Find the nearest pin label in the object instance and attach to it */
2969       for (ggen = aobj->plist; ggen < aobj->plist + aobj->parts; ggen++) {
2970 	 if (ELEMENTTYPE(*ggen) == LABEL) {
2971 	    labelptr alab = TOLABEL(ggen);
2972 	    if (alab->pin == LOCAL || alab->pin == GLOBAL) {
2973 	       ReferencePosition(ainst, &alab->position, &mdpoint);
2974 	       testdist = sqwirelen(&mdpoint, userpt);
2975 	       if (testdist < mindist) {
2976 		  mindist = testdist;
2977 		  *newpos = mdpoint;
2978 	       }
2979 	    }
2980 	 }
2981       }
2982    }
2983    else if (SELECTTYPE(&areawin->attachto) == LABEL) {
2984       /* Only one choice:  Attach to the label position */
2985       labelptr alabel = SELTOLABEL(&areawin->attachto);
2986       newpos->x = alabel->position.x;
2987       newpos->y = alabel->position.y;
2988    }
2989    else if (SELECTTYPE(&areawin->attachto) == POLYGON) {
2990        polyptr apoly = SELTOPOLY(&areawin->attachto);
2991        XPoint *testpt, *minpt, *nxtpt;
2992        long mindist = 1e8, testdist;
2993        minpt = nxtpt = apoly->points; 	/* so variables aren't uninitialized */
2994        for (testpt = apoly->points; testpt < apoly->points +
2995 		apoly->number - 1; testpt++) {
2996 	 testdist =  finddist(testpt, testpt + 1, userpt);
2997 	 if (testdist < mindist) {
2998 	    mindist = testdist;
2999 	    minpt = testpt;
3000 	    nxtpt = testpt + 1;
3001 	 }
3002       }
3003       if (!(apoly->style & UNCLOSED)) {
3004 	 testdist = finddist(testpt, apoly->points, userpt);
3005 	 if (testdist < mindist) {
3006 	    mindist = testdist;
3007 	    minpt = testpt;
3008 	    nxtpt = apoly->points;
3009 	 }
3010       }
3011       endpt1 = minpt;
3012       endpt2 = nxtpt;
3013       findwirex(endpt1, endpt2, userpt, newpos, &locrot);
3014       if (rot) *rot = locrot;
3015    }
3016 }
3017 
3018 /*--------------------------------------------*/
3019 /* Find closest point in a path to the cursor */
3020 /*--------------------------------------------*/
3021 
pathclosepoint(pathptr dragpath,XPoint * newpos)3022 XPoint *pathclosepoint(pathptr dragpath, XPoint *newpos)
3023 {
3024    XPoint *rpoint;
3025    genericptr *cpoint;
3026    short mpoint;
3027    int mdist = 1000000, tdist;
3028 
3029    for (cpoint = dragpath->plist; cpoint < dragpath->plist + dragpath->parts;
3030 	   cpoint++) {
3031       switch(ELEMENTTYPE(*cpoint)) {
3032 	 case ARC:
3033 	   tdist = wirelength(&(TOARC(cpoint)->position), newpos);
3034 	    if (tdist < mdist) {
3035 	       mdist = tdist;
3036 	       rpoint = &(TOARC(cpoint)->position);
3037 	    }
3038 	    break;
3039 	 case POLYGON:
3040 	    mpoint = closepoint(TOPOLY(cpoint), newpos);
3041 	    tdist = wirelength(TOPOLY(cpoint)->points + mpoint, newpos);
3042 	    if (tdist < mdist) {
3043 	       mdist = tdist;
3044 	       rpoint = TOPOLY(cpoint)->points + mpoint;
3045 	    }
3046 	    break;
3047 	 case SPLINE:
3048 	    tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[0]), newpos);
3049 	    if (tdist < mdist) {
3050 	       mdist = tdist;
3051 	       rpoint = &(TOSPLINE(cpoint)->ctrl[0]);
3052 	    }
3053 	    tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[3]), newpos);
3054 	    if (tdist < mdist) {
3055 	       mdist = tdist;
3056 	       rpoint = &(TOSPLINE(cpoint)->ctrl[3]);
3057 	    }
3058 	    break;
3059       }
3060    }
3061    return rpoint;
3062 }
3063 
3064 /*-------------------------------------------*/
3065 /* Drag a selected element around the screen */
3066 /*-------------------------------------------*/
3067 
drag(int x,int y)3068 void drag(int x, int y)
3069 {
3070    XEvent again;
3071    Boolean eventcheck = False;
3072    XPoint userpt;
3073    short deltax, deltay;
3074    int locx, locy;
3075 
3076    locx = x;
3077    locy = y;
3078 
3079    /* flush out multiple pointermotion events from the event queue */
3080    /* use only the last motion event */
3081    while (XCheckWindowEvent(dpy, areawin->window, PointerMotionMask |
3082 	Button1MotionMask, &again) == True) eventcheck = True;
3083    if (eventcheck) {
3084       XButtonEvent *event = (XButtonEvent *)(&again);
3085       locx = (int)event->x;
3086       locy = (int)event->y;
3087    }
3088 
3089    /* Determine if this event is supposed to be handled by 	*/
3090    /* trackselarea(), or whether we should not be here at all	*/
3091    /* (button press and mouse movement in an unsupported mode)	*/
3092 
3093    if (eventmode == SELAREA_MODE) {
3094       trackselarea();
3095       return;
3096    }
3097    else if (eventmode == RESCALE_MODE) {
3098       trackrescale();
3099       return;
3100    }
3101    else if (eventmode == PAN_MODE) {
3102       trackpan(locx, locy);
3103       return;
3104    }
3105    else if (eventmode != CATMOVE_MODE && eventmode != MOVE_MODE
3106 		&& eventmode != COPY_MODE)
3107       return;
3108 
3109    snap(locx, locy, &userpt);
3110    deltax = userpt.x - areawin->save.x;
3111    deltay = userpt.y - areawin->save.y;
3112    if (deltax == 0 && deltay == 0) return;
3113 
3114    areawin->save.x = userpt.x;
3115    areawin->save.y = userpt.y;
3116 
3117    /* set up the graphics state for moving a selected object */
3118 
3119    XTopSetForeground(SELECTCOLOR);
3120 
3121    placeselects(deltax, deltay, &userpt);
3122 
3123    /* restore graphics state */
3124 
3125    SetForeground(dpy, areawin->gc, areawin->gccolor);
3126 
3127    /* print the position and other useful measurements */
3128 
3129    printpos(userpt.x, userpt.y);
3130 }
3131 
3132 /*------------------------------------------------------*/
3133 /* Wrapper for drag() for xlib callback compatibility.	*/
3134 /*------------------------------------------------------*/
3135 
xlib_drag(xcWidget w,caddr_t clientdata,XEvent * event)3136 void xlib_drag(xcWidget w, caddr_t clientdata, XEvent *event)
3137 {
3138    XButtonEvent *bevent = (XButtonEvent *)event;
3139    UNUSED(w); UNUSED(clientdata);
3140 
3141    drag(bevent->x, bevent->y);
3142    if (areawin->redraw_needed)
3143      drawarea(NULL, NULL, NULL);
3144 }
3145 
3146 /*----------------------------------------------*/
3147 /* Rotate an element of a path			*/
3148 /*----------------------------------------------*/
3149 
elemrotate(genericptr * genobj,float direction,XPoint * position)3150 void elemrotate(genericptr *genobj, float direction, XPoint *position)
3151 {
3152    XPoint negpt, *newpts = (XPoint *)NULL;
3153 
3154    negpt.x = -position->x;
3155    negpt.y = -position->y;
3156 
3157    switch(ELEMENTTYPE(*genobj)) {
3158       case(ARC):{
3159 	 arcptr rotatearc = TOARC(genobj);
3160 	 rotatearc->angle1 -= direction;
3161 	 rotatearc->angle2 -= direction;
3162          if (rotatearc->angle1 >= 360) {
3163             rotatearc->angle1 -= 360;
3164             rotatearc->angle2 -= 360;
3165          }
3166          else if (rotatearc->angle2 <= 0) {
3167             rotatearc->angle1 += 360;
3168             rotatearc->angle2 += 360;
3169          }
3170 	 newpts = (XPoint *)malloc(sizeof(XPoint));
3171 	 UTransformPoints(&rotatearc->position, newpts, 1, negpt, 1.0, 0);
3172 	 UTransformPoints(newpts, &rotatearc->position, 1, *position,
3173 			1.0, direction);
3174 	 calcarc(rotatearc);
3175 	 }break;
3176 
3177       case(SPLINE):{
3178 	 splineptr rotatespline = TOSPLINE(genobj);
3179 	 newpts = (XPoint *)malloc(4 * sizeof(XPoint));
3180 	 UTransformPoints(rotatespline->ctrl, newpts, 4, negpt, 1.0, 0);
3181 	 UTransformPoints(newpts, rotatespline->ctrl, 4, *position,
3182 			1.0, direction);
3183 	 calcspline(rotatespline);
3184 	 }break;
3185 
3186       case(POLYGON):{
3187 	 polyptr rotatepoly = TOPOLY(genobj);
3188 	 newpts = (XPoint *)malloc(rotatepoly->number * sizeof(XPoint));
3189 	 UTransformPoints(rotatepoly->points, newpts, rotatepoly->number,
3190 		   negpt, 1.0, 0);
3191 	 UTransformPoints(newpts, rotatepoly->points, rotatepoly->number,
3192 		   *position, 1.0, direction);
3193 	 }break;
3194    }
3195    if (newpts) free(newpts);
3196 }
3197 
3198 /*------------------------------------------------------*/
3199 /* Rotate an element or group of elements		*/
3200 /* Objects and labels, if selected singly, rotate	*/
3201 /* about their position point.  All other elements,	*/
3202 /* and groups, rotate about the cursor point.		*/
3203 /*------------------------------------------------------*/
3204 
elementrotate(float direction,XPoint * position)3205 void elementrotate(float direction, XPoint *position)
3206 {
3207   short    *selectobj; /* , ld; (jdk) */
3208    Boolean  single = False;
3209    Boolean  need_refresh = False;
3210    Boolean  preselected;
3211    XPoint   newpt, negpt;
3212 
3213    preselected = (areawin->selects > 0) ? TRUE : FALSE;
3214    if (!checkselect(ALL_TYPES)) return;
3215    if (areawin->selects == 1) single = True;
3216 
3217    negpt.x = -position->x;
3218    negpt.y = -position->y;
3219 
3220    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3221 	+ areawin->selects; selectobj++) {
3222 
3223       /* erase the element */
3224       if (!need_refresh) {
3225          SetForeground(dpy, areawin->gc, BACKGROUND);
3226          easydraw(*selectobj, DOFORALL);
3227       }
3228 
3229       switch(SELECTTYPE(selectobj)) {
3230 
3231 	 case(OBJINST):{
3232             objinstptr rotateobj = SELTOOBJINST(selectobj);
3233 
3234 	    if (is_library(topobject) >= 0 && !is_virtual(rotateobj)) break;
3235             rotateobj->rotation += direction;
3236 	    while (rotateobj->rotation >= 360) rotateobj->rotation -= 360;
3237 	    while (rotateobj->rotation <= 0) rotateobj->rotation += 360;
3238 	    if (!single) {
3239 	       UTransformPoints(&rotateobj->position, &newpt, 1, negpt, 1.0, 0);
3240 	       UTransformPoints(&newpt, &rotateobj->position, 1, *position,
3241 			1.0, direction);
3242 	    }
3243 	    }break;
3244 
3245 	 case(LABEL):{
3246             labelptr rotatetext = SELTOLABEL(selectobj);
3247 
3248             rotatetext->rotation += direction;
3249 	    while (rotatetext->rotation >= 360) rotatetext->rotation -= 360;
3250 	    while (rotatetext->rotation <= 0) rotatetext->rotation += 360;
3251 	    if (!single) {
3252 	       UTransformPoints(&rotatetext->position, &newpt, 1, negpt, 1.0, 0);
3253 	       UTransformPoints(&newpt, &rotatetext->position, 1, *position,
3254 			1.0, direction);
3255 	    }
3256 	    }break;
3257 
3258 	 case(GRAPHIC):{
3259             graphicptr rotateg = SELTOGRAPHIC(selectobj);
3260 
3261             rotateg->rotation += direction;
3262 	    while (rotateg->rotation >= 360) rotateg->rotation -= 360;
3263 	    while (rotateg->rotation <= 0) rotateg->rotation += 360;
3264 #ifndef HAVE_CAIRO
3265 	    rotateg->valid = FALSE;
3266 #endif /* !HAVE_CAIRO */
3267 	    if (!single) {
3268 	       UTransformPoints(&rotateg->position, &newpt, 1, negpt, 1.0, 0);
3269 	       UTransformPoints(&newpt, &rotateg->position, 1, *position,
3270 			1.0, direction);
3271 	    }
3272 	    need_refresh = True;
3273 	    }break;
3274 
3275 	 case POLYGON: case ARC: case SPLINE:{
3276 	    genericptr *genpart = topobject->plist + *selectobj;
3277 	    register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3278 			*genpart);
3279 	    elemrotate(genpart, direction, position);
3280 	    }break;
3281 
3282 	 case PATH:{
3283 	    genericptr *genpart;
3284 	    pathptr rotatepath = SELTOPATH(selectobj);
3285 
3286 	    register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3287 			rotatepath);
3288 	    for (genpart = rotatepath->plist; genpart < rotatepath->plist
3289 		  + rotatepath->parts; genpart++)
3290 	       elemrotate(genpart, direction, position);
3291 	    }break;
3292       }
3293 
3294       /* redisplay the element */
3295       if (preselected || ((eventmode != NORMAL_MODE) && !need_refresh)) {
3296 	 SetForeground(dpy, areawin->gc, SELECTCOLOR);
3297 	 easydraw(*selectobj, DOFORALL);
3298       }
3299    }
3300 
3301    /* This takes care of all selected instances and labels in one go,	*/
3302    /* because we only need to know the origin and amount of rotation.	*/
3303 
3304    if (eventmode != COPY_MODE)
3305       register_for_undo(XCF_Rotate, UNDO_MORE, areawin->topinstance,
3306 		(eventmode == MOVE_MODE) ? &areawin->origin : position,
3307 		(double)direction);
3308 
3309    /* New rule (6/15/07) to be applied generally:  If objects were	*/
3310    /* selected prior to calling elementrotate() and similar functions,	*/
3311    /* leave them selected upon exit.  Otherwise, deselect them.		*/
3312 
3313    if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
3314       if (!preselected)
3315          unselect_all();
3316 
3317    if (eventmode == CATALOG_MODE) {
3318       int libnum;
3319       if ((libnum = is_library(topobject)) >= 0) {
3320 	 composelib(libnum + LIBRARY);
3321 	 need_refresh = TRUE;
3322       }
3323    }
3324    else {
3325       pwriteback(areawin->topinstance);
3326       calcbbox(areawin->topinstance);
3327    }
3328 
3329    if (need_refresh) drawarea(NULL, NULL, NULL);
3330 }
3331 
3332 /*----------------------------------------------*/
3333 /* Rescale the current edit element to the	*/
3334 /* dimensions of the rescale box.		*/
3335 /*----------------------------------------------*/
3336 
elementrescale(float newscale)3337 void elementrescale(float newscale)
3338 {
3339    short *selectobj;
3340    labelptr sclab;
3341    objinstptr scinst;
3342    graphicptr scgraph;
3343    float oldsize;
3344 
3345    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3346 		+ areawin->selects; selectobj++) {
3347       switch (SELECTTYPE(selectobj)) {
3348 	 case LABEL:
3349 	    sclab = SELTOLABEL(selectobj);
3350 	    oldsize = sclab->scale;
3351 	    sclab->scale = newscale;
3352 	    break;
3353 	 case OBJINST:
3354 	    scinst = SELTOOBJINST(selectobj);
3355 	    oldsize = scinst->scale;
3356 	    scinst->scale = newscale;
3357 	    break;
3358 	 case GRAPHIC:
3359 	    scgraph = SELTOGRAPHIC(selectobj);
3360 	    oldsize = scgraph->scale;
3361 	    scgraph->scale = newscale;
3362 	    break;
3363       }
3364       register_for_undo(XCF_Rescale, UNDO_MORE, areawin->topinstance,
3365              SELTOGENERIC(selectobj), (double)oldsize);
3366    }
3367    calcbbox(areawin->topinstance);
3368 }
3369 
3370 /*-------------------------------------------------*/
3371 /* Edit an element in an element-dependent fashion */
3372 /*-------------------------------------------------*/
3373 
edit(int x,int y)3374 void edit(int x, int y)
3375 {
3376    short *selectobj;
3377 
3378    if (areawin->selects == 0) {
3379       Boolean saveredraw = areawin->redraw_needed;
3380       selectobj = select_element(ALL_TYPES);
3381       areawin->redraw_needed = saveredraw;
3382    }
3383    else
3384       selectobj = areawin->selectlist;
3385    if (areawin->selects == 0)
3386       return;
3387    else if (areawin->selects != 1) {	/* Multiple object edit */
3388       int selnum;
3389       short *selectlist, selrefno;
3390       Boolean save_redraw = areawin->redraw_needed;
3391 
3392       /* Find the closest part to use as a reference */
3393       selnum = areawin->selects;
3394       selectlist = areawin->selectlist;
3395       areawin->selects = 0;
3396       areawin->selectlist = NULL;
3397       selectobj = select_element(ALL_TYPES);
3398       if (selectobj != NULL)
3399          selrefno = *selectobj;
3400       else
3401 	 selrefno = -1;
3402       freeselects();
3403       areawin->selects = selnum;
3404       areawin->selectlist = selectlist;
3405       areawin->redraw_needed = save_redraw;
3406       for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
3407 		+ areawin->selects; selectobj++) {
3408 	 if (*selectobj == selrefno) break;
3409       }
3410       if (selectobj == areawin->selectlist + areawin->selects) {
3411          Wprintf("Put cursor close to the reference element.");
3412 	 return;
3413       }
3414 
3415       /* Shuffle the reference element to the beginning of the select list */
3416       *selectobj = *(areawin->selectlist);
3417       *(areawin->selectlist) = selrefno;
3418       selectobj = areawin->selectlist;
3419    }
3420 
3421    switch(SELECTTYPE(selectobj)) {
3422        case LABEL: {
3423 	 labelptr *lastlabel = (labelptr *)EDITPART;
3424 	 short curfont;
3425 	 XPoint tmppt;
3426 	 TextExtents tmpext;
3427 
3428 	 /* save the old string, including parameters */
3429 	 register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3430 			*lastlabel);
3431 
3432 	 /* fill any NULL instance parameters with the default value */
3433 	 copyparams(areawin->topinstance, areawin->topinstance);
3434 
3435 	 /* place text cursor line at point nearest the cursor */
3436 	 /* unless textend is set. . .			       */
3437 
3438 	 if (areawin->textend == 0) {
3439 	    TextLinesInfo tlinfo;
3440 	    tlinfo.dostop = 0;
3441 	    tlinfo.tbreak = NULL;
3442 	    tlinfo.padding = NULL;
3443 
3444 	    window_to_user(x, y, &areawin->save);
3445 	    InvTransformPoints(&areawin->save, &tmppt, 1, (*lastlabel)->position,
3446 		(*lastlabel)->scale, (*lastlabel)->rotation);
3447             tmpext = ULength(*lastlabel, areawin->topinstance, &tlinfo);
3448 	    tmppt.x += ((*lastlabel)->anchor & NOTLEFT ?
3449 		((*lastlabel)->anchor & RIGHT ? tmpext.maxwidth
3450 		: tmpext.maxwidth >> 1) : 0);
3451 	    tmppt.y += ((*lastlabel)->anchor & NOTBOTTOM ?
3452 		((*lastlabel)->anchor & TOP ? tmpext.ascent :
3453 		(tmpext.ascent + tmpext.base) >> 1) : tmpext.base);
3454 	    if ((*lastlabel)->pin)
3455 	       pinadjust((*lastlabel)->anchor, &tmppt.x, NULL, -1);
3456 
3457 	    /* Where tbreak is passed to ULength, the character position */
3458 	    /* is returned in the TextLinesInfo dostop field.		 */
3459 	    tlinfo.tbreak = &tmppt;
3460             tmpext = ULength(*lastlabel, areawin->topinstance, &tlinfo);
3461 	    areawin->textpos = tlinfo.dostop;
3462 
3463 	    if (tlinfo.padding != NULL) free(tlinfo.padding);
3464 	 }
3465 
3466 	 /* find current font */
3467 
3468 	 curfont = findcurfont(areawin->textpos, (*lastlabel)->string,
3469 			areawin->topinstance);
3470 
3471 	 /* change menu buttons accordingly */
3472 
3473 	 setfontmarks(curfont, (*lastlabel)->anchor);
3474 
3475 	 if (eventmode == CATALOG_MODE) {
3476 	    /* CATTEXT_MODE may show an otherwise hidden library namespace */
3477 	    undrawtext(*lastlabel);
3478 	    eventmode = CATTEXT_MODE;
3479 	    redrawtext(*lastlabel);
3480             areawin->redraw_needed = False; /* ignore prev. redraw requests */
3481 	    text_mode_draw(xcDRAW_INIT, *lastlabel);
3482 	 }
3483 	 else {
3484 	    eventmode = ETEXT_MODE;
3485 	    text_mode_draw(xcDRAW_INIT, *lastlabel);
3486 	 }
3487 
3488          XDefineCursor(dpy, areawin->window, TEXTPTR);
3489 
3490 	 /* write the text at the bottom */
3491 
3492 	 charreport(*lastlabel);
3493 
3494       } break;
3495 
3496       case POLYGON: case ARC: case SPLINE: case PATH:
3497 	 window_to_user(x, y, &areawin->save);
3498 	 pathedit(*(EDITPART));
3499 	 break;
3500 
3501       case OBJINST: case GRAPHIC:
3502 	 if (areawin->selects == 1)
3503 	    unselect_all();
3504 	 return;
3505    }
3506    XDefineCursor (dpy, areawin->window, EDCURSOR);
3507 }
3508 
3509 /*----------------------------------------------------------------------*/
3510 /* edit() routine for path-type elements (polygons, splines, arcs, and	*/
3511 /* paths)								*/
3512 /*----------------------------------------------------------------------*/
3513 
pathedit(genericptr editpart)3514 void pathedit(genericptr editpart)
3515 {
3516    splineptr lastspline = NULL;
3517    pathptr lastpath;
3518    polyptr lastpoly = NULL;
3519    arcptr lastarc;
3520    XPoint *savept;
3521    short *eselect;
3522    int cycle;
3523    Boolean havecycle;
3524 
3525    /* Find and set constrained edit points on all elements.  Register	*/
3526    /* each element with the undo mechanism.				*/
3527 
3528    for (eselect = areawin->selectlist; eselect < areawin->selectlist +
3529 			areawin->selects; eselect++) {
3530       switch (SELECTTYPE(eselect)) {
3531 	 case POLYGON:
3532 	    findconstrained(SELTOPOLY(eselect));
3533 	    /* fall through */
3534 	 default:
3535             register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3536 			SELTOGENERIC(eselect));
3537 	    break;
3538       }
3539    }
3540 
3541    switch(ELEMENTTYPE(editpart)) {
3542       case PATH: {
3543 	 genericptr *ggen, *savegen = NULL;
3544 	 int mincycle, dist, mindist = 1e6;
3545 
3546          lastpath = (pathptr)editpart;
3547          havecycle = (checkcycle(editpart, 0) >= 0) ? True : False;
3548 
3549 	 /* determine which point of the path is closest to the cursor */
3550 	 for (ggen = lastpath->plist; ggen < lastpath->plist + lastpath->parts;
3551 		ggen++) {
3552 	    switch (ELEMENTTYPE(*ggen)) {
3553 	       case POLYGON:
3554 		  lastpoly = TOPOLY(ggen);
3555 		  cycle = closepoint(lastpoly, &areawin->save);
3556 		  dist = wirelength(lastpoly->points + cycle, &areawin->save);
3557 		  break;
3558 	       case SPLINE:
3559 		  lastspline = TOSPLINE(ggen);
3560 		  cycle =  (wirelength(&lastspline->ctrl[0],
3561 			&areawin->save) < wirelength(&lastspline->ctrl[3],
3562 			&areawin->save)) ? 0 : 3;
3563 		  dist = wirelength(&lastspline->ctrl[cycle], &areawin->save);
3564 		  break;
3565 	    }
3566 	    if (dist < mindist) {
3567 	       mindist = dist;
3568 	       mincycle = cycle;
3569 	       savegen = ggen;
3570 	    }
3571 	 }
3572 	 if (savegen == NULL) return;	/* something went terribly wrong */
3573 	 switch (ELEMENTTYPE(*savegen)) {
3574 	    case POLYGON:
3575 	       lastpoly = TOPOLY(savegen);
3576 	       addcycle(savegen, mincycle, 0);
3577 	       savept = lastpoly->points + mincycle;
3578 	       makerefcycle(lastpoly->cycle, mincycle);
3579 	       findconstrained(lastpoly);
3580 	       break;
3581 	    case SPLINE:
3582 	       lastspline = TOSPLINE(savegen);
3583 	       addcycle(savegen, mincycle, 0);
3584 	       savept = &lastspline->ctrl[mincycle];
3585 	       makerefcycle(lastspline->cycle, mincycle);
3586 	       break;
3587 	 }
3588 	 updatepath(lastpath);
3589 	 if (!havecycle)
3590             register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3591 			(genericptr)lastpath);
3592 	 patheditpush(lastpath);
3593 	 areawin->origin = areawin->save;
3594 	 checkwarp(savept);
3595 
3596 	 path_mode_draw(xcDRAW_INIT, lastpath);
3597 
3598 	 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3599 	    (xcEventHandler)trackelement, NULL);
3600 	 eventmode = EPATH_MODE;
3601          printpos(savept->x, savept->y);
3602 
3603       } break;
3604       case POLYGON: {
3605 
3606 	 lastpoly = (polyptr)editpart;
3607 
3608 	 /* Determine which point of polygon is closest to cursor */
3609 	 cycle = closepoint(lastpoly, &areawin->save);
3610 	 havecycle = (lastpoly->cycle == NULL) ? False : True;
3611 	 addcycle(&editpart, cycle, 0);
3612          savept = lastpoly->points + cycle;
3613 	 makerefcycle(lastpoly->cycle, cycle);
3614 	 if (!havecycle) {
3615 	    findconstrained(lastpoly);
3616             register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3617 			(genericptr)lastpoly);
3618  	 }
3619 
3620 	 /* Push onto the editstack */
3621 	 polyeditpush(lastpoly);
3622 
3623 	 /* remember our postion for pointer restore */
3624 	 areawin->origin = areawin->save;
3625 
3626 	 checkwarp(savept);
3627 
3628 	 poly_mode_draw(xcDRAW_INIT, lastpoly);
3629 
3630 	 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3631 	    (xcEventHandler)trackelement, NULL);
3632 	 eventmode = EPOLY_MODE;
3633 	 printeditbindings();
3634          printpos(savept->x, savept->y);
3635       } break;
3636       case SPLINE: {
3637 	 XPoint *curpt;
3638 
3639 	 lastspline = (splineptr)editpart;
3640 
3641 	 /* find which point is closest to the cursor */
3642 
3643          cycle =  (wirelength(&lastspline->ctrl[0],
3644 	      &areawin->save) < wirelength(&lastspline->ctrl[3],
3645 	      &areawin->save)) ? 0 : 3;
3646 	 havecycle = (lastspline->cycle == NULL) ? False : True;
3647 	 addcycle(&editpart, cycle, 0);
3648 	 makerefcycle(lastspline->cycle, cycle);
3649 	 curpt = &lastspline->ctrl[cycle];
3650 	 if (!havecycle)
3651             register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance,
3652 			(genericptr)lastspline);
3653 
3654 	 /* Push onto the editstack */
3655 	 splineeditpush(lastspline);
3656 
3657 	 /* remember our postion for pointer restore */
3658 	 areawin->origin = areawin->save;
3659 
3660          checkwarp(curpt);
3661 
3662 	 spline_mode_draw(xcDRAW_INIT, lastspline);
3663 	 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3664                (xcEventHandler)trackelement, NULL);
3665 	 eventmode = ESPLINE_MODE;
3666       } break;
3667       case ARC: {
3668 	 XPoint curpt;
3669 	 float tmpratio, tlen;
3670 
3671 	 lastarc = (arcptr)editpart;
3672 
3673 	 /* find a part of the arc close to the pointer */
3674 
3675 	 tlen = (float)wirelength(&areawin->save, &(lastarc->position));
3676 	 tmpratio = (float)(abs(lastarc->radius)) / tlen;
3677 	 curpt.x = lastarc->position.x + tmpratio * (areawin->save.x
3678 	     - lastarc->position.x);
3679 	 tmpratio = (float)lastarc->yaxis / tlen;
3680 	 curpt.y = lastarc->position.y + tmpratio * (areawin->save.y
3681 	     - lastarc->position.y);
3682 	 addcycle(&editpart, 0, 0);
3683 	 saveratio = (double)(lastarc->yaxis) / (double)(abs(lastarc->radius));
3684 
3685 	 /* Push onto the editstack */
3686 	 arceditpush(lastarc);
3687 	 areawin->origin = areawin->save;
3688 
3689 	 checkwarp(&curpt);
3690 
3691 	 areawin->save.x = curpt.x;	/* for redrawing dotted edit line */
3692 	 areawin->save.y = curpt.y;
3693 	 arc_mode_draw(xcDRAW_INIT, lastarc);
3694 	 xcAddEventHandler(areawin->area, PointerMotionMask, False,
3695 	    (xcEventHandler)trackarc, NULL);
3696 	 eventmode = EARC_MODE;
3697          printpos(curpt.x, curpt.y);
3698       } break;
3699    }
3700 }
3701 
3702 /*------------------------------------------------------*/
3703 /* Raise an element to the top of the list	   	*/
3704 /*------------------------------------------------------*/
3705 
xc_top(short * selectno,short * orderlist)3706 void xc_top(short *selectno, short *orderlist)
3707 {
3708    short i;
3709    genericptr *raiseobj, *genobj, temp;
3710 
3711    raiseobj = topobject->plist + *selectno;
3712    temp = *raiseobj;
3713    i = *selectno;
3714    for (genobj = topobject->plist + *selectno; genobj <
3715 		topobject->plist + topobject->parts - 1; genobj++) {
3716       *genobj = *(genobj + 1);
3717       *(orderlist + i) = *(orderlist + i + 1);
3718       i++;
3719    }
3720    *(topobject->plist + topobject->parts - 1) = temp;
3721    *(orderlist + topobject->parts - 1) = *selectno;
3722    *selectno = topobject->parts - 1;
3723 }
3724 
3725 /*------------------------------------------------------*/
3726 /* Lower an element to the bottom of the list	   	*/
3727 /*------------------------------------------------------*/
3728 
xc_bottom(short * selectno,short * orderlist)3729 void xc_bottom(short *selectno, short *orderlist)
3730 {
3731    short i;
3732    genericptr *lowerobj, *genobj, temp;
3733 
3734    lowerobj = topobject->plist + *selectno;
3735    temp = *lowerobj;
3736    i = *selectno;
3737    for (genobj = topobject->plist + *selectno;
3738 		genobj > topobject->plist; genobj--) {
3739       *genobj = *(genobj - 1);
3740       *(orderlist + i) = *(orderlist + i - 1);
3741       i--;
3742    }
3743    *genobj = temp;
3744    *orderlist = *selectno;
3745    *selectno = 0;
3746 }
3747 
3748 /*--------------------------------------------------------------*/
3749 /* Raise all selected elements by one position in the list	*/
3750 /*--------------------------------------------------------------*/
3751 
xc_raise()3752 void xc_raise()
3753 {
3754    short *sel, topsel, maxsel, *topidx, limit, *orderlist, i;
3755    genericptr *raiseobj, temp;
3756 
3757    orderlist = (short *)malloc(topobject->parts * sizeof(short));
3758    for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3759 
3760    /* Find topmost element in the select list */
3761    maxsel = -1;
3762    for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3763 		sel++) {
3764       if (*sel > maxsel) {
3765 	 maxsel = *sel;
3766 	 topidx = sel;
3767       }
3768    }
3769    if (maxsel == -1) return;	/* Error condition */
3770 
3771    topsel = maxsel;
3772    limit = topobject->parts - 1;
3773    while (1) {
3774 
3775       /* Exchange the topmost element with the one above it */
3776       if (topsel < limit) {
3777          raiseobj = topobject->plist + topsel;
3778          temp = *raiseobj;
3779          *raiseobj = *(raiseobj + 1);
3780          *(raiseobj + 1) = temp;
3781 	 (*topidx)++;
3782 	 i = *(orderlist + topsel);
3783 	 *(orderlist + topsel) = *(orderlist + topsel + 1);
3784 	 *(orderlist + topsel + 1) = i;
3785       }
3786       else
3787 	 limit = topsel - 1;
3788 
3789       /* Find next topmost element */
3790       topsel = -1;
3791       for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3792 		sel++) {
3793 	 if (*sel < maxsel) {
3794 	    if (*sel > topsel) {
3795 	       topsel = *sel;
3796 	       topidx = sel;
3797 	    }
3798 	 }
3799       }
3800       if (topsel == -1) break;	/* No more elements to raise */
3801       maxsel = topsel;
3802    }
3803    register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist,
3804 		topobject->parts);
3805 }
3806 
3807 /*--------------------------------------------------------------*/
3808 /* Lower all selected elements by one position in the list	*/
3809 /*--------------------------------------------------------------*/
3810 
xc_lower()3811 void xc_lower()
3812 {
3813    short *sel, botsel, minsel, *botidx, limit, *orderlist, i;
3814    genericptr *lowerobj, temp;
3815 
3816    orderlist = (short *)malloc(topobject->parts * sizeof(short));
3817    for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3818 
3819    /* Find bottommost element in the select list */
3820    minsel = topobject->parts;
3821    for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3822 		sel++) {
3823       if (*sel < minsel) {
3824 	 minsel = *sel;
3825 	 botidx = sel;
3826       }
3827    }
3828    if (minsel == topobject->parts) return;	/* Error condition */
3829 
3830    botsel = minsel;
3831    limit = 0;
3832    while (1) {
3833 
3834       /* Exchange the topmost element with the one below it */
3835       if (botsel > limit) {
3836          lowerobj = topobject->plist + botsel;
3837          temp = *lowerobj;
3838          *lowerobj = *(lowerobj - 1);
3839          *(lowerobj - 1) = temp;
3840 	 (*botidx)--;
3841 	 i = *(orderlist + botsel);
3842 	 *(orderlist + botsel) = *(orderlist + botsel - 1);
3843 	 *(orderlist + botsel - 1) = i;
3844       }
3845       else
3846 	 limit = botsel + 1;
3847 
3848       /* Find next topmost element */
3849       botsel = topobject->parts;
3850       for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects;
3851 		sel++) {
3852 	 if (*sel > minsel) {
3853 	    if (*sel < botsel) {
3854 	       botsel = *sel;
3855 	       botidx = sel;
3856 	    }
3857 	 }
3858       }
3859       if (botsel == topobject->parts) break;	/* No more elements to raise */
3860       minsel = botsel;
3861    }
3862    register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist,
3863 		topobject->parts);
3864 }
3865 
3866 /*------------------------------------------------------*/
3867 /* Generate a virtual copy of an object instance in the	*/
3868 /* user library.  This is like the library virtual copy	*/
3869 /* except that it allows the user to generate a library	*/
3870 /* copy of an existing instance, without having to make	*/
3871 /* a copy of the master library instance and edit it.	*/
3872 /* copyvirtual() also allows the library virtual	*/
3873 /* instance to take on a specific rotation or flip	*/
3874 /* value, which cannot be done with the library virtual	*/
3875 /* copy function.					*/
3876 /*------------------------------------------------------*/
3877 
copyvirtual()3878 void copyvirtual()
3879 {
3880    short *selectno, created = 0;
3881    objinstptr vcpobj, libinst;
3882 
3883    for (selectno = areawin->selectlist; selectno < areawin->selectlist +
3884 		areawin->selects; selectno++) {
3885       if (SELECTTYPE(selectno) == OBJINST) {
3886 	 vcpobj = SELTOOBJINST(selectno);
3887 	 libinst = addtoinstlist(USERLIB - LIBRARY, vcpobj->thisobject, TRUE);
3888 	 instcopy(libinst, vcpobj);
3889 	 created++;
3890       }
3891    }
3892    if (created == 0) {
3893       Wprintf("No object instances selected for virtual copy!");
3894    }
3895    else {
3896       unselect_all();
3897       composelib(USERLIB);
3898    }
3899 }
3900 
3901 /*------------------------------------------------------*/
3902 /* Exchange the list position (drawing order) of two	*/
3903 /* elements, or move the position of one element to the	*/
3904 /* top or bottom.					*/
3905 /*------------------------------------------------------*/
3906 
exchange()3907 void exchange()
3908 {
3909    short *selectno, *orderlist, i;
3910    genericptr *exchobj, *exchobj2, temp;
3911    Boolean preselected;
3912 
3913    preselected = (areawin->selects > 0) ? TRUE : FALSE;
3914    if (!checkselect(ALL_TYPES)) {
3915       Wprintf("Select 1 or 2 objects");
3916       return;
3917    }
3918 
3919    selectno = areawin->selectlist;
3920    orderlist = (short *)malloc(topobject->parts * sizeof(short));
3921    for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i;
3922 
3923    if (areawin->selects == 1) {  /* lower if on top; raise otherwise */
3924       if (*selectno == topobject->parts - 1)
3925 	 xc_bottom(selectno, orderlist);
3926       else
3927 	 xc_top(selectno, orderlist);
3928    }
3929    else {  /* exchange the two objects */
3930       exchobj = topobject->plist + *selectno;
3931       exchobj2 = topobject->plist + *(selectno + 1);
3932 
3933       temp = *exchobj;
3934       *exchobj = *exchobj2;
3935       *exchobj2 = temp;
3936 
3937       i = *(orderlist + *selectno);
3938       *(orderlist + *selectno) = *(orderlist + *(selectno + 1));
3939       *(orderlist + *(selectno + 1)) = i;
3940    }
3941    register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance,
3942 		orderlist, topobject->parts);
3943 
3944    incr_changes(topobject);
3945    if (!preselected)
3946       clearselects();
3947    drawarea(NULL, NULL, NULL);
3948 }
3949 
3950 /*--------------------------------------------------------*/
3951 /* Flip an element horizontally (POLYGON, ARC, or SPLINE) */
3952 /*--------------------------------------------------------*/
3953 
elhflip(genericptr * genobj,short x)3954 void elhflip(genericptr *genobj, short x)
3955 {
3956    switch(ELEMENTTYPE(*genobj)) {
3957       case POLYGON:{
3958 	 polyptr flippoly = TOPOLY(genobj);
3959 	 pointlist ppoint;
3960 	 for (ppoint = flippoly->points; ppoint < flippoly->points +
3961 	       flippoly->number; ppoint++)
3962 	    ppoint->x = (x << 1) - ppoint->x;
3963 	 }break;
3964 
3965       case ARC:{
3966 	 arcptr fliparc = TOARC(genobj);
3967 	 float tmpang = 180 - fliparc->angle1;
3968 	 fliparc->angle1 = 180 - fliparc->angle2;
3969 	 fliparc->angle2 = tmpang;
3970 	 if (fliparc->angle2 < 0) {
3971 	    fliparc->angle1 += 360;
3972 	    fliparc->angle2 += 360;
3973 	 }
3974 	 fliparc->radius = -fliparc->radius;
3975 	 fliparc->position.x = (x << 1) - fliparc->position.x;
3976 	 calcarc(fliparc);
3977 	 }break;
3978 
3979       case SPLINE:{
3980 	 splineptr flipspline = TOSPLINE(genobj);
3981 	 int i;
3982 	 for (i = 0; i < 4; i++)
3983 	    flipspline->ctrl[i].x = (x << 1) - flipspline->ctrl[i].x;
3984 	 calcspline(flipspline);
3985 	 }break;
3986    }
3987 }
3988 
3989 /*--------------------------------------------------------*/
3990 /* Flip an element vertically (POLYGON, ARC, or SPLINE)   */
3991 /*--------------------------------------------------------*/
3992 
elvflip(genericptr * genobj,short y)3993 void elvflip(genericptr *genobj, short y)
3994 {
3995    switch(ELEMENTTYPE(*genobj)) {
3996 
3997       case POLYGON:{
3998 	 polyptr flippoly = TOPOLY(genobj);
3999 	 pointlist ppoint;
4000 
4001 	 for (ppoint = flippoly->points; ppoint < flippoly->points +
4002 	    	   flippoly->number; ppoint++)
4003 	    ppoint->y = (y << 1) - ppoint->y;
4004 	 }break;
4005 
4006       case ARC:{
4007 	 arcptr fliparc = TOARC(genobj);
4008 	 float tmpang = 360 - fliparc->angle1;
4009 	 fliparc->angle1 = 360 - fliparc->angle2;
4010 	 fliparc->angle2 = tmpang;
4011 	 if (fliparc->angle1 >= 360) {
4012 	    fliparc->angle1 -= 360;
4013 	    fliparc->angle2 -= 360;
4014 	 }
4015 	 fliparc->radius = -fliparc->radius;
4016 	 fliparc->position.y = (y << 1) - fliparc->position.y;
4017 	 calcarc(fliparc);
4018 	 }break;
4019 
4020       case SPLINE:{
4021 	 splineptr flipspline = TOSPLINE(genobj);
4022 	 int i;
4023 	 for (i = 0; i < 4; i++)
4024 	    flipspline->ctrl[i].y = (y << 1) - flipspline->ctrl[i].y;
4025 	 calcspline(flipspline);
4026 	 }break;
4027    }
4028 }
4029 
4030 /*------------------------------------------------------*/
4031 /* Horizontally flip an element				*/
4032 /*------------------------------------------------------*/
4033 
elementflip(XPoint * position)4034 void elementflip(XPoint *position)
4035 {
4036    short *selectobj;
4037    Boolean single = False;
4038    Boolean preselected;
4039 
4040    preselected = (areawin->selects > 0) ? TRUE : FALSE;
4041    if (!checkselect(ALL_TYPES)) return;
4042    if (areawin->selects == 1) single = True;
4043 
4044    if (eventmode != COPY_MODE)
4045       register_for_undo(XCF_Flip_X, UNDO_MORE, areawin->topinstance,
4046 		(eventmode == MOVE_MODE) ? &areawin->origin : position);
4047 
4048    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
4049 	+ areawin->selects; selectobj++) {
4050 
4051       /* erase the object */
4052       SetForeground(dpy, areawin->gc, BACKGROUND);
4053       easydraw(*selectobj, DOFORALL);
4054 
4055       switch(SELECTTYPE(selectobj)) {
4056 	 case LABEL:{
4057 	    labelptr fliplab = SELTOLABEL(selectobj);
4058 	    if ((fliplab->anchor & (RIGHT | NOTLEFT)) != NOTLEFT)
4059 	       fliplab->anchor ^= (RIGHT | NOTLEFT);
4060 	    if (!single)
4061 	       fliplab->position.x = (position->x << 1) - fliplab->position.x;
4062 	    }break;
4063 	 case GRAPHIC:{
4064 	    graphicptr flipg = SELTOGRAPHIC(selectobj);
4065 	    flipg->scale = -flipg->scale;
4066 #ifndef HAVE_CAIRO
4067 	    flipg->valid = FALSE;
4068 #endif /* !HAVE_CAIRO */
4069 	    if (!single)
4070 	       flipg->position.x = (position->x << 1) - flipg->position.x;
4071 	    }break;
4072 	 case OBJINST:{
4073             objinstptr flipobj = SELTOOBJINST(selectobj);
4074 	    if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break;
4075    	    flipobj->scale = -flipobj->scale;
4076 	    if (!single)
4077 	       flipobj->position.x = (position->x << 1) - flipobj->position.x;
4078 	    }break;
4079 	 case POLYGON: case ARC: case SPLINE:
4080 	    elhflip(topobject->plist + *selectobj, position->x);
4081 	    break;
4082 	 case PATH:{
4083 	    genericptr *genpart;
4084 	    pathptr flippath = SELTOPATH(selectobj);
4085 
4086 	    for (genpart = flippath->plist; genpart < flippath->plist
4087 		  + flippath->parts; genpart++)
4088 	       elhflip(genpart, position->x);
4089 	    }break;
4090       }
4091 
4092       if (preselected || (eventmode != NORMAL_MODE)) {
4093          SetForeground(dpy, areawin->gc, SELECTCOLOR);
4094 	 easydraw(*selectobj, DOFORALL);
4095       }
4096    }
4097    select_invalidate_netlist();
4098    if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
4099       if (!preselected)
4100          unselect_all();
4101 
4102    if (eventmode == NORMAL_MODE)
4103       incr_changes(topobject);
4104    if (eventmode == CATALOG_MODE) {
4105       int libnum;
4106       if ((libnum = is_library(topobject)) >= 0) {
4107 	 composelib(libnum + LIBRARY);
4108 	 drawarea(NULL, NULL, NULL);
4109       }
4110    }
4111    else {
4112       pwriteback(areawin->topinstance);
4113       calcbbox(areawin->topinstance);
4114    }
4115 }
4116 
4117 /*----------------------------------------------*/
4118 /* Vertically flip an element			*/
4119 /*----------------------------------------------*/
4120 
elementvflip(XPoint * position)4121 void elementvflip(XPoint *position)
4122 {
4123    short *selectobj;
4124    /*short fsign;  (jdk) */
4125    Boolean preselected;
4126    Boolean single = False;
4127 
4128    preselected = (areawin->selects > 0) ? TRUE : FALSE;
4129    if (!checkselect(ALL_TYPES)) return;
4130    if (areawin->selects == 1) single = True;
4131 
4132    if (eventmode != COPY_MODE)
4133       register_for_undo(XCF_Flip_Y, UNDO_MORE, areawin->topinstance,
4134 		(eventmode == MOVE_MODE) ? &areawin->origin : position);
4135 
4136    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
4137 	+ areawin->selects; selectobj++) {
4138 
4139       /* erase the object */
4140       SetForeground(dpy, areawin->gc, BACKGROUND);
4141       easydraw(*selectobj, DOFORALL);
4142 
4143       switch(SELECTTYPE(selectobj)) {
4144 	 case(LABEL):{
4145 	    labelptr fliplab = SELTOLABEL(selectobj);
4146 	    if ((fliplab->anchor & (TOP | NOTBOTTOM)) != NOTBOTTOM)
4147 	       fliplab->anchor ^= (TOP | NOTBOTTOM);
4148 	    if (!single)
4149 	       fliplab->position.y = (position->y << 1) - fliplab->position.y;
4150 	    } break;
4151 	 case(OBJINST):{
4152             objinstptr flipobj = SELTOOBJINST(selectobj);
4153 
4154 	    if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break;
4155 	    flipobj->scale = -(flipobj->scale);
4156 	    flipobj->rotation += 180;
4157 	    while (flipobj->rotation >= 360) flipobj->rotation -= 360;
4158 	    if (!single)
4159 	       flipobj->position.y = (position->y << 1) - flipobj->position.y;
4160 	    }break;
4161 	 case(GRAPHIC):{
4162             graphicptr flipg = SELTOGRAPHIC(selectobj);
4163 
4164 	    flipg->scale = -(flipg->scale);
4165 	    flipg->rotation += 180;
4166 	    while (flipg->rotation >= 360) flipg->rotation -= 360;
4167 	    if (!single)
4168 	       flipg->position.y = (position->y << 1) - flipg->position.y;
4169 	    }break;
4170 	 case POLYGON: case ARC: case SPLINE:
4171 	    elvflip(topobject->plist + *selectobj, position->y);
4172 	    break;
4173 	 case PATH:{
4174 	    genericptr *genpart;
4175 	    pathptr flippath = SELTOPATH(selectobj);
4176 
4177 	    for (genpart = flippath->plist; genpart < flippath->plist
4178 		  + flippath->parts; genpart++)
4179 	       elvflip(genpart, position->y);
4180 	    }break;
4181       }
4182       if (preselected || (eventmode != NORMAL_MODE)) {
4183          SetForeground(dpy, areawin->gc, SELECTCOLOR);
4184 	 easydraw(*selectobj, DOFORALL);
4185       }
4186    }
4187    select_invalidate_netlist();
4188    if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE)
4189       if (!preselected)
4190          unselect_all();
4191    if (eventmode == NORMAL_MODE) {
4192       incr_changes(topobject);
4193    }
4194    if (eventmode == CATALOG_MODE) {
4195       int libnum;
4196       if ((libnum = is_library(topobject)) >= 0) {
4197 	 composelib(libnum + LIBRARY);
4198 	 drawarea(NULL, NULL, NULL);
4199       }
4200    }
4201    else {
4202       pwriteback(areawin->topinstance);
4203       calcbbox(areawin->topinstance);
4204    }
4205 }
4206 
4207 /*----------------------------------------*/
4208 /* ButtonPress handler during delete mode */
4209 /*----------------------------------------*/
4210 
deletebutton(int x,int y)4211 void deletebutton(int x, int y)
4212 {
4213    UNUSED(x); UNUSED(y);
4214 
4215    if (checkselect(ALL_TYPES)) {
4216       standard_element_delete(ERASE);
4217       calcbbox(areawin->topinstance);
4218    }
4219    setoptionmenu();	/* Return GUI check/radio boxes to default */
4220 }
4221 
4222 /*----------------------------------------------------------------------*/
4223 /* Process of element deletion.  Remove one element from the indicated	*/
4224 /* object.								*/
4225 /*----------------------------------------------------------------------*/
4226 
delete_one_element(objinstptr thisinstance,genericptr thiselement)4227 void delete_one_element(objinstptr thisinstance, genericptr thiselement)
4228 {
4229    objectptr thisobject;
4230    genericptr *genobj;
4231    Boolean pinchange = False;
4232 
4233    thisobject = thisinstance->thisobject;
4234 
4235    /* The netlist contains pointers to elements which no longer		*/
4236    /* exist on the page, so we should remove them from the netlist.	*/
4237 
4238    if (RemoveFromNetlist(thisobject, thiselement)) pinchange = True;
4239    for (genobj = thisobject->plist; genobj < thisobject->plist
4240 		+ thisobject->parts; genobj++)
4241       if (*genobj == thiselement)
4242 	 break;
4243 
4244    if (genobj == thisobject->plist + thisobject->parts) return;
4245 
4246    for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++)
4247       *(genobj - 1) = *genobj;
4248    thisobject->parts--;
4249 
4250    if (pinchange) setobjecttype(thisobject);
4251    incr_changes(thisobject);
4252    calcbbox(thisinstance);
4253    invalidate_netlist(thisobject);
4254    /* freenetlist(thisobject); */
4255 }
4256 
4257 /*----------------------------------------------------------------------*/
4258 /* Process of element deletion.  Remove everything in the selection	*/
4259 /* list from the indicated object, and return a new object containing	*/
4260 /* only the deleted elements.						*/
4261 /*									*/
4262 /* if drawmode is DRAW, we erase the objects as we remove them.		*/
4263 /*									*/
4264 /* Note that if "slist" is areawin->selectlist, it is freed by this	*/
4265 /* routine (calls freeselects()), but not otherwise.			*/
4266 /*----------------------------------------------------------------------*/
4267 
delete_element(objinstptr thisinstance,short * slist,int selects,short drawmode)4268 objectptr delete_element(objinstptr thisinstance, short *slist, int selects,
4269 		short drawmode)
4270 {
4271    short *selectobj;
4272    objectptr delobj, thisobject;
4273    genericptr *genobj;
4274    Boolean pinchange = False;
4275 
4276    if (slist == NULL || selects == 0) return NULL;
4277 
4278    thisobject = thisinstance->thisobject;
4279 
4280    delobj = (objectptr) malloc(sizeof(object));
4281    initmem(delobj);
4282 
4283    if (drawmode) {
4284       SetForeground(dpy, areawin->gc, BACKGROUND);
4285    }
4286 
4287    for (selectobj = slist; selectobj < slist + selects; selectobj++) {
4288       genobj = thisobject->plist + *selectobj;
4289       if (drawmode) easydraw(*selectobj, DOFORALL);
4290       PLIST_INCR(delobj);
4291       *(delobj->plist + delobj->parts) = *genobj;
4292       delobj->parts++;
4293 
4294        /* The netlist contains pointers to elements which no longer	*/
4295        /* exist on the page, so we should remove them from the netlist.	*/
4296 
4297       if (RemoveFromNetlist(thisobject, *genobj)) pinchange = True;
4298       for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++)
4299 	 *(genobj - 1) = *genobj;
4300       thisobject->parts--;
4301       reviseselect(slist, selects, selectobj);
4302    }
4303    if (pinchange) setobjecttype(thisobject);
4304 
4305    if (slist == areawin->selectlist)
4306       freeselects();
4307 
4308    calcbbox(thisinstance);
4309    /* freenetlist(thisobject); */
4310 
4311    if (drawmode) {
4312       SetForeground(dpy, areawin->gc, FOREGROUND);
4313       drawarea(NULL, NULL, NULL);
4314    }
4315    return delobj;
4316 }
4317 
4318 /*----------------------------------------------------------------------*/
4319 /* Wrapper for delete_element().  Remember this deletion for the undo	*/
4320 /* function.								*/
4321 /*----------------------------------------------------------------------*/
4322 
standard_element_delete(short drawmode)4323 void standard_element_delete(short drawmode)
4324 {
4325    objectptr delobj;
4326 
4327 /* register_for_undo(XCF_Select, UNDO_MORE, areawin->topinstance, */
4328 /*		areawin->selectlist, areawin->selects); */
4329    select_invalidate_netlist();
4330    delobj = delete_element(areawin->topinstance, areawin->selectlist,
4331 	areawin->selects, drawmode);
4332    register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance,
4333 		delobj, (int)drawmode);
4334    incr_changes(topobject);  /* Treat as one change */
4335 }
4336 
4337 /*----------------------------------------------------------------------*/
4338 /* Another wrapper for delete_element(), in which we do not save the	*/
4339 /* deletion as an undo event.  However, the returned object is saved	*/
4340 /* on areawin->editstack, so that the objects can be grabbed.  This	*/
4341 /* allows objects to be carried across pages and through the hierarchy.	*/
4342 /*----------------------------------------------------------------------*/
4343 
delete_for_xfer(short drawmode,short * slist,int selects)4344 void delete_for_xfer(short drawmode, short *slist, int selects)
4345 {
4346    if (selects > 0) {
4347       reset(areawin->editstack, DESTROY);
4348       areawin->editstack = delete_element(areawin->topinstance,
4349 		slist, selects, drawmode);
4350    }
4351 }
4352 
4353 /*----------------------------------------------------------------------*/
4354 /* Yet another wrapper for delete_element(), in which we destroy the	*/
4355 /* object returned and free all associated memory.			*/
4356 /*----------------------------------------------------------------------*/
4357 
delete_noundo(short drawmode)4358 void delete_noundo(short drawmode)
4359 {
4360    objectptr delobj;
4361 
4362    select_invalidate_netlist();
4363    delobj = delete_element(areawin->topinstance, areawin->selectlist,
4364 	areawin->selects, drawmode);
4365 
4366    if (delobj != NULL) reset(delobj, DESTROY);
4367 }
4368 
4369 /*----------------------------------------------------------------------*/
4370 /* Undelete last deleted elements and return a selectlist of the	*/
4371 /* elements.  If "olist" is non-NULL, then the undeleted elements are	*/
4372 /* placed into the object of thisinstance in the order given by olist,	*/
4373 /* and a copy of olist is returned.  If "olist" is NULL, then the	*/
4374 /* undeleted elements are placed at the end of thisinstance->thisobject	*/
4375 /* ->plist, and a new selection list is returned.  If "olist" is non-	*/
4376 /* NULL, then the size of olist had better match the number of objects	*/
4377 /* in delobj!  It is up to the calling routine to check this.		*/
4378 /*----------------------------------------------------------------------*/
4379 
xc_undelete(objinstptr thisinstance,objectptr delobj,short mode,short * olist)4380 short *xc_undelete(objinstptr thisinstance, objectptr delobj, short mode,
4381 	short *olist)
4382 {
4383    objectptr  thisobject;
4384    genericptr *regen;
4385    short      *slist, count, i; /* position; (jdk) */
4386 
4387    thisobject = thisinstance->thisobject;
4388    slist = (short *)malloc(delobj->parts * sizeof(short));
4389    count = 0;
4390 
4391    for (regen = delobj->plist; regen < delobj->plist + delobj->parts; regen++) {
4392       PLIST_INCR(thisobject);
4393       if (olist == NULL) {
4394          *(slist + count) = thisobject->parts;
4395          *(topobject->plist + topobject->parts) = *regen;
4396       }
4397       else {
4398          *(slist + count) = *(olist + count);
4399 	 for (i = thisobject->parts; i > *(olist + count); i--)
4400 	    *(thisobject->plist + i) = *(thisobject->plist + i - 1);
4401 	 *(thisobject->plist + i) = *regen;
4402       }
4403       thisobject->parts++;
4404       if (mode) {
4405          XTopSetForeground((*regen)->color);
4406 	 easydraw(*(slist + count), DEFAULTCOLOR);
4407       }
4408       count++;
4409 
4410       /* If the element has passed parameters (eparam), then we have to */
4411       /* check if the key exists in the new parent object.  If not,	*/
4412       /* delete the parameter.						*/
4413 
4414       if ((*regen)->passed) {
4415 	 eparamptr nextepp, epp = (*regen)->passed;
4416 	 while (epp != NULL) {
4417 	    nextepp = epp->next;
4418 	    if (!match_param(thisobject, epp->key)) {
4419 	       if (epp == (*regen)->passed) (*regen)->passed = nextepp;
4420 	       free_element_param(*regen, epp);
4421 	    }
4422 	    epp = nextepp;
4423 	 }
4424       }
4425 
4426       /* Likewise, string parameters must be checked in labels because	*/
4427       /* they act like element parameters.				*/
4428 
4429       if (IS_LABEL(*regen)) {
4430 	 labelptr glab = TOLABEL(regen);
4431 	 stringpart *gstr, *lastpart = NULL;
4432 	 for (gstr = glab->string; gstr != NULL; gstr = lastpart->nextpart) {
4433 	    if (gstr->type == PARAM_START) {
4434 	       if (!match_param(thisobject, gstr->data.string)) {
4435 		  free(gstr->data.string);
4436 		  if (lastpart)
4437 		     lastpart->nextpart = gstr->nextpart;
4438 		  else
4439 		     glab->string = gstr->nextpart;
4440 		  free(gstr);
4441 		  gstr = (lastpart) ? lastpart : glab->string;
4442 	       }
4443 	    }
4444 	    lastpart = gstr;
4445 	 }
4446       }
4447    }
4448    incr_changes(thisobject);	/* treat as one change */
4449    calcbbox(thisinstance);
4450 
4451    /* flush the delete buffer but don't delete the elements */
4452    reset(delobj, SAVE);
4453 
4454    if (delobj != areawin->editstack) free(delobj);
4455 
4456    return slist;
4457 }
4458 
4459 /*----------------------------*/
4460 /* select save object handler */
4461 /*----------------------------*/
4462 
printname(objectptr curobject)4463 void printname(objectptr curobject)
4464 {
4465    char editstr[10], pagestr[10];
4466    short ispage;
4467 
4468 #ifndef TCL_WRAPPER
4469    Arg	wargs[1];
4470    Dimension swidth, swidth2, sarea;
4471    char tmpname[256];
4472    char *sptr = tmpname;
4473 #endif
4474 
4475    /* print full string to make message widget proper size */
4476 
4477    strcpy(editstr, ((ispage = is_page(curobject)) >= 0) ? "Editing: " : "");
4478    strcpy(editstr, (is_library(curobject) >= 0) ? "Library: " : "");
4479    if (strstr(curobject->name, "Page") == NULL && (ispage >= 0))
4480       sprintf(pagestr, " (p. %d)", areawin->page + 1);
4481    else
4482       pagestr[0] = '\0';
4483    W2printf("%s%s%s", editstr, curobject->name, pagestr);
4484 
4485    /* Tcl doesn't update width changes immediately. . . what to do? */
4486    /* (i.e., Tk_Width(message2) gives the original width) */
4487 #ifndef TCL_WRAPPER
4488 
4489    XtSetArg(wargs[0], XtNwidth, &sarea);
4490    XtGetValues(message2, wargs, 1);
4491 
4492    /* in the remote case that the string is longer than message widget,    */
4493    /* truncate the string and denote the truncation with an ellipsis (...) */
4494 
4495    strcpy(tmpname, curobject->name);
4496    swidth2 = XTextWidth(appdata.xcfont, editstr, strlen(editstr));
4497    swidth = XTextWidth(appdata.xcfont, tmpname, strlen(tmpname));
4498 
4499    if ((swidth + swidth2) > sarea) {
4500       char *ip;
4501       while ((swidth + swidth2) > sarea) {
4502          sptr++;
4503          swidth = XTextWidth(appdata.xcfont, sptr, strlen(sptr));
4504       }
4505       for(ip = sptr; ip < sptr + 3 && *ip != '\0'; ip++) *ip = '.';
4506 
4507       W2printf("Editing: %s", sptr);
4508    }
4509 #endif
4510 }
4511 
4512 /*--------------------------------------------------------------*/
4513 /* Make sure that a string does not conflict with postscript	*/
4514 /* names (commands and definitions found in xcircps2.pro).	*/
4515 /*								*/
4516 /* Return value is NULL if no change was made.  Otherwise, the	*/
4517 /* return value is an allocated string.				*/
4518 /*--------------------------------------------------------------*/
4519 
checkvalidname(char * teststring,objectptr newobj)4520 char *checkvalidname(char *teststring, objectptr newobj)
4521 {
4522    int i, j;
4523    short dupl;  /* flag a duplicate string */
4524    objectptr *libobj;
4525    char *sptr, *pptr, *cptr;
4526    aliasptr aref;
4527    slistptr sref;
4528 
4529    /* Try not to allocate memory unless necessary */
4530 
4531    sptr = teststring;
4532    pptr = sptr;
4533 
4534    do {
4535       dupl = 0;
4536       if (newobj != NULL) {
4537          for (i = 0; i < xobjs.numlibs; i++) {
4538 	    for (j = 0; j < xobjs.userlibs[i].number; j++) {
4539 	       libobj = xobjs.userlibs[i].library + j;
4540 
4541 	       if (*libobj == newobj) continue;
4542                if (!strcmp(pptr, (*libobj)->name)) {
4543 
4544 		  /* Prepend an underscore to the object name.	If the	*/
4545 		  /* object has no technology, create a null technology	*/
4546 		  /* name.  Otherwise, the technology remains the same	*/
4547 		  /* but the object name gets the prepended underscore.	*/
4548 
4549 		  if ((cptr = strstr(pptr, "::")) == NULL) {
4550                      pptr = (char *)malloc(strlen((*libobj)->name) + 4);
4551 		     sprintf(pptr, "::_%s", (*libobj)->name);
4552 		  }
4553 		  else {
4554 		     int offset = cptr - pptr + 2;
4555 		     if (pptr == sptr)
4556                         pptr = (char *)malloc(strlen((*libobj)->name) + 2);
4557 		     else
4558                         pptr = (char *)realloc(pptr, strlen((*libobj)->name) + 2);
4559 	             sprintf(pptr, "%s", (*libobj)->name);
4560 	             sprintf(pptr + offset, "_%s", (*libobj)->name + offset);
4561 		  }
4562 	          dupl = 1;
4563 	       }
4564 	    }
4565 	 }
4566 
4567          /* If we're in the middle of a file load, the name cannot be	*/
4568          /* the same as an alias, either.				*/
4569 
4570          if (aliastop != NULL) {
4571 	    for (aref = aliastop; aref != NULL; aref = aref->next) {
4572 	       for (sref = aref->aliases; sref != NULL; sref = sref->next) {
4573 	          if (!strcmp(pptr, sref->alias)) {
4574 		     if (pptr == sptr)
4575                         pptr = (char *)malloc(strlen(sref->alias) + 2);
4576 		     else
4577                         pptr = (char *)realloc(pptr, strlen(sref->alias) + 2);
4578 	             sprintf(pptr, "_%s", sref->alias);
4579 	             dupl = 1;
4580 		  }
4581 	       }
4582 	    }
4583          }
4584       }
4585 
4586    } while (dupl == 1);
4587 
4588    return (pptr == sptr) ? NULL : pptr;
4589 }
4590 
4591 /*--------------------------------------------------------------*/
4592 /* Make sure that name for new object does not conflict with	*/
4593 /* existing object definitions					*/
4594 /*								*/
4595 /* Return:  True if name required change, False otherwise	*/
4596 /*--------------------------------------------------------------*/
4597 
checkname(objectptr newobj)4598 Boolean checkname(objectptr newobj)
4599 {
4600    char *pptr;
4601 
4602    /* Check for empty string */
4603    if (strlen(newobj->name) == 0) {
4604       Wprintf("Blank object name changed to default");
4605       sprintf(newobj->name, "user_object");
4606    }
4607 
4608    pptr = checkvalidname(newobj->name, newobj);
4609 
4610    /* Change name if necessary to avoid naming conflicts */
4611    if (pptr == NULL) {
4612       Wprintf("Created new object %s", newobj->name);
4613       return False;
4614    }
4615    else {
4616       Wprintf("Changed name from %s to %s to avoid conflict with "
4617 			"existing object", newobj->name, pptr);
4618 
4619       strncpy(newobj->name, pptr, 79);
4620       free(pptr);
4621    }
4622    return True;
4623 }
4624 
4625 /*------------------------------------------------------------*/
4626 /* Find the object "dot" in the builtin library, if it exists */
4627 /*------------------------------------------------------------*/
4628 
finddot()4629 objectptr finddot()
4630 {
4631    objectptr dotobj;
4632    short i, j;
4633    char *name, *pptr;
4634 
4635    for (i = 0; i < xobjs.numlibs; i++) {
4636       for (j = 0; j < xobjs.userlibs[i].number; j++) {
4637 	 dotobj = *(xobjs.userlibs[i].library + j);
4638 	 name = dotobj->name;
4639          if ((pptr = strstr(name, "::")) != NULL) name = pptr + 2;
4640          if (!strcmp(name, "dot")) {
4641             return dotobj;
4642          }
4643       }
4644    }
4645    return (objectptr)NULL;
4646 }
4647 
4648 /*--------------------------------------*/
4649 /* Add value origin to all points	*/
4650 /*--------------------------------------*/
4651 
movepoints(genericptr * ssgen,short deltax,short deltay)4652 void movepoints(genericptr *ssgen, short deltax, short deltay)
4653 {
4654    switch(ELEMENTTYPE(*ssgen)) {
4655 	 case ARC:{
4656             fpointlist sspoints;
4657             TOARC(ssgen)->position.x += deltax;
4658             TOARC(ssgen)->position.y += deltay;
4659             for (sspoints = TOARC(ssgen)->points; sspoints < TOARC(ssgen)->points +
4660 	          TOARC(ssgen)->number; sspoints++) {
4661 	       sspoints->x += deltax;
4662 	       sspoints->y += deltay;
4663             }
4664 	    }break;
4665 
4666 	 case POLYGON:{
4667             pointlist sspoints;
4668             for (sspoints = TOPOLY(ssgen)->points; sspoints < TOPOLY(ssgen)->points +
4669 	          TOPOLY(ssgen)->number; sspoints++) {
4670 	       sspoints->x += deltax;
4671 	       sspoints->y += deltay;
4672 	    }
4673 	    }break;
4674 
4675 	 case SPLINE:{
4676             fpointlist sspoints;
4677             short j;
4678             for (sspoints = TOSPLINE(ssgen)->points; sspoints <
4679 		  TOSPLINE(ssgen)->points + INTSEGS; sspoints++) {
4680 	       sspoints->x += deltax;
4681 	       sspoints->y += deltay;
4682             }
4683             for (j = 0; j < 4; j++) {
4684                TOSPLINE(ssgen)->ctrl[j].x += deltax;
4685                TOSPLINE(ssgen)->ctrl[j].y += deltay;
4686             }
4687             }break;
4688 	 case OBJINST:
4689 	    TOOBJINST(ssgen)->position.x += deltax;
4690 	    TOOBJINST(ssgen)->position.y += deltay;
4691 	    break;
4692 	 case GRAPHIC:
4693 	    TOGRAPHIC(ssgen)->position.x += deltax;
4694 	    TOGRAPHIC(ssgen)->position.y += deltay;
4695 	    break;
4696 	 case LABEL:
4697 	    TOLABEL(ssgen)->position.x += deltax;
4698 	    TOLABEL(ssgen)->position.y += deltay;
4699 	    break;
4700    }
4701 }
4702 
4703 /*----------------------------------------------------------------------*/
4704 /* Add value origin to all edited points, according to edit flags	*/
4705 /*----------------------------------------------------------------------*/
4706 
editpoints(genericptr * ssgen,short deltax,short deltay)4707 void editpoints(genericptr *ssgen, short deltax, short deltay)
4708 {
4709    pathptr editpath;
4710    polyptr editpoly;
4711    splineptr editspline;
4712    short cycle, cpoint;
4713    pointselect *cptr;
4714    XPoint *curpt;
4715    genericptr *ggen;
4716 
4717    switch(ELEMENTTYPE(*ssgen)) {
4718       case POLYGON:
4719 	 editpoly = TOPOLY(ssgen);
4720 	 if (editpoly->cycle == NULL)
4721 	    movepoints(ssgen, deltax, deltay);
4722 	 else {
4723 	    for (cptr = editpoly->cycle;; cptr++) {
4724 	       cycle = cptr->number;
4725 	       curpt = editpoly->points + cycle;
4726 	       if (cptr->flags & EDITX) curpt->x += deltax;
4727 	       if (cptr->flags & EDITY) curpt->y += deltay;
4728 	       if (cptr->flags & LASTENTRY) break;
4729 	    }
4730 	 }
4731 	 exprsub(*ssgen);
4732 	 break;
4733 
4734       case SPLINE:
4735 	 editspline = TOSPLINE(ssgen);
4736 	 if (editspline->cycle == NULL)
4737 	    movepoints(ssgen, deltax, deltay);
4738 	 else {
4739 	    for (cptr = editspline->cycle;; cptr++) {
4740 	       cycle = cptr->number;
4741 	       if (cycle == 0 || cycle == 3) {
4742 		  cpoint = (cycle == 0) ? 1 : 2;
4743 	          if (cptr->flags & EDITX) editspline->ctrl[cpoint].x += deltax;
4744 	          if (cptr->flags & EDITY) editspline->ctrl[cpoint].y += deltay;
4745 	       }
4746 	       if (cptr->flags & EDITX) editspline->ctrl[cycle].x += deltax;
4747 	       if (cptr->flags & EDITY) editspline->ctrl[cycle].y += deltay;
4748 	       if (cptr->flags & ANTIXY) {
4749 		  editspline->ctrl[cycle].x -= deltax;
4750 		  editspline->ctrl[cycle].y -= deltay;
4751 	       }
4752 	       if (cptr->flags & LASTENTRY) break;
4753 	    }
4754 	 }
4755 	 exprsub(*ssgen);
4756 	 calcspline(editspline);
4757          break;
4758 
4759       case PATH:
4760 	 editpath = TOPATH(ssgen);
4761 	 if (checkcycle(*ssgen, 0) < 0) {
4762 	    for (ggen = editpath->plist;  ggen < editpath->plist + editpath->parts;
4763 			ggen++)
4764 	       movepoints(ggen, deltax, deltay);
4765 	 }
4766 	 else {
4767 	    for (ggen = editpath->plist;  ggen < editpath->plist + editpath->parts;
4768 			ggen++) {
4769 	       if (checkcycle(*ggen, 0) >= 0)
4770 	          editpoints(ggen, deltax, deltay);
4771 	    }
4772          }
4773          break;
4774 
4775       default:
4776 	 movepoints(ssgen, deltax, deltay);
4777 	 exprsub(*ssgen);
4778 	 break;
4779    }
4780 }
4781 
4782 #ifndef TCL_WRAPPER
4783 
xlib_makeobject(xcWidget w,caddr_t nulldata)4784 void xlib_makeobject(xcWidget w, caddr_t nulldata)
4785 {
4786    UNUSED(w); UNUSED(nulldata);
4787 
4788    domakeobject(-1, (char *)_STR2, FALSE);
4789 }
4790 
4791 #endif /* !TCL_WRAPPER */
4792 
4793 /*--------------------------------------------------------------*/
4794 /* Set the name for a new user-defined object and make the	*/
4795 /* object.  If "forceempty" is true, we allow creation of a new	*/
4796 /* object with no elements (normally would be used only from a	*/
4797 /* script, where an object is being constructed automatically).	*/
4798 /*--------------------------------------------------------------*/
4799 
domakeobject(int libnum,char * name,Boolean forceempty)4800 objinstptr domakeobject(int libnum, char *name, Boolean forceempty)
4801 {
4802    objectptr *newobj;
4803    objinstptr *newinst;
4804    genericptr *ssgen;
4805    oparamptr ops, newop;
4806    eparamptr epp, newepp;
4807    stringpart *sptr;
4808    XPoint origin;
4809    short loclibnum = libnum;
4810 
4811    if (libnum == -1) loclibnum = USERLIB - LIBRARY;
4812 
4813    /* make room for new entry in library list */
4814 
4815    xobjs.userlibs[loclibnum].library = (objectptr *)
4816 	realloc(xobjs.userlibs[loclibnum].library,
4817 	(xobjs.userlibs[loclibnum].number + 1) * sizeof(objectptr));
4818 
4819    newobj = xobjs.userlibs[loclibnum].library + xobjs.userlibs[loclibnum].number;
4820 
4821    *newobj = delete_element(areawin->topinstance, areawin->selectlist,
4822 			areawin->selects, NORMAL);
4823 
4824    if (*newobj == NULL) {
4825       objectptr initobj;
4826 
4827       if (!forceempty) return NULL;
4828 
4829       /* Create a new (empty) object */
4830 
4831       initobj = (objectptr) malloc(sizeof(object));
4832       initmem(initobj);
4833       *newobj = initobj;
4834    }
4835 
4836    invalidate_netlist(topobject);
4837    xobjs.userlibs[loclibnum].number++;
4838 
4839    /* Create the instance of this object so we can compute a bounding box */
4840 
4841    NEW_OBJINST(newinst, topobject);
4842    instancedefaults(*newinst, *newobj, 0, 0);
4843    calcbbox(*newinst);
4844 
4845    /* find closest snap point to bbox center and make this the obj. center */
4846 
4847    if (areawin->center) {
4848       origin.x = (*newobj)->bbox.lowerleft.x + (*newobj)->bbox.width / 2;
4849       origin.y = (*newobj)->bbox.lowerleft.y + (*newobj)->bbox.height / 2;
4850    }
4851    else {
4852       origin.x = origin.y = 0;
4853    }
4854    u2u_snap(&origin);
4855    instancedefaults(*newinst, *newobj, origin.x, origin.y);
4856 
4857    for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts;
4858 	  ssgen++) {
4859       switch(ELEMENTTYPE(*ssgen)) {
4860 
4861 	 case(OBJINST):
4862             TOOBJINST(ssgen)->position.x -= origin.x;
4863             TOOBJINST(ssgen)->position.y -= origin.y;
4864 	    break;
4865 
4866 	 case(GRAPHIC):
4867             TOGRAPHIC(ssgen)->position.x -= origin.x;
4868             TOGRAPHIC(ssgen)->position.y -= origin.y;
4869 	    break;
4870 
4871 	 case(LABEL):
4872             TOLABEL(ssgen)->position.x -= origin.x;
4873             TOLABEL(ssgen)->position.y -= origin.y;
4874 	    break;
4875 
4876 	 case(PATH):{
4877 	    genericptr *pathlist;
4878 	    for (pathlist = TOPATH(ssgen)->plist;  pathlist < TOPATH(ssgen)->plist
4879 		  + TOPATH(ssgen)->parts; pathlist++) {
4880 	       movepoints(pathlist, -origin.x, -origin.y);
4881 	    }
4882 	    }break;
4883 
4884 	 default:
4885 	    movepoints(ssgen, -origin.x, -origin.y);
4886 	    break;
4887       }
4888    }
4889 
4890    for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts;
4891 	  ssgen++) {
4892       for (epp = (*ssgen)->passed; epp != NULL; epp = epp->next) {
4893 	 ops = match_param(topobject, epp->key);
4894 	 newop = copyparameter(ops);
4895 	 newop->next = (*newobj)->params;
4896 	 (*newobj)->params = newop;
4897 
4898 	 /* Generate an indirect parameter reference from child to parent */
4899 	 newepp = make_new_eparam(epp->key);
4900 	 newepp->flags |= P_INDIRECT;
4901 	 newepp->pdata.refkey = strdup(epp->key);
4902 	 newepp->next = (*newinst)->passed;
4903 	 (*newinst)->passed = newepp;
4904       }
4905       if (IS_LABEL(*ssgen)) {
4906 	 /* Also need to check for substring parameters in labels */
4907 	 for (sptr = TOLABEL(ssgen)->string; sptr != NULL; sptr = sptr->nextpart) {
4908 	    if (sptr->type == PARAM_START) {
4909 	       ops = match_param(topobject, sptr->data.string);
4910 	       if (ops) {
4911 	          newop = copyparameter(ops);
4912 	          newop->next = (*newobj)->params;
4913 	          (*newobj)->params = newop;
4914 	       }
4915 
4916 	       /* Generate an indirect parameter reference from child to parent */
4917 	       newepp = make_new_eparam(sptr->data.string);
4918 	       newepp->flags |= P_INDIRECT;
4919 	       newepp->pdata.refkey = strdup(sptr->data.string);
4920 	       newepp->next = (*newinst)->passed;
4921 	       (*newinst)->passed = newepp;
4922 	    }
4923 	 }
4924       }
4925    }
4926 
4927    /* any parameters in the top-level object that used by the selected	*/
4928    /* elements must be copied into the new object.			*/
4929 
4930    /* put new object back into place */
4931 
4932    (*newobj)->hidden = False;
4933    (*newobj)->schemtype = SYMBOL;
4934 
4935    calcbbox(*newinst);
4936    incr_changes(*newobj);
4937 
4938    /* (netlist invalidation was taken care of by delete_element() */
4939    /* Also, incr_changes(topobject) was done by delete_element())	*/
4940 
4941    XTopSetForeground((*newinst)->color);
4942    UDrawObject(*newinst, SINGLE, (*newinst)->color,
4943 		xobjs.pagelist[areawin->page]->wirewidth, NULL);
4944 
4945 
4946    /* Copy name into object and check for conflicts */
4947 
4948    strcpy((*newobj)->name, name);
4949    checkname(*newobj);
4950 
4951    /* register the technology and mark the technology as not saved */
4952    AddObjectTechnology(*newobj);
4953 
4954    /* generate library instance for this object (bounding box	*/
4955    /* should be default, so don't do calcbbox() on it)		*/
4956 
4957    addtoinstlist(loclibnum, *newobj, FALSE);
4958 
4959    /* recompile the user catalog and reset view bounds */
4960 
4961    composelib(loclibnum + LIBRARY);
4962    centerview(xobjs.libtop[loclibnum + LIBRARY]);
4963 
4964    return *newinst;
4965 }
4966 
4967 #ifndef TCL_WRAPPER
4968 
4969 /*-------------------------------------------*/
4970 /* Make a user object from selected elements */
4971 /*-------------------------------------------*/
4972 
selectsave(xcWidget w,caddr_t clientdata,caddr_t calldata)4973 void selectsave(xcWidget w, caddr_t clientdata, caddr_t calldata)
4974 {
4975    buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));
4976    UNUSED(clientdata); UNUSED(calldata);
4977 
4978    if (areawin->selects == 0) return;  /* nothing was selected */
4979 
4980    /* Get a name for the new object */
4981 
4982    eventmode = NORMAL_MODE;
4983    popdata->dataptr = NULL;
4984    popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */
4985    popupprompt(w, "Enter name for new object:", "\0", xlib_makeobject, popdata, NULL);
4986 }
4987 
4988 #endif
4989 
4990 /*-----------------------------*/
4991 /* Edit-stack support routines */
4992 /*-----------------------------*/
4993 
arceditpush(arcptr lastarc)4994 void arceditpush(arcptr lastarc)
4995 {
4996    arcptr *newarc;
4997 
4998    NEW_ARC(newarc, areawin->editstack);
4999    arccopy(*newarc, lastarc);
5000    copycycles(&((*newarc)->cycle), &(lastarc->cycle));
5001 }
5002 
5003 /*--------------------------------------*/
5004 
splineeditpush(splineptr lastspline)5005 void splineeditpush(splineptr lastspline)
5006 {
5007    splineptr *newspline;
5008 
5009    NEW_SPLINE(newspline, areawin->editstack);
5010    splinecopy(*newspline, lastspline);
5011 }
5012 
5013 /*--------------------------------------*/
5014 
polyeditpush(polyptr lastpoly)5015 void polyeditpush(polyptr lastpoly)
5016 {
5017    polyptr *newpoly;
5018 
5019    NEW_POLY(newpoly, areawin->editstack);
5020    polycopy(*newpoly, lastpoly);
5021 }
5022 
5023 /*--------------------------------------*/
5024 
patheditpush(pathptr lastpath)5025 void patheditpush(pathptr lastpath)
5026 {
5027    pathptr *newpath;
5028 
5029    NEW_PATH(newpath, areawin->editstack);
5030    pathcopy(*newpath, lastpath);
5031 }
5032 
5033 /*-----------------------------*/
5034 /* Copying support routines    */
5035 /*-----------------------------*/
5036 
copypoints(pointlist points,int number)5037 pointlist copypoints(pointlist points, int number)
5038 {
5039    pointlist rpoints, cpoints, newpoints;
5040 
5041    rpoints = (pointlist) malloc(number * sizeof(XPoint));
5042    for (newpoints = rpoints, cpoints = points;
5043 		newpoints < rpoints + number;
5044 		newpoints++, cpoints++) {
5045       newpoints->x = cpoints->x;
5046       newpoints->y = cpoints->y;
5047    }
5048    return rpoints;
5049 }
5050 
5051 /*------------------------------------------*/
5052 
graphiccopy(graphicptr newg,graphicptr copyg)5053 void graphiccopy(graphicptr newg, graphicptr copyg)
5054 {
5055    Imagedata *iptr;
5056    int i;
5057 
5058    newg->source = copyg->source;
5059    newg->position.x = copyg->position.x;
5060    newg->position.y = copyg->position.y;
5061    newg->rotation = copyg->rotation;
5062    newg->scale = copyg->scale;
5063    newg->color = copyg->color;
5064    newg->passed = NULL;
5065    copyalleparams((genericptr)newg, (genericptr)copyg);
5066 #ifndef HAVE_CAIRO
5067    newg->valid = FALSE;
5068    newg->target = NULL;
5069    newg->clipmask = (Pixmap)NULL;
5070 #endif /* HAVE_CAIRO */
5071 
5072    /* Update the refcount of the source image */
5073    for (i = 0; i < xobjs.images; i++) {
5074       iptr = xobjs.imagelist + i;
5075       if (iptr->image == newg->source) {
5076 	 iptr->refcount++;
5077 	 break;
5078       }
5079    }
5080 }
5081 
5082 /*------------------------------------------*/
5083 
labelcopy(labelptr newtext,labelptr copytext)5084 void labelcopy(labelptr newtext, labelptr copytext)
5085 {
5086    newtext->string = stringcopy(copytext->string);
5087    newtext->position.x = copytext->position.x;
5088    newtext->position.y = copytext->position.y;
5089    newtext->rotation = copytext->rotation;
5090    newtext->scale = copytext->scale;
5091    newtext->anchor = copytext->anchor;
5092    newtext->color = copytext->color;
5093    newtext->passed = NULL;
5094    newtext->cycle = NULL;
5095    copyalleparams((genericptr)newtext, (genericptr)copytext);
5096    newtext->pin = copytext->pin;
5097 }
5098 
5099 /*------------------------------------------*/
5100 
arccopy(arcptr newarc,arcptr copyarc)5101 void arccopy(arcptr newarc, arcptr copyarc)
5102 {
5103    newarc->style = copyarc->style;
5104    newarc->color = copyarc->color;
5105    newarc->position.x = copyarc->position.x;
5106    newarc->position.y = copyarc->position.y;
5107    newarc->radius = copyarc->radius;
5108    newarc->yaxis = copyarc->yaxis;
5109    newarc->angle1 = copyarc->angle1;
5110    newarc->angle2 = copyarc->angle2;
5111    newarc->width = copyarc->width;
5112    newarc->passed = NULL;
5113    newarc->cycle = NULL;
5114    copyalleparams((genericptr)newarc, (genericptr)copyarc);
5115    calcarc(newarc);
5116 }
5117 
5118 /*------------------------------------------*/
5119 
polycopy(polyptr newpoly,polyptr copypoly)5120 void polycopy(polyptr newpoly, polyptr copypoly)
5121 {
5122    newpoly->style = copypoly->style;
5123    newpoly->color = copypoly->color;
5124    newpoly->width = copypoly->width;
5125    newpoly->number = copypoly->number;
5126    copycycles(&(newpoly->cycle), &(copypoly->cycle));
5127    newpoly->points = copypoints(copypoly->points, copypoly->number);
5128 
5129    newpoly->passed = NULL;
5130    copyalleparams((genericptr)newpoly, (genericptr)copypoly);
5131 }
5132 
5133 /*------------------------------------------*/
5134 
splinecopy(splineptr newspline,splineptr copyspline)5135 void splinecopy(splineptr newspline, splineptr copyspline)
5136 {
5137    short i;
5138 
5139    newspline->style = copyspline->style;
5140    newspline->color = copyspline->color;
5141    newspline->width = copyspline->width;
5142    copycycles(&(newspline->cycle), &(copyspline->cycle));
5143    for (i = 0; i < 4; i++) {
5144      newspline->ctrl[i].x = copyspline->ctrl[i].x;
5145      newspline->ctrl[i].y = copyspline->ctrl[i].y;
5146    }
5147    for (i = 0; i < INTSEGS; i++) {
5148       newspline->points[i].x = copyspline->points[i].x;
5149       newspline->points[i].y = copyspline->points[i].y;
5150    }
5151    newspline->passed = NULL;
5152    copyalleparams((genericptr)newspline, (genericptr)copyspline);
5153 }
5154 
5155 /*----------------------------------------------*/
5156 /* Copy a path 					*/
5157 /*----------------------------------------------*/
5158 
pathcopy(pathptr newpath,pathptr copypath)5159 void pathcopy(pathptr newpath, pathptr copypath)
5160 {
5161    genericptr *ggen;
5162    splineptr *newspline, copyspline;
5163    polyptr *newpoly, copypoly;
5164 
5165    newpath->style = copypath->style;
5166    newpath->color = copypath->color;
5167    newpath->width = copypath->width;
5168    newpath->parts = 0;
5169    newpath->passed = NULL;
5170    copyalleparams((genericptr)newpath, (genericptr)copypath);
5171    newpath->plist = (genericptr *)malloc(copypath->parts * sizeof(genericptr));
5172 
5173    for (ggen = copypath->plist; ggen < copypath->plist + copypath->parts; ggen++) {
5174       switch (ELEMENTTYPE(*ggen)) {
5175 	 case POLYGON:
5176 	    copypoly = TOPOLY(ggen);
5177   	    NEW_POLY(newpoly, newpath);
5178 	    polycopy(*newpoly, copypoly);
5179 	    break;
5180 	 case SPLINE:
5181 	    copyspline = TOSPLINE(ggen);
5182   	    NEW_SPLINE(newspline, newpath);
5183 	    splinecopy(*newspline, copyspline);
5184 	    break;
5185       }
5186    }
5187 }
5188 
5189 /*--------------------------------------------------------------*/
5190 /* Copy an object instance					*/
5191 /*--------------------------------------------------------------*/
5192 
instcopy(objinstptr newobj,objinstptr copyobj)5193 void instcopy(objinstptr newobj, objinstptr copyobj)
5194 {
5195    newobj->position.x = copyobj->position.x;
5196    newobj->position.y = copyobj->position.y;
5197    newobj->rotation = copyobj->rotation;
5198    newobj->scale = copyobj->scale;
5199    newobj->style = copyobj->style;
5200    newobj->thisobject = copyobj->thisobject;
5201    newobj->color = copyobj->color;
5202    newobj->bbox.lowerleft.x = copyobj->bbox.lowerleft.x;
5203    newobj->bbox.lowerleft.y = copyobj->bbox.lowerleft.y;
5204    newobj->bbox.width = copyobj->bbox.width;
5205    newobj->bbox.height = copyobj->bbox.height;
5206 
5207    newobj->passed = NULL;
5208    copyalleparams((genericptr)newobj, (genericptr)copyobj);
5209 
5210    newobj->params = NULL;
5211    copyparams(newobj, copyobj);
5212 
5213    /* If the parameters are the same, the bounding box should be, too. */
5214    if (copyobj->schembbox != NULL) {
5215       newobj->schembbox = (BBox *)malloc(sizeof(BBox));
5216       newobj->schembbox->lowerleft.x = copyobj->schembbox->lowerleft.x;
5217       newobj->schembbox->lowerleft.y = copyobj->schembbox->lowerleft.y;
5218       newobj->schembbox->width = copyobj->schembbox->width;
5219       newobj->schembbox->height = copyobj->schembbox->height;
5220    }
5221    else
5222       newobj->schembbox = NULL;
5223 }
5224 
5225 /*--------------------------------------------------------------*/
5226 /* The method for removing objects from a list is to add the	*/
5227 /* value REMOVE_TAG to the type of each object needing to be	*/
5228 /* removed, and then calling this routine.			*/
5229 /*--------------------------------------------------------------*/
5230 
delete_tagged(objinstptr thisinst)5231 void delete_tagged(objinstptr thisinst) {
5232    Boolean tagged = True;
5233    objectptr thisobject, delobj;
5234    genericptr *pgen;
5235    short *sobj, stmp;
5236 
5237    thisobject = thisinst->thisobject;
5238 
5239    while (tagged) {
5240       tagged = False;
5241       for (stmp = 0; stmp < thisobject->parts; stmp++) {
5242 	 pgen = thisobject->plist + stmp;
5243          if ((*pgen)->type & REMOVE_TAG) {
5244 	    (*pgen)->type &= (~REMOVE_TAG);
5245 	    tagged = True;
5246 
5247   	    delobj = delete_element(thisinst, &stmp, 1, 0);
5248 	    register_for_undo(XCF_Delete, UNDO_MORE, thisinst, delobj, 0);
5249 
5250 	    /* If we destroy elements in the current window, we need to	   */
5251 	    /* make sure that the selection list is updated appropriately. */
5252 
5253 	    if ((thisobject == topobject) && (areawin->selects > 0)) {
5254 	       for (sobj = areawin->selectlist; sobj < areawin->selectlist +
5255 			areawin->selects; sobj++)
5256 	          if (*sobj > stmp) (*sobj)--;
5257 	    }
5258 
5259 	    /* Also ensure that this element is not referenced in any	*/
5260 	    /* netlist.   If it is, remove it and mark the netlist as	*/
5261 	    /* invalid.							*/
5262 
5263 	    remove_netlist_element(thisobject, *pgen);
5264 	 }
5265       }
5266    }
5267    undo_finish_series();
5268 }
5269 
5270 /*-----------------------------------------------------------------*/
5271 /* For copying:  Check if an object is about to be placed directly */
5272 /* on top of the same object.  If so, delete the one underneath.   */
5273 /*-----------------------------------------------------------------*/
5274 
checkoverlap()5275 void checkoverlap()
5276 {
5277    short *sobj, *cobj;
5278    genericptr *sgen, *pgen;
5279    Boolean tagged = False;
5280 
5281    /* Work through the select list */
5282 
5283    for (sobj = areawin->selectlist; sobj < areawin->selectlist +
5284 	areawin->selects; sobj++) {
5285       sgen = topobject->plist + (*sobj);
5286 
5287       /* For each object being copied, compare it against every object	*/
5288       /* on the current page (except self).  Flag if it's the same.	*/
5289 
5290       for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts;
5291 		pgen++) {
5292 	 if (pgen == sgen) continue;
5293 	 if (compare_single(sgen, pgen)) {
5294 	    /* Make sure that this object is not part of the selection, */
5295 	    /* else chaos will reign.					*/
5296 	    for (cobj = areawin->selectlist; cobj < areawin->selectlist +
5297 			areawin->selects; cobj++) {
5298 	       if (pgen == topobject->plist + (*cobj)) break;
5299 	    }
5300 	    /* Tag it for future deletion and prevent further compares */
5301 	    if (cobj == areawin->selectlist + areawin->selects) {
5302 	       tagged = True;
5303 	       (*pgen)->type |= REMOVE_TAG;
5304 	   }
5305 	 }
5306       }
5307    }
5308    if (tagged) {
5309       /* Delete the tagged elements */
5310       Wprintf("Duplicate object deleted");
5311       delete_tagged(areawin->topinstance);
5312       incr_changes(topobject);
5313    }
5314 }
5315 
5316 /*--------------------------------------------------------------*/
5317 /* Direct placement of elements.  Assumes that the selectlist	*/
5318 /* contains all the elements to be positioned.  "deltax" and	*/
5319 /* "deltay" are relative x and y positions to move the		*/
5320 /* elements.							*/
5321 /*--------------------------------------------------------------*/
5322 
placeselects(short deltax,short deltay,XPoint * userpt)5323 void placeselects(short deltax, short deltay, XPoint *userpt)
5324 {
5325    short *dragselect;
5326    XPoint newpos, *ppt;
5327    float rot;
5328    short closest;
5329    Boolean doattach;
5330    genericptr *pgen;
5331    polyptr cpoly;
5332 
5333    doattach = ((userpt == NULL) || (areawin->attachto < 0)) ? FALSE : TRUE;
5334 
5335    /* under attachto condition, keep element attached to */
5336    /* the attachto element.				 */
5337 
5338    if (doattach) findattach(&newpos, &rot, userpt);
5339 
5340 #ifdef HAVE_CAIRO
5341 #else
5342    areawin->clipped = -1;	/* Prevent clipping */
5343 #endif /* HAVE_CAIRO */
5344 
5345    for (dragselect = areawin->selectlist; dragselect < areawin->selectlist
5346       + areawin->selects; dragselect++) {
5347 
5348       switch(SELECTTYPE(dragselect)) {
5349          case OBJINST: {
5350 	    objinstptr draginst = SELTOOBJINST(dragselect);
5351 
5352 	    if (doattach) {
5353 	       draginst->position.x = newpos.x;
5354 	       draginst->position.y = newpos.y;
5355 	       while (rot >= 360.0) rot -= 360.0;
5356 	       while (rot < 0.0) rot += 360.0;
5357 	       draginst->rotation = rot;
5358 	    }
5359 	    else {
5360 	       draginst->position.x += deltax;
5361 	       draginst->position.y += deltay;
5362 	    }
5363 
5364 	 } break;
5365          case GRAPHIC: {
5366 	    graphicptr dragg = SELTOGRAPHIC(dragselect);
5367 	    dragg->position.x += deltax;
5368 	    dragg->position.y += deltay;
5369 	 } break;
5370 	 case LABEL: {
5371 	    labelptr draglabel = SELTOLABEL(dragselect);
5372 	    if (doattach) {
5373 	       draglabel->position.x = newpos.x;
5374 	       draglabel->position.y = newpos.y;
5375 	       draglabel->rotation = rot;
5376 	    }
5377 	    else {
5378 	       draglabel->position.x += deltax;
5379 	       draglabel->position.y += deltay;
5380 	    }
5381 	 } break;
5382 	 case PATH: {
5383 	    pathptr dragpath = SELTOPATH(dragselect);
5384 	    genericptr *pathlist;
5385 
5386 	    if (doattach) {
5387 	       XPoint *pdelta = pathclosepoint(dragpath, &newpos);
5388 	       deltax = newpos.x - pdelta->x;
5389 	       deltay = newpos.y - pdelta->y;
5390 	    }
5391 	    for (pathlist = dragpath->plist;  pathlist < dragpath->plist
5392 		  + dragpath->parts; pathlist++) {
5393 	       movepoints(pathlist, deltax, deltay);
5394 	    }
5395 	 } break;
5396 	 case POLYGON: {
5397 	    polyptr dragpoly = SELTOPOLY(dragselect);
5398 	    pointlist dragpoints;
5399 
5400 	    /* if (dragpoly->cycle != NULL) continue; */
5401 	    if (doattach) {
5402 	       closest = closepoint(dragpoly, &newpos);
5403 	       deltax = newpos.x - dragpoly->points[closest].x;
5404 	       deltay = newpos.y - dragpoly->points[closest].y;
5405 	    }
5406 	    for (dragpoints = dragpoly->points; dragpoints < dragpoly->points
5407 	           + dragpoly->number; dragpoints++) {
5408 	       dragpoints->x += deltax;
5409 	       dragpoints->y += deltay;
5410 	    }
5411 	 } break;
5412 	 case SPLINE: {
5413 	    splineptr dragspline = SELTOSPLINE(dragselect);
5414 	    short j;
5415 	    fpointlist dragpoints;
5416 
5417 	    /* if (dragspline->cycle != NULL) continue; */
5418 	    if (doattach) {
5419 	       closest = (wirelength(&dragspline->ctrl[0], &newpos)
5420 		  > wirelength(&dragspline->ctrl[3], &newpos)) ? 3 : 0;
5421 	       deltax = newpos.x - dragspline->ctrl[closest].x;
5422 	       deltay = newpos.y - dragspline->ctrl[closest].y;
5423 	    }
5424 	    for (dragpoints = dragspline->points; dragpoints < dragspline->
5425 		   points + INTSEGS; dragpoints++) {
5426 	       dragpoints->x += deltax;
5427 	       dragpoints->y += deltay;
5428 	    }
5429 	    for (j = 0; j < 4; j++) {
5430 	       dragspline->ctrl[j].x += deltax;
5431 	       dragspline->ctrl[j].y += deltay;
5432 	    }
5433 	 } break;
5434 	 case ARC: {
5435 	    arcptr dragarc = SELTOARC(dragselect);
5436 	    fpointlist dragpoints;
5437 
5438 	    if (doattach) {
5439 	       deltax = newpos.x - dragarc->position.x;
5440 	       deltay = newpos.y - dragarc->position.y;
5441 	    }
5442 	    dragarc->position.x += deltax;
5443 	    dragarc->position.y += deltay;
5444 	    for (dragpoints = dragarc->points; dragpoints < dragarc->
5445 		 points + dragarc->number; dragpoints++) {
5446 	       dragpoints->x += deltax;
5447 	       dragpoints->y += deltay;
5448 	    }
5449          } break;
5450       }
5451    }
5452 
5453    if (areawin->pinattach) {
5454       for (pgen = topobject->plist; pgen < topobject->plist +
5455 		topobject->parts; pgen++) {
5456          if (ELEMENTTYPE(*pgen) == POLYGON) {
5457 	    cpoly = TOPOLY(pgen);
5458 	    if (cpoly->cycle != NULL) {
5459 	       ppt = cpoly->points + cpoly->cycle->number;
5460 	       newpos.x = ppt->x + deltax;
5461 	       newpos.y = ppt->y + deltay;
5462 	       if (areawin->manhatn)
5463 		  manhattanize(&newpos, cpoly, cpoly->cycle->number, FALSE);
5464 	       ppt->x = newpos.x;
5465 	       ppt->y = newpos.y;
5466 	    }
5467 	 }
5468       }
5469    }
5470 
5471    move_mode_draw(xcDRAW_EDIT, NULL);
5472 #ifdef HAVE_CAIRO
5473 #else
5474    areawin->clipped = 0;
5475 #endif /* HAVE_CAIRO */
5476 }
5477 
5478 /*----------------------------------------------------------------------*/
5479 /* Copy handler.  Assumes that the selectlist contains the elements	*/
5480 /* to be copied, and that the initial position of the copy is held	*/
5481 /* in areawin->save.							*/
5482 /*----------------------------------------------------------------------*/
5483 
createcopies()5484 void createcopies()
5485 {
5486    short *selectobj;
5487 
5488    if (!checkselect_draw(ALL_TYPES, True)) return;
5489    u2u_snap(&areawin->save);
5490    for (selectobj = areawin->selectlist; selectobj < areawin->selectlist
5491 		+ areawin->selects; selectobj++) {
5492 
5493       /* Cycles will not be used for copy mode:  remove them */
5494       removecycle(topobject->plist + (*selectobj));
5495 
5496       switch(SELECTTYPE(selectobj)) {
5497          case LABEL: { /* copy label */
5498 	    labelptr copytext = SELTOLABEL(selectobj);
5499 	    labelptr *newtext;
5500 
5501 	    NEW_LABEL(newtext, topobject);
5502 	    labelcopy(*newtext, copytext);
5503          } break;
5504          case OBJINST: { /* copy object instance */
5505 	    objinstptr copyobj = SELTOOBJINST(selectobj);
5506 	    objinstptr *newobj;
5507 	    NEW_OBJINST(newobj, topobject);
5508 	    instcopy(*newobj, copyobj);
5509          } break;
5510          case GRAPHIC: { /* copy graphic instance */
5511 	    graphicptr copyg = SELTOGRAPHIC(selectobj);
5512 	    graphicptr *newg;
5513 	    NEW_GRAPHIC(newg, topobject);
5514 	    graphiccopy(*newg, copyg);
5515          } break;
5516 	 case PATH: { /* copy path */
5517 	    pathptr copypath = SELTOPATH(selectobj);
5518 	    pathptr *newpath;
5519 	    NEW_PATH(newpath, topobject);
5520 	    pathcopy(*newpath, copypath);
5521 	 } break;
5522 	 case ARC: { /* copy arc */
5523 	    arcptr copyarc = SELTOARC(selectobj);
5524 	    arcptr *newarc;
5525    	    NEW_ARC(newarc, topobject);
5526 	    arccopy(*newarc, copyarc);
5527          } break;
5528          case POLYGON: { /* copy polygons */
5529             polyptr copypoly = SELTOPOLY(selectobj);
5530             polyptr *newpoly;
5531    	    NEW_POLY(newpoly, topobject);
5532 	    polycopy(*newpoly, copypoly);
5533          } break;
5534 	 case SPLINE: { /* copy spline */
5535 	    splineptr copyspline = SELTOSPLINE(selectobj);
5536 	    splineptr *newspline;
5537    	    NEW_SPLINE(newspline, topobject);
5538 	    splinecopy(*newspline, copyspline);
5539 	 } break;
5540       }
5541 
5542       /* change selection from the old to the new object */
5543 
5544       *selectobj = topobject->parts - 1;
5545    }
5546 }
5547 
5548 /*--------------------------------------------------------------*/
5549 /* Function which initiates interactive placement of copied	*/
5550 /* elements.							*/
5551 /*--------------------------------------------------------------*/
5552 
copydrag()5553 void copydrag()
5554 {
5555    if (areawin->selects > 0) {
5556       move_mode_draw(xcDRAW_INIT, NULL);
5557       if (eventmode == NORMAL_MODE) {
5558 	 XDefineCursor(dpy, areawin->window, COPYCURSOR);
5559 	 eventmode = COPY_MODE;
5560 #ifdef TCL_WRAPPER
5561          Tk_CreateEventHandler(areawin->area, PointerMotionMask |
5562 		ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5563 #else
5564          XtAddEventHandler(areawin->area, PointerMotionMask |
5565 		ButtonMotionMask, False, (XtEventHandler)xlib_drag,
5566 		NULL);
5567 #endif
5568       }
5569       select_invalidate_netlist();
5570    }
5571 }
5572 
5573 /*-----------------------------------------------------------*/
5574 /* Copy handler for copying from a button push or key event. */
5575 /*-----------------------------------------------------------*/
5576 
copy_op(int op,int x,int y)5577 void copy_op(int op, int x, int y)
5578 {
5579    if (op == XCF_Copy) {
5580       window_to_user(x, y, &areawin->save);
5581       createcopies();	/* This function does all the hard work */
5582       copydrag();		/* Start interactive placement */
5583    }
5584    else {
5585       eventmode = NORMAL_MODE;
5586       areawin->attachto = -1;
5587       W3printf("");
5588 #ifdef TCL_WRAPPER
5589       xcRemoveEventHandler(areawin->area, PointerMotionMask |
5590 		ButtonMotionMask, False, (xcEventHandler)xctk_drag,
5591 		NULL);
5592 #else
5593       xcRemoveEventHandler(areawin->area, PointerMotionMask |
5594 		ButtonMotionMask, False, (xcEventHandler)xlib_drag,
5595 		NULL);
5596 #endif
5597       XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5598       u2u_snap(&areawin->save);
5599       if (op == XCF_Cancel) {
5600 	 move_mode_draw(xcDRAW_EMPTY, NULL);
5601 	 delete_noundo(NORMAL);
5602       }
5603       else if (op == XCF_Finish_Copy) {
5604 	 move_mode_draw(xcDRAW_FINAL, NULL);
5605 	 /* If selected objects are the only ones on the page, */
5606 	 /* then do a full bbox calculation.			  */
5607 	 if (topobject->parts == areawin->selects)
5608 	    calcbbox(areawin->topinstance);
5609 	 else
5610 	    calcbboxselect();
5611 	 checkoverlap();
5612 	 register_for_undo(XCF_Copy, UNDO_MORE, areawin->topinstance,
5613 			areawin->selectlist, areawin->selects);
5614 	 unselect_all();
5615          incr_changes(topobject);
5616       }
5617       else {	 /* XCF_Continue_Copy */
5618 	 move_mode_draw(xcDRAW_FINAL, NULL);
5619 	 if (topobject->parts == areawin->selects)
5620 	    calcbbox(areawin->topinstance);
5621 	 else
5622 	    calcbboxselect();
5623 	 checkoverlap();
5624 	 register_for_undo(XCF_Copy, UNDO_DONE, areawin->topinstance,
5625 			areawin->selectlist, areawin->selects);
5626          createcopies();
5627          copydrag();		/* Start interactive placement again */
5628          incr_changes(topobject);
5629       }
5630    }
5631 }
5632 
5633 /*----------------------------------------------*/
5634 /* Check for more than one button being pressed	*/
5635 /*----------------------------------------------*/
5636 
checkmultiple(XButtonEvent * event)5637 Boolean checkmultiple(XButtonEvent *event)
5638 {
5639    int state = Button1Mask | Button2Mask | Button3Mask |
5640 		Button4Mask | Button5Mask;
5641    state &= event->state;
5642    /* ((x - 1) & x) is always non-zero if x has more than one bit set */
5643    return (((state - 1) & state) == 0) ? False : True;
5644 }
5645 
5646 /*----------------------------------------------------------------------*/
5647 /* Operation continuation---dependent upon the ongoing operation.	*/
5648 /* This operation only pertains to a few event modes for which		*/
5649 /* continuation	of action makes sense---drawing wires (polygons), and	*/
5650 /* editing polygons, arcs, splines, and paths.				*/
5651 /*----------------------------------------------------------------------*/
5652 
continue_op(int op,int x,int y)5653 void continue_op(int op, int x, int y)
5654 {
5655    XPoint ppos;
5656 
5657    if (eventmode != EARC_MODE && eventmode != ARC_MODE) {
5658       window_to_user(x, y, &areawin->save);
5659    }
5660    snap(x, y, &ppos);
5661    printpos(ppos.x, ppos.y);
5662 
5663    switch(eventmode) {
5664       case(COPY_MODE):
5665 	 copy_op(op, x, y);
5666 	 break;
5667       case(WIRE_MODE):
5668 	 wire_op(op, x, y);
5669 	 break;
5670       case(EPATH_MODE): case(EPOLY_MODE): case (ARC_MODE):
5671       case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE):
5672 	 path_op(*(EDITPART), op, x, y);
5673 	 break;
5674       case(EINST_MODE):
5675 	 inst_op(*(EDITPART), op, x, y);
5676 	 break;
5677       case(BOX_MODE):
5678 	 finish_op(XCF_Finish_Element, x, y);
5679 	 break;
5680       default:
5681 	 break;
5682    }
5683 }
5684 
5685 /*--------------------------------------------------------------*/
5686 /* Finish or cancel an operation.  This forces a return to	*/
5687 /* "normal" mode, with whatever other side effects are caused	*/
5688 /* by the operation.						*/
5689 /*--------------------------------------------------------------*/
5690 
finish_op(int op,int x,int y)5691 void finish_op(int op, int x, int y)
5692 {
5693    labelptr curlabel;
5694    int libnum;
5695    XPoint snappt, boxpts[4];
5696    float fscale;
5697 
5698    if (eventmode != EARC_MODE && eventmode != ARC_MODE) {
5699       window_to_user(x, y, &areawin->save);
5700    }
5701    switch(eventmode) {
5702       case(EPATH_MODE): case(BOX_MODE): case(EPOLY_MODE): case (ARC_MODE):
5703 	     case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE):
5704 	 path_op(*(EDITPART), op, x, y);
5705 	 break;
5706 
5707       case(EINST_MODE):
5708 	 inst_op(*(EDITPART), op, x, y);
5709 	 break;
5710 
5711       case (FONTCAT_MODE):
5712       case (EFONTCAT_MODE):
5713 	 fontcat_op(op, x, y);
5714 	 eventmode = (eventmode == FONTCAT_MODE) ? TEXT_MODE : ETEXT_MODE;
5715 	 text_mode_draw(xcDRAW_INIT, TOLABEL(EDITPART));
5716 	 XDefineCursor (dpy, areawin->window, TEXTPTR);
5717 	 break;
5718 
5719       case(ASSOC_MODE):
5720       case(CATALOG_MODE):
5721 	 catalog_op(op, x, y);
5722 	 break;
5723 
5724       case(WIRE_MODE):
5725 	 wire_op(op, x, y);
5726 	 break;
5727 
5728       case(COPY_MODE):
5729 	 copy_op(op, x, y);
5730 	 break;
5731 
5732       case(TEXT_MODE):
5733 	 curlabel = TOLABEL(EDITPART);
5734 	 if (op == XCF_Cancel) {
5735 	     redrawtext(curlabel);
5736    	     areawin->redraw_needed = False; /* ignore previous requests */
5737 	     text_mode_draw(xcDRAW_EMPTY, curlabel);
5738 	     freelabel(curlabel->string);
5739 	     free(curlabel);
5740 	     topobject->parts--;
5741 	     curlabel = NULL;
5742 	 }
5743 	 else {
5744 	    singlebbox(EDITPART);
5745 	    incr_changes(topobject);
5746             select_invalidate_netlist();
5747 	    text_mode_draw(xcDRAW_FINAL, curlabel);
5748 	 }
5749 	 setdefaultfontmarks();
5750 	 eventmode = NORMAL_MODE;
5751          break;
5752 
5753       case(ETEXT_MODE): case(CATTEXT_MODE):
5754 	 curlabel = TOLABEL(EDITPART);
5755 	 if (op == XCF_Cancel) {
5756 	    /* restore the original text */
5757 	    undrawtext(curlabel);
5758 	    undo_finish_series();
5759 	    undo_action();
5760 	    redrawtext(curlabel);
5761    	    areawin->redraw_needed = False; /* ignore previous requests */
5762 	    text_mode_draw(xcDRAW_EMPTY, curlabel);
5763 	    if (eventmode == CATTEXT_MODE) eventmode = CATALOG_MODE;
5764 	    W3printf("");
5765 	    setdefaultfontmarks();
5766 	 }
5767 	 else textreturn();  /* Generate "return" key character */
5768 	 areawin->textend = 0;
5769 	 break;
5770 
5771       case(CATMOVE_MODE):
5772 	 u2u_snap(&areawin->save);
5773 #ifdef TCL_WRAPPER
5774 	 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5775 		PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5776 #else
5777 	 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5778 		PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5779 		NULL);
5780 #endif
5781 	 if (op == XCF_Cancel) {
5782 	    /* Just regenerate the library where we started */
5783 	    if (areawin->selects >= 1) {
5784 	       objinstptr selinst = SELTOOBJINST(areawin->selectlist);
5785 	       libnum = libfindobject(selinst->thisobject, NULL);
5786 	       if (libnum >= 0)
5787 		  composelib(libnum + LIBRARY);
5788 	    }
5789 	 }
5790 	 else {
5791 	    catmove(x, y);
5792 	 }
5793 	 clearselects();
5794 	 eventmode = CATALOG_MODE;
5795 	 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5796 	 break;
5797 
5798       case(MOVE_MODE):
5799 	 u2u_snap(&areawin->save);
5800 #ifdef TCL_WRAPPER
5801 	 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5802 		PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5803 #else
5804 	 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5805 		PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5806 		NULL);
5807 #endif
5808 	 if (op == XCF_Cancel) {
5809 	    /* If we came from the library with an object instance, in	*/
5810 	    /* MOVE_MODE, then "cancel" should delete the element.	*/
5811 	    /* Otherwise, put the position of the element back to what	*/
5812 	    /* it was before we started the move.  The difference is	*/
5813 	    /* indicated by the value of areawin->editpart.		*/
5814 
5815 	    if ((areawin->selects > 0) && (*areawin->selectlist == topobject->parts))
5816 	       delete_noundo(NORMAL);
5817 	    else
5818 	       placeselects(areawin->origin.x - areawin->save.x,
5819 			areawin->origin.y - areawin->save.y, NULL);
5820 	    clearselects();
5821 	    drawarea(NULL, NULL, NULL);
5822 	 }
5823 	 else {
5824 	    if (areawin->selects > 0) {
5825 	       register_for_undo(XCF_Move,
5826 			/* (was_preselected) ? UNDO_DONE : UNDO_MORE, */
5827 			UNDO_MORE,
5828 			areawin->topinstance,
5829 			(int)(areawin->save.x - areawin->origin.x),
5830 			(int)(areawin->save.y - areawin->origin.y));
5831 	       pwriteback(areawin->topinstance);
5832 	       incr_changes(topobject);
5833 	       select_invalidate_netlist();
5834 	       unselect_all();		/* The way it used to be. . . */
5835 	    }
5836 	    W3printf("");
5837 	    /* full calc needed: move may shrink bbox */
5838 	    calcbbox(areawin->topinstance);
5839 	    checkoverlap();
5840 	    /* if (!was_preselected) clearselects(); */
5841 	 }
5842 	 areawin->attachto = -1;
5843 	 break;
5844 
5845       case(RESCALE_MODE):
5846 
5847 #ifdef TCL_WRAPPER
5848 	 Tk_DeleteEventHandler(areawin->area, PointerMotionMask |
5849 		ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5850 #else
5851 	 xcRemoveEventHandler(areawin->area, PointerMotionMask |
5852 		ButtonMotionMask, FALSE, (xcEventHandler)xlib_drag,
5853 		NULL);
5854 #endif
5855 	 rescale_mode_draw(xcDRAW_FINAL, NULL);
5856 	 if (op != XCF_Cancel) {
5857 	    XPoint newpoints[5];
5858 	    fscale = UGetRescaleBox(&areawin->save, newpoints);
5859 	    if (fscale != 0.) {
5860 	       elementrescale(fscale);
5861 	       areawin->redraw_needed = True;
5862 	    }
5863 	 }
5864 	 eventmode = NORMAL_MODE;
5865 	 break;
5866 
5867       case(SELAREA_MODE):
5868 	 selarea_mode_draw(xcDRAW_FINAL, NULL);
5869 
5870 #ifdef TCL_WRAPPER
5871 	 Tk_DeleteEventHandler(areawin->area, ButtonMotionMask |
5872 		PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5873 #else
5874 	 xcRemoveEventHandler(areawin->area, ButtonMotionMask |
5875 		PointerMotionMask, FALSE, (xcEventHandler)xlib_drag,
5876 		NULL);
5877 #endif
5878 	 /* Zero-width boxes act like a normal selection.  Otherwise,	*/
5879  	 /* proceed with the area select.				*/
5880 
5881 	 if ((areawin->origin.x == areawin->save.x) &&
5882 	     (areawin->origin.y == areawin->save.y))
5883             select_add_element(ALL_TYPES);
5884 	 else {
5885 	    boxpts[0] = areawin->origin;
5886 	    boxpts[1].x = areawin->save.x;
5887 	    boxpts[1].y = areawin->origin.y;
5888 	    boxpts[2] = areawin->save;
5889 	    boxpts[3].x = areawin->origin.x;
5890 	    boxpts[3].y = areawin->save.y;
5891 	    selectarea(topobject, boxpts, 0);
5892 	 }
5893 	 break;
5894 
5895       case(PAN_MODE):
5896 	 u2u_snap(&areawin->save);
5897 
5898 #ifdef TCL_WRAPPER
5899 	 Tk_DeleteEventHandler(areawin->area, PointerMotionMask |
5900 		ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL);
5901 #else
5902          xcRemoveEventHandler(areawin->area, PointerMotionMask |
5903 		ButtonMotionMask, False, (xcEventHandler)xlib_drag,
5904 		NULL);
5905 #endif
5906 	 areawin->panx = areawin->pany = 0;
5907 	 if (op != XCF_Cancel)
5908 	    panbutton((u_int) 5, (areawin->width >> 1) - (x - areawin->origin.x),
5909 			(areawin->height >> 1) - (y - areawin->origin.y), 0);
5910 	 break;
5911       default:
5912 	 break;
5913    }
5914 
5915    /* Remove any selections */
5916    if ((eventmode == SELAREA_MODE) || (eventmode == PAN_MODE)
5917 		|| (eventmode == MOVE_MODE))
5918    {
5919       eventmode = NORMAL_MODE;
5920       areawin->redraw_needed = True;
5921    }
5922    else if (eventmode != MOVE_MODE && eventmode != EPATH_MODE &&
5923 		eventmode != EPOLY_MODE && eventmode != ARC_MODE &&
5924 		eventmode != EARC_MODE && eventmode != SPLINE_MODE &&
5925 		eventmode != ESPLINE_MODE && eventmode != WIRE_MODE &&
5926 		eventmode != ETEXT_MODE && eventmode != TEXT_MODE) {
5927       unselect_all();
5928    }
5929 
5930    if (eventmode == NORMAL_MODE) {
5931 
5932       /* Return any highlighted networks to normal */
5933       highlightnetlist(topobject, areawin->topinstance, 0);
5934 
5935       XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5936    }
5937 
5938    snap(x, y, &snappt);
5939    printpos(snappt.x, snappt.y);
5940 }
5941 
5942 /*--------------------------------------------------------------*/
5943 /* Edit operations for instances.  This is used to allow	*/
5944 /* numeric parameters to be adjusted from the hierarchical	*/
5945 /* level above, shielding the the unparameterized parts from	*/
5946 /* change.							*/
5947 /*--------------------------------------------------------------*/
5948 
inst_op(genericptr editpart,int op,int x,int y)5949 void inst_op(genericptr editpart, int op, int x, int y)
5950 {
5951    UNUSED(editpart); UNUSED(op); UNUSED(x); UNUSED(y);
5952 }
5953 
5954 /*--------------------------------------------------------------*/
5955 /* Operations for path components				*/
5956 /*--------------------------------------------------------------*/
5957 
path_op(genericptr editpart,int op,int x,int y)5958 void path_op(genericptr editpart, int op, int x, int y)
5959 {
5960    polyptr newpoly;
5961    splineptr newspline;
5962    Boolean donecycles = False;
5963    UNUSED(x); UNUSED(y);
5964 
5965    /* Don't allow point cycling in a multi-part edit.	*/
5966    /* Allowing it is just confusing.  Instead, we treat	*/
5967    /* button 1 (cycle) like button 2 (finish).		*/
5968    if (op == XCF_Continue_Element && areawin->selects > 1)
5969       op = XCF_Finish_Element;
5970 
5971    switch(ELEMENTTYPE(editpart)) {
5972       case (PATH): {
5973 	 pathptr newpath = (pathptr)editpart;
5974          short dotrack = True;
5975          pathptr editpath;
5976 
5977          areawin->attachto = -1;
5978 
5979 	 if (op != XCF_Continue_Element) {
5980 	    dotrack = False;
5981 	 }
5982 	 if (op == XCF_Continue_Element) {
5983 	    nextpathcycle(newpath, 1);
5984 	    patheditpush(newpath);
5985 	 }
5986 	 else if (op == XCF_Finish_Element) {
5987    	    path_mode_draw(xcDRAW_FINAL, newpath);
5988 	    incr_changes(topobject);
5989 	 }
5990 	 else {		/* restore previous path from edit stack */
5991 	    free_single((genericptr)newpath);
5992 	    if (areawin->editstack->parts > 0) {
5993 	       if (op == XCF_Cancel) {
5994 	          editpath = TOPATH(areawin->editstack->plist);
5995 		  pathcopy(newpath, editpath);
5996 	          reset(areawin->editstack, NORMAL);
5997 	       }
5998 	       else {
5999 	          editpath = TOPATH(areawin->editstack->plist +
6000 				areawin->editstack->parts - 1);
6001 		  pathcopy(newpath, editpath);
6002 		  free_single((genericptr)editpath);
6003 		  free(editpath);
6004 	          areawin->editstack->parts--;
6005 	       }
6006 	       if (areawin->editstack->parts > 0) {
6007 		  dotrack = True;
6008 	          nextpathcycle(newpath, 1);
6009    	          path_mode_draw(xcDRAW_EDIT, newpath);
6010 	       }
6011 	       else {
6012 	       	  XPoint warppt;
6013 		  user_to_window(areawin->origin, &warppt);
6014 		  warppointer(warppt.x, warppt.y);
6015    	          path_mode_draw(xcDRAW_FINAL, newpath);
6016 	       }
6017 	    }
6018 	    else {
6019    	       path_mode_draw(xcDRAW_EMPTY, newpath);
6020 	       topobject->parts--;
6021 	       free_single((genericptr)newpath);
6022 	       free(newpath);
6023 	    }
6024 	 }
6025 	 pwriteback(areawin->topinstance);
6026 
6027 	 if (!dotrack) {
6028 	    /* Free the editstack */
6029 	    reset(areawin->editstack, NORMAL);
6030 	    xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6031 			(xcEventHandler)trackelement, NULL);
6032 	    eventmode = NORMAL_MODE;
6033 	    donecycles = True;
6034 	 }
6035       } break;
6036 
6037       case (POLYGON): {
6038 	 if (eventmode == BOX_MODE) {
6039             polyptr   newbox;
6040 
6041             newbox = (polyptr)editpart;
6042 
6043             /* prevent length and/or width zero boxes */
6044             if (newbox->points->x != (newbox->points + 2)->x && (newbox->points
6045 	         + 1)->y != (newbox->points + 3)->y) {
6046 	       if (op != XCF_Cancel) {
6047    	    	  poly_mode_draw(xcDRAW_FINAL, newbox);
6048 	          incr_changes(topobject);
6049 		  if (!nonnetwork(newbox)) invalidate_netlist(topobject);
6050 		  register_for_undo(XCF_Box, UNDO_MORE, areawin->topinstance,
6051 					newbox);
6052 	       }
6053 	       else {
6054    	    	  poly_mode_draw(xcDRAW_EMPTY, newbox);
6055 		  free_single((genericptr)newbox);
6056 		  free(newbox);
6057 	          topobject->parts--;
6058 
6059 	       }
6060             }
6061 	    else {
6062    	       poly_mode_draw(xcDRAW_EMPTY, newbox);
6063 	       free_single((genericptr)newbox);
6064 	       free(newbox);
6065 	       topobject->parts--;
6066 	    }
6067 
6068             xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6069                (xcEventHandler)trackbox, NULL);
6070             eventmode = NORMAL_MODE;
6071          }
6072 	 else {   /* EPOLY_MODE */
6073             polyptr      editpoly;
6074             short        dotrack = True;
6075 
6076             newpoly = (polyptr)editpart;
6077             areawin->attachto = -1;
6078 
6079             if (op != XCF_Continue_Element) {
6080 	       dotrack = False;
6081             }
6082 
6083             if (op == XCF_Continue_Element) {
6084 	       nextpolycycle(&newpoly, 1);
6085 	       polyeditpush(newpoly);
6086 	    }
6087             else if (op == XCF_Finish_Element) {
6088 
6089 	       /* Check for degenerate polygons (all points the same). */
6090 	       int i;
6091 	       for (i = 1; i < newpoly->number; i++)
6092 		  if ((newpoly->points[i].x != newpoly->points[i - 1].x) ||
6093 			(newpoly->points[i].y != newpoly->points[i - 1].y))
6094 		     break;
6095 	       if (i == newpoly->number) {
6096    	          poly_mode_draw(xcDRAW_EMPTY, newpoly);
6097 		  /* Remove this polygon with the standard delete	*/
6098 		  /* method (saves polygon on undo stack).		*/
6099 		  newpoly->type |= REMOVE_TAG;
6100 	          delete_tagged(areawin->topinstance);
6101 	       }
6102 	       else {
6103    	          poly_mode_draw(xcDRAW_FINAL, newpoly);
6104                   if (!nonnetwork(newpoly)) invalidate_netlist(topobject);
6105 		  incr_changes(topobject);
6106 	       }
6107 	    }
6108             else {
6109 	       XPoint warppt;
6110 	       free_single((genericptr)newpoly);
6111 	       if (areawin->editstack->parts > 0) {
6112 	          if (op == XCF_Cancel) {
6113 		     editpoly = TOPOLY(areawin->editstack->plist);
6114 		     polycopy(newpoly, editpoly);
6115 	             reset(areawin->editstack, NORMAL);
6116 	          }
6117 		  else {
6118 		     editpoly = TOPOLY(areawin->editstack->plist +
6119 				areawin->editstack->parts - 1);
6120 		     polycopy(newpoly, editpoly);
6121 		     free_single((genericptr)editpoly);
6122 		     free(editpoly);
6123 	             areawin->editstack->parts--;
6124 		  }
6125 	          if (areawin->editstack->parts > 0) {
6126 		     dotrack = True;
6127 		     nextpolycycle(&newpoly, -1);
6128    	             poly_mode_draw(xcDRAW_EDIT, newpoly);
6129 	          }
6130 	          else {
6131          	     XcTopSetForeground(newpoly->color);
6132 		     user_to_window(areawin->origin, &warppt);
6133 		     warppointer(warppt.x, warppt.y);
6134    	             poly_mode_draw(xcDRAW_FINAL, newpoly);
6135 	          }
6136 	       }
6137 	       else {
6138    	          poly_mode_draw(xcDRAW_EMPTY, newpoly);
6139 	          topobject->parts--;
6140 		  free(newpoly);
6141 	       }
6142 	    }
6143 	    pwriteback(areawin->topinstance);
6144 
6145             if (!dotrack) {
6146 	       /* Free the editstack */
6147 	       reset(areawin->editstack, NORMAL);
6148 
6149                xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6150                    (xcEventHandler)trackelement, NULL);
6151                eventmode = NORMAL_MODE;
6152 	       donecycles = True;
6153             }
6154  	 }
6155       } break;
6156 
6157       case (ARC): {
6158          arcptr   newarc, editarc;
6159          short    dotrack = True;
6160 
6161 	 newarc = (arcptr)editpart;
6162 
6163 	 if (op != XCF_Continue_Element) {
6164 	    dotrack = False;
6165          }
6166 
6167 	 if (op == XCF_Continue_Element) {
6168 	    nextarccycle(&newarc, 1);
6169 	    arceditpush(newarc);
6170 	 }
6171 
6172          else if (op == XCF_Finish_Element) {
6173 	    dotrack = False;
6174 
6175             if (newarc->radius != 0 && newarc->yaxis != 0 &&
6176 		   (newarc->angle1 != newarc->angle2)) {
6177                XTopSetForeground(newarc->color);
6178 	       incr_changes(topobject);
6179                if (eventmode == ARC_MODE) {
6180 	          register_for_undo(XCF_Arc, UNDO_MORE, areawin->topinstance,
6181 				newarc);
6182 	       }
6183    	       arc_mode_draw(xcDRAW_FINAL, newarc);
6184 	    }
6185 	    else {
6186 
6187                /* Remove the record if the radius is zero.  If we were	*/
6188 	       /* creating the arc, just delete it;  it's as if it	*/
6189 	       /* never existed.  If we were editing an arc, use the	*/
6190 	       /* standard delete method (saves arc on undo stack).	*/
6191 
6192    	       arc_mode_draw(xcDRAW_EMPTY, newarc);
6193 	       if (eventmode == ARC_MODE) {
6194 	          free_single((genericptr)newarc);
6195 	          free(newarc);
6196 		  topobject->parts--;
6197 	       }
6198 	       else {
6199 	          newarc->type |= REMOVE_TAG;
6200 	          delete_tagged(areawin->topinstance);
6201 	       }
6202 	    }
6203 	 }
6204 	 else {	 /* Cancel: restore previous arc from edit stack */
6205 	    free_single((genericptr)newarc);
6206 	    if (areawin->editstack->parts > 0) {
6207 	       if (op == XCF_Cancel) {
6208 	          editarc = TOARC(areawin->editstack->plist);
6209 		  arccopy(newarc, editarc);
6210 		  copycycles(&(newarc->cycle), &(editarc->cycle));
6211 	          reset(areawin->editstack, NORMAL);
6212 	       }
6213 	       else {
6214 	          editarc = TOARC(areawin->editstack->plist +
6215 				areawin->editstack->parts - 1);
6216 		  arccopy(newarc, editarc);
6217 		  copycycles(&(newarc->cycle), &(editarc->cycle));
6218 		  free_single((genericptr)editarc);
6219 		  free(editarc);
6220 	          areawin->editstack->parts--;
6221 	       }
6222 	       if (areawin->editstack->parts > 0) {
6223 		  dotrack = True;
6224 	          nextarccycle(&newarc, -1);
6225    	       	  arc_mode_draw(xcDRAW_EDIT, newarc);
6226 	       }
6227 	       else {
6228 		  if (eventmode != ARC_MODE) {
6229 	       	     XPoint warppt;
6230 		     user_to_window(areawin->origin, &warppt);
6231 		     warppointer(warppt.x, warppt.y);
6232 		  }
6233    	       	  arc_mode_draw(xcDRAW_FINAL, newarc);
6234 	       }
6235 	    }
6236 	    else {
6237    	       arc_mode_draw(xcDRAW_EMPTY, newarc);
6238 	       topobject->parts--;
6239 	    }
6240          }
6241 	 pwriteback(areawin->topinstance);
6242 
6243 	 if (!dotrack) {
6244 	    /* Free the editstack */
6245 	    reset(areawin->editstack, NORMAL);
6246 
6247             xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6248                (xcEventHandler)trackarc, NULL);
6249             eventmode = NORMAL_MODE;
6250 	 }
6251       } break;
6252 
6253       case (SPLINE): {
6254 	 splineptr	editspline;
6255 	 short 		dotrack = True;
6256 
6257 	 newspline = (splineptr)editpart;
6258 
6259 	 if (op != XCF_Continue_Element) {
6260 	    dotrack = False;
6261 	 }
6262 
6263 	 if (op == XCF_Continue_Element) {
6264 	    /* Note:  we work backwards through spline control points.	*/
6265 	    /* The reason is that when creating a spline, the sudden	*/
6266 	    /* move from the endpoint to the startpoint	(forward	*/
6267 	    /* direction) is more disorienting than moving from the	*/
6268 	    /* endpoint to the endpoint's control point.		*/
6269 
6270 	    nextsplinecycle(&newspline, -1);
6271 	    splineeditpush(newspline);
6272 	 }
6273 
6274 	 /* unlikely but possible to create zero-length splines */
6275 	 else if (newspline->ctrl[0].x != newspline->ctrl[3].x ||
6276 	      	newspline->ctrl[0].x != newspline->ctrl[1].x ||
6277 	      	newspline->ctrl[0].x != newspline->ctrl[2].x ||
6278 	      	newspline->ctrl[0].y != newspline->ctrl[3].y ||
6279 	      	newspline->ctrl[0].y != newspline->ctrl[1].y ||
6280 	      	newspline->ctrl[0].y != newspline->ctrl[2].y) {
6281 	    if (op == XCF_Finish_Element) {
6282 	       incr_changes(topobject);
6283 	       if (eventmode == SPLINE_MODE) {
6284 		  register_for_undo(XCF_Spline, UNDO_MORE, areawin->topinstance,
6285 				newspline);
6286 	       }
6287 	       spline_mode_draw(xcDRAW_FINAL, newspline);
6288 	    }
6289 	    else {	/* restore previous spline from edit stack */
6290 	       free_single((genericptr)newspline);
6291 	       if (areawin->editstack->parts > 0) {
6292 		  if (op == XCF_Cancel) {
6293 	             editspline = TOSPLINE(areawin->editstack->plist);
6294 		     splinecopy(newspline, editspline);
6295 	             reset(areawin->editstack, NORMAL);
6296 		  }
6297 		  else {
6298 	             editspline = TOSPLINE(areawin->editstack->plist +
6299 				areawin->editstack->parts - 1);
6300 		     splinecopy(newspline, editspline);
6301 		     free_single((genericptr)editspline);
6302 		     free(editspline);
6303 	             areawin->editstack->parts--;
6304 		  }
6305 		  if (areawin->editstack->parts > 0) {
6306 		     dotrack = True;
6307 	             nextsplinecycle(&newspline, 1);
6308 		     spline_mode_draw(xcDRAW_EDIT, newspline);
6309 		  }
6310 		  else {
6311 		     if (eventmode != SPLINE_MODE) {
6312 	       		XPoint warppt;
6313 		        user_to_window(areawin->origin, &warppt);
6314 		        warppointer(warppt.x, warppt.y);
6315 		     }
6316 		     spline_mode_draw(xcDRAW_FINAL, newspline);
6317 		  }
6318 	       }
6319 	       else {
6320 	    	  spline_mode_draw(xcDRAW_EMPTY, newspline);
6321 		  topobject->parts--;
6322 	       }
6323 	    }
6324 	 }
6325 	 else {
6326 	    spline_mode_draw(xcDRAW_EMPTY, newspline);
6327 	    free_single((genericptr)newspline);
6328 	    free(newspline);
6329 	    topobject->parts--;
6330 	 }
6331 	 pwriteback(areawin->topinstance);
6332 
6333 	 if (!dotrack) {
6334 	    /* Free the editstack */
6335 	    reset(areawin->editstack, NORMAL);
6336 
6337 	    xcRemoveEventHandler(areawin->area, PointerMotionMask, False,
6338 	            (xcEventHandler)trackelement, NULL);
6339 	    eventmode = NORMAL_MODE;
6340 	    donecycles = True;
6341 	 }
6342       } break;
6343    }
6344    calcbbox(areawin->topinstance);
6345 
6346    /* Multiple-element edit:  Some items may have been moved as	*/
6347    /* opposed to edited, and should be registered here.	 To do	*/
6348    /* this correctly, we must first unselect the edited items,	*/
6349    /* then register the move for the remaining items.		*/
6350 
6351    if (donecycles) {
6352       short *eselect;
6353 
6354       for (eselect = areawin->selectlist; eselect < areawin->selectlist +
6355 		areawin->selects; eselect++)
6356 	 checkcycle(SELTOGENERIC(eselect), 0);
6357 
6358       /* Remove all (remaining) cycles */
6359       for (eselect = areawin->selectlist; eselect < areawin->selectlist +
6360 		areawin->selects; eselect++)
6361 	 removecycle(SELTOGENERICPTR(eselect));
6362 
6363       /* Remove edits from the undo stack when canceling */
6364       if (op == XCF_Cancel || op == XCF_Cancel_Last) {
6365          if (xobjs.undostack && (xobjs.undostack->type == XCF_Edit)) {
6366 	    undo_finish_series();
6367 	    undo_action();
6368 	 }
6369       }
6370    }
6371 }
6372 
6373 /*-------------------------------------------------------*/
6374 /* Recalculate values for a drawing-area widget resizing */
6375 /*-------------------------------------------------------*/
6376 
resizearea(xcWidget w,caddr_t clientdata,caddr_t calldata)6377 void resizearea(xcWidget w, caddr_t clientdata, caddr_t calldata)
6378 {
6379 #ifndef TCL_WRAPPER
6380    Arg	wargs[2];
6381 #endif
6382    XEvent discard;
6383    int savewidth = areawin->width, saveheight = areawin->height;
6384    XCWindowData *thiswin;
6385 #ifndef HAVE_CAIRO
6386    XGCValues values;
6387 #endif /* !HAVE_CAIRO */
6388    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
6389 
6390    if ((dpy != NULL) && xcIsRealized(areawin->area)) {
6391 
6392 #ifdef TCL_WRAPPER
6393       areawin->width = Tk_Width(w);
6394       areawin->height = Tk_Height(w);
6395 #else
6396       XtSetArg(wargs[0], XtNwidth, &areawin->width);
6397       XtSetArg(wargs[1], XtNheight, &areawin->height);
6398       XtGetValues(areawin->area, wargs, 2);
6399 #endif
6400 
6401       if (areawin->width != savewidth || areawin->height != saveheight) {
6402 
6403 	 int maxwidth = 0, maxheight = 0;
6404          for (thiswin = xobjs.windowlist; thiswin != NULL; thiswin = thiswin->next) {
6405             if (thiswin->width > maxwidth) maxwidth = thiswin->width;
6406             if (thiswin->height > maxheight) maxheight = thiswin->height;
6407          }
6408 #if !defined(HAVE_CAIRO)
6409          if (dbuf != (Pixmap)NULL) XFreePixmap(dpy, dbuf);
6410          dbuf = XCreatePixmap(dpy, areawin->window, maxwidth, maxheight,
6411 		DefaultDepthOfScreen(xcScreen(w)));
6412 #endif
6413 #ifdef HAVE_CAIRO
6414 	 /* TODO: probably make this a generalized function call, which	*/
6415 	 /* should be handled depending on the surface in the xtgui.c,	*/
6416 	 /* xcwin32.c, etc. files.					*/
6417 	 /* For now only support xlib surface				*/
6418 	 cairo_xlib_surface_set_size(areawin->surface, areawin->width,
6419 		areawin->height);
6420 #else
6421          if (areawin->clipmask != (Pixmap)NULL) XFreePixmap(dpy, areawin->clipmask);
6422          areawin->clipmask = XCreatePixmap(dpy, areawin->window,
6423 		maxwidth, maxheight, 1);
6424 
6425          if (areawin->pbuf != (Pixmap)NULL) {
6426 	    XFreePixmap(dpy, areawin->pbuf);
6427             areawin->pbuf = XCreatePixmap(dpy, areawin->window,
6428 		maxwidth, maxheight, 1);
6429 	 }
6430 
6431          if (areawin->cmgc != (GC)NULL) XFreeGC(dpy, areawin->cmgc);
6432 	 values.foreground = 0;
6433 	 values.background = 0;
6434 	 areawin->cmgc = XCreateGC(dpy, areawin->clipmask,
6435                 GCForeground | GCBackground, &values);
6436 #endif /* !HAVE_CAIRO */
6437 
6438 	 /* Clear fixed_pixmap */
6439 	 if (areawin->fixed_pixmap) {
6440 #ifdef HAVE_CAIRO
6441             cairo_pattern_destroy(areawin->fixed_pixmap);
6442             areawin->fixed_pixmap = NULL;
6443 #else /* !HAVE_CAIRO */
6444 	    XFreePixmap(dpy, areawin->fixed_pixmap);
6445 	    areawin->fixed_pixmap = (Pixmap) NULL;
6446 #endif /* !HAVE_CAIRO */
6447 	 }
6448 
6449          reset_gs();
6450 
6451 	 /* Re-compose the directores to match the new dimensions */
6452 	 composelib(LIBLIB);
6453 	 composelib(PAGELIB);
6454 
6455          /* Re-center image in resized window */
6456          zoomview(NULL, NULL, NULL);
6457       }
6458 
6459       /* Flush all expose events from the buffer */
6460       while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard) == True);
6461    }
6462 }
6463 
6464 /*----------------------*/
6465 /* Draw the grids, etc. */
6466 /*----------------------*/
6467 
6468 #ifndef HAVE_CAIRO
draw_grids(void)6469 void draw_grids(void)
6470 {
6471    float x, y, spc, spc2, i, j, fpart;
6472    float major_snapspace, spc3;
6473 
6474    spc = xobjs.pagelist[areawin->page]->gridspace * areawin->vscale;
6475    if (areawin->gridon && spc > 8) {
6476       fpart = (float)(-areawin->pcorner.x)
6477 		     / xobjs.pagelist[areawin->page]->gridspace;
6478       x = xobjs.pagelist[areawin->page]->gridspace *
6479 	    (fpart - (float)((int)fpart)) * areawin->vscale;
6480       fpart = (float)(-areawin->pcorner.y)
6481 		     / xobjs.pagelist[areawin->page]->gridspace;
6482       y = xobjs.pagelist[areawin->page]->gridspace *
6483 	    (fpart - (float)((int)fpart)) * areawin->vscale;
6484 
6485       SetForeground(dpy, areawin->gc, GRIDCOLOR);
6486       for (i = x; i < (float)areawin->width; i += spc)
6487 	 DrawLine (dpy, areawin->window, areawin->gc, (int)(i + 0.5),
6488 	       0, (int)(i + 0.5), areawin->height);
6489       for (j = (float)areawin->height - y; j > 0; j -= spc)
6490          DrawLine (dpy, areawin->window, areawin->gc, 0, (int)(j - 0.5),
6491 	       areawin->width, (int)(j - 0.5));
6492    }
6493 
6494    if (areawin->axeson) {
6495       XPoint originpt, zeropt;
6496       zeropt.x = zeropt.y = 0;
6497       SetForeground(dpy, areawin->gc, AXESCOLOR);
6498       user_to_window(zeropt, &originpt);
6499       DrawLine(dpy, areawin->window, areawin->gc, originpt.x, 0,
6500 	       originpt.x, areawin->height);
6501       DrawLine(dpy, areawin->window, areawin->gc, 0, originpt.y,
6502 	       areawin->width, originpt.y);
6503    }
6504 
6505    /* bounding box goes beneath everything except grid/axis lines */
6506    UDrawBBox();
6507 
6508    /* draw a little red dot at each snap-to point */
6509 
6510    spc2 = xobjs.pagelist[areawin->page]->snapspace * areawin->vscale;
6511    if (areawin->snapto && spc2 > 8) {
6512       float x2, y2;
6513 
6514       fpart = (float)(-areawin->pcorner.x)
6515       	       / xobjs.pagelist[areawin->page]->snapspace;
6516       x2 = xobjs.pagelist[areawin->page]->snapspace *
6517 	       (fpart - (float)((int)fpart)) * areawin->vscale;
6518       fpart = (float)(-areawin->pcorner.y)
6519 	       / xobjs.pagelist[areawin->page]->snapspace;
6520       y2 = xobjs.pagelist[areawin->page]->snapspace *
6521 	       (fpart - (float)((int)fpart)) * areawin->vscale;
6522 
6523 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6524       {
6525 	 HDC hdc = CreateCompatibleDC(NULL);
6526 	 SelectObject(hdc, Tk_GetHWND(areawin->window));
6527 #endif
6528          SetForeground(dpy, areawin->gc, SNAPCOLOR);
6529          for (i = x2; i < areawin->width; i += spc2)
6530             for (j = areawin->height - y2; j > 0; j -= spc2)
6531 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6532 	       SetPixelV(hdc, (int)(i + 0.5), (int)(j - 0.05), areawin->gc->foreground);
6533 #endif
6534                DrawPoint (dpy, areawin->window, areawin->gc, (int)(i + 0.5),
6535 			(int)(j - 0.5));
6536 #if defined(TCL_WRAPPER) && defined(XC_WIN32)
6537 	 DeleteDC(hdc);
6538       }
6539 #endif
6540    };
6541 
6542    /* Draw major snap points (code contributed by John Barry) */
6543 
6544    major_snapspace = xobjs.pagelist[areawin->page]->gridspace * 20;
6545    spc3 = major_snapspace * areawin->vscale;
6546    if (spc > 4) {
6547       fpart = (float)(-areawin->pcorner.x) / major_snapspace;
6548       x = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale;
6549       fpart = (float)(-areawin->pcorner.y) / major_snapspace;
6550       y = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale;
6551 
6552       SetForeground(dpy, areawin->gc, GRIDCOLOR);
6553       for (i = x; i < (float)areawin->width; i += spc3) {
6554 	 for (j = (float)areawin->height - y; j > 0; j -= spc3) {
6555 	    XDrawArc(dpy, areawin->window, areawin->gc, (int)(i + 0.5) - 1,
6556 		  (int)(j - 0.5) - 1, 2, 2, 0, 360*64);
6557 	 }
6558       }
6559    }
6560 
6561    SetBackground(dpy, areawin->gc, BACKGROUND);
6562 }
6563 #endif /* !HAVE_CAIRO */
6564 
6565 /*------------------------------------------------------*/
6566 /* Draw fixed parts of the primary graphics window	*/
6567 /*------------------------------------------------------*/
6568 
draw_fixed(void)6569 void draw_fixed(void)
6570 {
6571    Boolean old_ongoing;
6572 #ifndef HAVE_CAIRO
6573    Window old_window;
6574 #endif /* !HAVE_CAIRO */
6575 
6576    if (xobjs.suspend >= 0) return;
6577    old_ongoing = areawin->redraw_ongoing;
6578    areawin->redraw_ongoing = True;
6579 
6580    /* Set drawing context to fixed_pixmap */
6581 #ifdef HAVE_CAIRO
6582    cairo_identity_matrix(areawin->cr);
6583    cairo_push_group(areawin->cr);
6584 #else /* HAVE_CAIRO */
6585    old_window = areawin->window;
6586    areawin->window = areawin->fixed_pixmap;
6587 #endif /* HAVE_CAIRO */
6588 
6589    /* Clear background */
6590 #ifdef HAVE_CAIRO
6591    if (xobjs.pagelist[areawin->page]->background.name != (char *)NULL) {
6592 #ifdef HAVE_GS
6593       copybackground();
6594 #else /* HAVE_GS */
6595       SetForeground(dpy, areawin->gc, BACKGROUND);
6596       cairo_paint(areawin->cr);
6597 #endif /* HAVE_GS */
6598    }
6599    else {
6600       SetForeground(dpy, areawin->gc, BACKGROUND);
6601       cairo_paint(areawin->cr);
6602    }
6603 #else /* HAVE_CAIRO */
6604    if (xobjs.pagelist[areawin->page]->background.name == (char *)NULL
6605 	|| (copybackground() < 0)) {
6606       SetForeground(dpy, areawin->gc, BACKGROUND);
6607       XFillRectangle(dpy, areawin->window, areawin->gc, 0, 0, areawin->width,
6608 	   areawin->height);
6609    }
6610    SetThinLineAttributes(dpy, areawin->gc, 0, LineSolid, CapRound, JoinBevel);
6611 #endif /* !HAVE_CAIRO */
6612 
6613    newmatrix();
6614 
6615    /* draw GRIDCOLOR lines for grid; mark axes in AXESCOLOR */
6616 
6617    if (eventmode != CATALOG_MODE && eventmode != ASSOC_MODE
6618 	&& eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE
6619 	&& eventmode != CATMOVE_MODE && eventmode != CATTEXT_MODE) {
6620 
6621       draw_grids();
6622 
6623       /* Determine the transformation matrix for the topmost object */
6624       /* and draw the hierarchy above the current edit object (if   */
6625       /* "edit-in-place" is selected).				    */
6626 
6627       if (areawin->editinplace == True) {
6628          if (areawin->stack != NULL) {
6629 	    pushlistptr lastlist = NULL, thislist;
6630 	    Matrix mtmp;
6631 
6632 	    UPushCTM();	/* save our current state */
6633 
6634 	    /* It's easiest if we first push the current page onto the stack, */
6635 	    /* then we don't need to treat the top-level page separately.  We */
6636 	    /* pop it at the end.					      */
6637 	    push_stack(&areawin->stack, areawin->topinstance, NULL);
6638 
6639 	    thislist = areawin->stack;
6640 
6641 	    while ((thislist != NULL) &&
6642 			(is_library(thislist->thisinst->thisobject) < 0)) {
6643 
6644 	       /* Invert the transformation matrix of the instance on the stack */
6645 	       /* to get the proper transformation matrix of the drawing one	*/
6646 	       /* up in the hierarchy.						*/
6647 
6648 	       UResetCTM(&mtmp);
6649 	       UPreMultCTM(&mtmp, thislist->thisinst->position,
6650 			thislist->thisinst->scale, thislist->thisinst->rotation);
6651 	       InvertCTM(&mtmp);
6652 	       UPreMultCTMbyMat(DCTM, &mtmp);
6653 
6654 	       lastlist = thislist;
6655 	       thislist = thislist->next;
6656 
6657 	       /* The following will be true for moves between schematics and symbols */
6658 	       if ((thislist != NULL) && (thislist->thisinst->thisobject->symschem
6659 			== lastlist->thisinst->thisobject))
6660 	          break;
6661 	    }
6662 
6663 	    if (lastlist != NULL) {
6664 	       pushlistptr stack = NULL;
6665 	       SetForeground(dpy, areawin->gc, OFFBUTTONCOLOR);
6666                UDrawObject(lastlist->thisinst, SINGLE, DOFORALL,
6667 			xobjs.pagelist[areawin->page]->wirewidth, &stack);
6668 	       /* This shouldn't happen, but just in case. . . */
6669 	       if (stack) free_stack(&stack);
6670 	    }
6671 
6672 	    pop_stack(&areawin->stack); /* restore the original stack state */
6673 	    UPopCTM();			  /* restore the original matrix state */
6674 	 }
6675       }
6676    }
6677 
6678    /* draw all of the elements on the screen */
6679 
6680    SetForeground(dpy, areawin->gc, FOREGROUND);
6681 
6682    /* Initialize hierstack */
6683    if (areawin->hierstack) free_stack(&areawin->hierstack);
6684    UDrawObject(areawin->topinstance, TOPLEVEL, FOREGROUND,
6685 		xobjs.pagelist[areawin->page]->wirewidth, &areawin->hierstack);
6686    if (areawin->hierstack) free_stack(&areawin->hierstack);
6687 
6688 #ifdef HAVE_CAIRO
6689    if (areawin->fixed_pixmap)
6690       cairo_pattern_destroy(areawin->fixed_pixmap);
6691    areawin->fixed_pixmap = cairo_pop_group(areawin->cr);
6692 #else /* HAVE_CAIRO */
6693    areawin->window = old_window;
6694 #endif /* HAVE_CAIRO */
6695    areawin->redraw_ongoing = old_ongoing;
6696 }
6697 
6698 /*--------------------------------------*/
6699 /* Draw the primary graphics window	*/
6700 /*--------------------------------------*/
6701 
drawwindow(xcWidget w,caddr_t clientdata,caddr_t calldata)6702 void drawwindow(xcWidget w, caddr_t clientdata, caddr_t calldata)
6703 {
6704    XEvent discard;
6705    xcDrawType redrawtype = xcDRAW_EDIT;
6706    UNUSED(w); UNUSED(clientdata); UNUSED(calldata);
6707 
6708    if (areawin->area == NULL) return;
6709    if (!xcIsRealized(areawin->area)) return;
6710    if (xobjs.suspend >= 0) return;
6711 
6712 #ifndef HAVE_CAIRO
6713    /* Make sure a fixed pixmap exists */
6714    if (!areawin->fixed_pixmap) {
6715       areawin->fixed_pixmap = XCreatePixmap(dpy, areawin->window,
6716             areawin->width, areawin->height,
6717             DefaultDepthOfScreen(xcScreen(areawin->area)));
6718    }
6719 #endif /* !HAVE_CAIRO */
6720 
6721    /* Sanity check---specifically to track down an error */
6722    if ((areawin->selects == 1) && *(areawin->selectlist) >= topobject->parts) {
6723       Wprintf("Internal error!");
6724       areawin->selects = 0;
6725       unselect_all();
6726    }
6727 
6728    if (areawin->redraw_needed)
6729       redrawtype = xcREDRAW_FORCED;
6730 
6731    switch (eventmode) {
6732       case ARC_MODE: case EARC_MODE:
6733 	 arc_mode_draw(redrawtype, TOARC(EDITPART));
6734 	 break;
6735       case SPLINE_MODE: case ESPLINE_MODE:
6736 	 spline_mode_draw(redrawtype, TOSPLINE(EDITPART));
6737 	 break;
6738       case BOX_MODE: case EPOLY_MODE: case WIRE_MODE:
6739 	 poly_mode_draw(redrawtype, TOPOLY(EDITPART));
6740 	 break;
6741       case EPATH_MODE:
6742 	 path_mode_draw(redrawtype, TOPATH(EDITPART));
6743 	 break;
6744       case TEXT_MODE: case CATTEXT_MODE: case ETEXT_MODE:
6745 	 text_mode_draw(redrawtype, TOLABEL(EDITPART));
6746 	 break;
6747       case SELAREA_MODE:
6748 	 selarea_mode_draw(redrawtype, NULL);
6749 	 break;
6750       case RESCALE_MODE:
6751 	 rescale_mode_draw(redrawtype, NULL);
6752 	 break;
6753       case CATMOVE_MODE: case MOVE_MODE: case COPY_MODE:
6754 	 move_mode_draw(redrawtype, NULL);
6755 	 break;
6756       case ASSOC_MODE: case EINST_MODE: case FONTCAT_MODE: case EFONTCAT_MODE:
6757       case PAN_MODE: case NORMAL_MODE: case UNDO_MODE: case CATALOG_MODE:
6758 	 normal_mode_draw(redrawtype, NULL);
6759    }
6760 
6761    /* flush out multiple expose/resize events from the event queue */
6762    while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard));
6763 
6764    /* end by restoring graphics state */
6765    SetForeground(dpy, areawin->gc, areawin->gccolor);
6766 
6767    areawin->redraw_needed = False;
6768 }
6769 
6770 /*----------------------------------------------------------------------*/
6771 /* Draw the current window (areawin).  Check if other windows contain	*/
6772 /* the same object or one of its ancestors.  If so, redraw them, too.	*/
6773 /*----------------------------------------------------------------------*/
6774 
drawarea(xcWidget w,caddr_t clientdata,caddr_t calldata)6775 void drawarea(xcWidget w, caddr_t clientdata, caddr_t calldata)
6776 {
6777    XCWindowDataPtr thiswin, focuswin;
6778 
6779    if (xobjs.suspend >= 0) {
6780       if (xobjs.suspend == 0)
6781 	 xobjs.suspend = 1;	/* Mark that a refresh is pending */
6782       return;
6783    }
6784 
6785    focuswin = areawin;
6786 
6787    for (thiswin = xobjs.windowlist;  thiswin != NULL; thiswin = thiswin->next) {
6788       if (thiswin == focuswin) continue;
6789 
6790       /* Note:  need to check ancestry here, not just blindly redraw	*/
6791       /* all the windows all the time.					*/
6792       areawin = thiswin;
6793 
6794 #ifdef HAVE_CAIRO
6795       /* Don't respond to an expose event if the graphics context  */
6796       /* has not yet been created.                                 */
6797       if (areawin->cr != NULL)
6798 #endif
6799          drawwindow(NULL, NULL, NULL);
6800    }
6801    areawin = focuswin;
6802    drawwindow(w, clientdata, calldata);
6803 }
6804 
6805 /*-------------------------------------------------------------------------*/
6806