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