/*-------------------------------------------------------------------------*/ /* events.c --- xcircuit routines handling Xevents and Callbacks */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /* written by Tim Edwards, 8/13/93 */ /*-------------------------------------------------------------------------*/ #include #include #include #include #include #ifndef XC_WIN32 #include #include #define XK_MISCELLANY #define XK_LATIN1 #include #else #ifdef TCL_WRAPPER #define XK_MISCELLANY #define XK_LATIN1 #include #endif #endif #ifdef OPENGL #include #include #endif /* OPENGL */ /*-------------------------------------------------------------------------*/ /* Local includes */ /*-------------------------------------------------------------------------*/ #ifdef TCL_WRAPPER #include #endif #include "xcircuit.h" #include "colordefs.h" #define HOLD_MASK (Mod2Mask << 16) /*----------------------------------------------------------------------*/ /* Function prototype declarations */ /*----------------------------------------------------------------------*/ #include "prototypes.h" /*-------------------------------------------------------------------------*/ /* Global Variable definitions */ /*-------------------------------------------------------------------------*/ short saverot; short eventmode; /* keep track of the mode the screen is in */ short textpos, textend; /* keep track of the cursor position in text */ short refselect; short attachto = 0; extern XtAppContext app; extern Display *dpy; extern int *appcolors; extern Cursor appcursors[NUM_CURSORS]; extern Globaldata xobjs; extern Clientdata areastruct; extern ApplicationData appdata; extern short popups; extern int pressmode; extern xcWidget message2, top; extern char _STR[150], _STR2[250]; extern short beeper; extern double saveratio; extern u_char texttype; extern aliasptr aliastop; #ifdef TCL_WRAPPER extern Tcl_Interp *xcinterp; #else extern short help_up; #endif /* double buffer */ #ifdef DOUBLEBUFFER Pixmap dbuf = (Pixmap)NULL; #endif #ifdef OPENGL extern GLXContext grXcontext; #endif /*----------------------------------------------------------------------------*/ /* Edit Object pushing and popping. */ /*----------------------------------------------------------------------------*/ Boolean recursefind(objectptr parent, objectptr suspect) { genericptr *shell; if (parent == suspect) return True; for (shell = parent->plist; shell < parent->plist + parent->parts; shell++) if (IS_OBJINST(*shell)) if (recursefind(TOOBJINST(shell)->thisobject, suspect)) return True; return False; } /*--------------------------------------------------------------*/ /* Transfer objects in the select list to the current object */ /* (but disallow infinitely recursive loops!) */ /*--------------------------------------------------------------*/ /* IMPORTANT: delete_for_xfer() MUST be executed prior to */ /* calling transferselects(), so that the deleted elements are */ /* in an object saved in areastruct.editstack. */ /*--------------------------------------------------------------*/ void transferselects() { objinstptr tobj; if (areastruct.editstack->parts == 0) return; if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) { short ps = topobject->parts; freeselects(); areastruct.selects = areastruct.editstack->parts; areastruct.selectlist = xc_undelete(areastruct.topinstance, areastruct.editstack, (short)NORMAL, (short *)NULL); /* check to make sure this object is not the current object */ /* or one of its direct ancestors, else an infinite loop results. */ for (ps = 0; ps < topobject->parts; ps++) { if (IS_OBJINST(*(topobject->plist + ps))) { tobj = TOOBJINST(topobject->plist + ps); if (recursefind(tobj->thisobject, topobject)) { Wprintf("Attempt to place object inside of itself"); delete_noundo(NORMAL); break; } } } } } /*-------------------------------------------------------------------*/ /* Make a new matrix corresponding to the current position and scale */ /*-------------------------------------------------------------------*/ void newmatrix() { if (DCTM == NULL) { DCTM = (Matrixptr)malloc(sizeof(Matrix)); DCTM->nextmatrix = NULL; } UResetCTM(DCTM); UMakeWCTM(DCTM); } /*-------------------------------------------------------*/ /* set the viewscale variable to the proper address */ /*-------------------------------------------------------*/ void setpage(Boolean killselects) { areastruct.vscale = &(topobject->viewscale); areastruct.pcorner = &(topobject->pcorner); newmatrix(); if (killselects) clearselects_noundo(); } /*-------------------------------------------------------*/ /* switch to a new page */ /*-------------------------------------------------------*/ int changepage(short pagenumber) { short npage; objectptr pageobj; u_char undo_type; /* to add to existing number of top level pages. . . */ if (pagenumber == 255) { if (xobjs.pages == 255) { Wprintf("Out of available pages!"); return -1; } else pagenumber = xobjs.pages; } if (pagenumber >= xobjs.pages) { xobjs.pagelist = (Pagedata **)realloc(xobjs.pagelist, (pagenumber + 1) * sizeof(Pagedata *)); xobjs.pagelist[pagenumber] = (Pagedata *)malloc(sizeof(Pagedata)); xobjs.pagelist[pagenumber]->filename = NULL; xobjs.pagelist[pagenumber]->background.name = NULL; for (npage = xobjs.pages; npage <= pagenumber; npage++) xobjs.pagelist[npage]->pageinst = NULL; xobjs.pages = pagenumber + 1; makepagebutton(); } if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) { delete_for_xfer(NORMAL, areastruct.selectlist, areastruct.selects); undo_type = UNDO_MORE; } else { clearselects(); undo_type = UNDO_DONE; } if (areastruct.page != pagenumber) register_for_undo(XCF_Page, undo_type, areastruct.topinstance, areastruct.page, pagenumber); if (eventmode != ASSOC_MODE) { areastruct.page = pagenumber; free_stack(&areastruct.stack); } if (xobjs.pagelist[pagenumber]->pageinst == NULL) { /* initialize a new page */ pageobj = (objectptr) malloc (sizeof(object)); initmem(pageobj); sprintf(pageobj->name, "Page %d", pagenumber + 1); xobjs.pagelist[pagenumber]->pageinst = newpageinst(pageobj); xobjs.pagelist[pagenumber]->filename = NULL; xobjs.pagelist[pagenumber]->background.name = NULL; pagereset(pagenumber); } areastruct.topinstance = xobjs.pagelist[pagenumber]->pageinst; setpage(TRUE); return 0; } /*-------------------------------------------------------*/ /* switch to a new page and redisplay */ /*-------------------------------------------------------*/ void newpage(short pagenumber) { switch (eventmode) { case CATALOG_MODE: eventmode = NORMAL_MODE; catreturn(); break; case NORMAL_MODE: case COPY_MODE: case MOVE_MODE: case UNDO_MODE: if (changepage(pagenumber) >= 0) { transferselects(); renderbackground(); refresh(NULL, NULL, NULL); togglegrid((u_short)xobjs.pagelist[areastruct.page]->coordstyle); setsymschem(); } break; default: Wprintf("Cannot switch pages from this mode"); break; } } /*---------------------------------------*/ /* Stack structure push and pop routines */ /*---------------------------------------*/ void push_stack(pushlistptr *stackroot, objinstptr thisinst) { pushlistptr newpush; newpush = (pushlistptr)malloc(sizeof(pushlist)); newpush->next = *stackroot; newpush->thisinst = thisinst; *stackroot = newpush; } /*----------------------------------------------------------*/ void pop_stack(pushlistptr *stackroot) { pushlistptr lastpush; if (!(*stackroot)) { Fprintf(stderr, "pop_genstack() Error: NULL instance stack!\n"); return; } lastpush = (*stackroot)->next; free(*stackroot); *stackroot = lastpush; } /*----------------------------------------------------------*/ void free_stack(pushlistptr *stackroot) { while ((*stackroot) != NULL) pop_stack(stackroot); } /*------------------------------------------*/ /* Push object onto hierarchy stack to edit */ /*------------------------------------------*/ void pushobject(objinstptr thisinst) { short i, *selectobj, *savelist; int saves; u_char undo_type = UNDO_DONE; objinstptr pushinst = thisinst; savelist = NULL; if (eventmode == MOVE_MODE || eventmode == COPY_MODE) { savelist = areastruct.selectlist; saves = areastruct.selects; areastruct.selectlist = NULL; areastruct.selects = 0; undo_type = UNDO_MORE; } if (pushinst == NULL) { selectobj = areastruct.selectlist; if (areastruct.selects == 0) selectobj = select_element(OBJINST); if (areastruct.selects == 0) { Wprintf("No objects selected."); return; } else if (areastruct.selects > 1) { Wprintf("Choose only one object."); return; } else if (SELECTTYPE(selectobj) != OBJINST) { Wprintf("Element to push must be an object."); return; } else pushinst = SELTOOBJINST(selectobj); } if (savelist != NULL) { delete_for_xfer(NORMAL, savelist, saves); free(savelist); } register_for_undo(XCF_Push, undo_type, areastruct.topinstance, pushinst); /* save the address of the current object to the push stack */ push_stack(&areastruct.stack, areastruct.topinstance); areastruct.topinstance = pushinst; /* move selected items to the new object */ setpage(TRUE); transferselects(); invalidate_graphics(topobject); refresh(NULL, NULL, NULL); setsymschem(); } /*--------------------------*/ /* Pop edit hierarchy stack */ /*--------------------------*/ void popobject(xcWidget w, pointertype no_undo, caddr_t calldata) { u_char undo_type = UNDO_DONE; if (areastruct.stack == NULL || (eventmode != NORMAL_MODE && eventmode != MOVE_MODE && eventmode != COPY_MODE && eventmode != FONTCAT_MODE && eventmode != ASSOC_MODE && eventmode != UNDO_MODE && eventmode != EFONTCAT_MODE)) return; if ((eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) && ((areastruct.stack->thisinst == xobjs.libtop[LIBRARY]) || (areastruct.stack->thisinst == xobjs.libtop[USERLIB]))) return; /* remove any selected items from the current object */ if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) { undo_type = UNDO_MORE; delete_for_xfer(NORMAL, areastruct.selectlist, areastruct.selects); } else unselect_all(); /* If coming from the library, don't register an undo action, because */ /* it has already been registered as type XCF_Library_Pop. */ if (no_undo == (pointertype)0) register_for_undo(XCF_Pop, undo_type, areastruct.topinstance); areastruct.topinstance = areastruct.stack->thisinst; pop_stack(&areastruct.stack); /* if new object is a library or PAGELIB, put back into CATALOG_MODE */ if (is_library(topobject) >= 0) eventmode = CATALOG_MODE; /* move selected items to the new object */ setpage(TRUE); setsymschem(); if (eventmode != ASSOC_MODE) transferselects(); invalidate_graphics(topobject); refresh(NULL, NULL, NULL); } /*-------------------------------------------------------------------------*/ /* Destructive reset of entire object */ /*-------------------------------------------------------------------------*/ void resetbutton(xcWidget button, pointertype pageno, caddr_t calldata) { short page; objectptr pageobj; objinstptr pageinst; if (eventmode != NORMAL_MODE) return; page = (pageno == (pointertype)0) ? areastruct.page : (short)(pageno - 1); pageinst = xobjs.pagelist[page]->pageinst; if (pageinst == NULL) return; /* page already cleared */ pageobj = pageinst->thisobject; /* Make sure this is a real top-level page */ if (is_page(topobject) < 0) { if (pageno == (pointertype)0) { Wprintf("Can only clear top-level pages!"); return; } else { /* Make sure that we're not in the hierarchy of the page being deleted */ pushlistptr slist; for (slist = areastruct.stack; slist != NULL; slist = slist->next) if (slist->thisinst->thisobject == pageobj) { Wprintf("Can't delete the page while you're in its hierarchy!"); return; } } } /* Watch for pages which are linked by schematic/symbol. */ if (pageobj->symschem != NULL) { sprintf(_STR, "Schematic association to object %s", pageobj->symschem->name); Wprintf(_STR); return; } sprintf(pageobj->name, "Page %d", page + 1); xobjs.pagelist[page]->filename = (char *)realloc(xobjs.pagelist[page]->filename, (strlen(pageobj->name) + 1) * sizeof(char)); strcpy(xobjs.pagelist[page]->filename, pageobj->name); reset(pageobj, NORMAL); flush_undo_stack(); if (page == areastruct.page) { drawarea(areastruct.area, NULL, NULL); printname(pageobj); renamepage(page); Wprintf("Page cleared."); } } /*------------------------------------------------------*/ /* Redraw the horizontal scrollbar */ /*------------------------------------------------------*/ void drawhbar(xcWidget bar, caddr_t clientdata, caddr_t calldata) { Window bwin; float frac; long rleft, rright, rmid; if (!xcIsRealized(bar)) return; bwin = xcWindow(bar); if (topobject->bbox.width > 0) { frac = (float) areastruct.width / (float) topobject->bbox.width; rleft = (long)(frac * (float)(areastruct.pcorner->x - topobject->bbox.lowerleft.x)); rright = rleft + (long)(frac * (float)areastruct.width / (*areastruct.vscale)); } else { rleft = 0L; rright = (long)areastruct.width; } rmid = (rright + rleft) >> 1; if (rleft < 0) rleft = 0; if (rright > areastruct.width) rright = areastruct.width; XSetFunction(dpy, areastruct.gc, GXcopy); XSetForeground(dpy, areastruct.gc, BARCOLOR); if (rmid > 0 && rleft > 0) XClearArea(dpy, bwin, 0, 0, (int)rleft, SBARSIZE, FALSE); XFillRectangle(dpy, bwin, areastruct.gc, (int)rleft + 1, 1, (int)(rright - rleft), SBARSIZE - 1); if (rright > rmid) XClearArea(dpy, bwin, (int)rright + 1, 0, areastruct.width - (int)rright, SBARSIZE, FALSE); XClearArea(dpy, bwin, (int)rmid - 1, 1, 3, SBARSIZE, FALSE); XSetFunction(dpy, areastruct.gc, areastruct.gctype); XSetForeground(dpy, areastruct.gc, areastruct.gccolor); } /*------------------------------------------------------*/ /* Redraw the vertical scrollbar */ /*------------------------------------------------------*/ void drawvbar(xcWidget bar, caddr_t clientdata, caddr_t calldata) { Window bwin = xcWindow(bar); float frac; long rtop, rbot, rmid; if (!xcIsRealized(bar)) return; if (topobject->bbox.height > 0) { frac = (float)areastruct.height / (float)topobject->bbox.height; rbot = (long)(frac * (float)(topobject->bbox.lowerleft.y - areastruct.pcorner->y + topobject->bbox.height)); rtop = rbot - (long)(frac * (float)areastruct.height / (*areastruct.vscale)); } else { rbot = areastruct.height; rtop = 0; } rmid = (rtop + rbot) >> 1; if (rtop < 0) rtop = 0; if (rbot > areastruct.height) rbot = areastruct.height; XSetFunction(dpy, areastruct.gc, GXcopy); XSetForeground(dpy, areastruct.gc, BARCOLOR); if (rmid > 0 && rtop > 0) XClearArea(dpy, bwin, 0, 0, SBARSIZE, (int)rtop, FALSE); XFillRectangle(dpy, bwin, areastruct.gc, 0, (int)rtop + 2, SBARSIZE, (int)(rbot - rtop)); if (rbot > rmid) XClearArea(dpy, bwin, 0, (int)rbot + 1, SBARSIZE, areastruct.height - (int)rbot, FALSE); XClearArea(dpy, bwin, 0, (int)rmid - 1, SBARSIZE, 3, FALSE); XSetFunction(dpy, areastruct.gc, areastruct.gctype); XSetForeground(dpy, areastruct.gc, areastruct.gccolor); } /*------------------------------------------------------*/ /* Simultaneously scroll the screen and horizontal */ /* bar when dragging the mouse in the scrollbar area */ /*------------------------------------------------------*/ void panhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) { long newx, newpx; short savex = areastruct.pcorner->x; if (eventmode == SELAREA_MODE) return; newx = (long)(event->x * ((float)topobject->bbox.width / areastruct.width) + topobject->bbox.lowerleft.x - 0.5 * ((float)areastruct.width / (*areastruct.vscale))); areastruct.pcorner->x = (short)newx; drawhbar(bar, NULL, NULL); areastruct.pcorner->x = savex; #ifdef DOUBLEBUFFER if ((newpx = (long)(newx - savex) * (*areastruct.vscale)) == 0) return; SetFunction(dpy, areastruct.gc, GXcopy); if (newpx > 0) { XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, newpx, 0, areastruct.width - newpx, areastruct.height, 0, 0); XClearArea(dpy, areastruct.areawin, areastruct.width - newpx, 0, newpx, areastruct.height, FALSE); } else { XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, 0, 0, areastruct.width + newpx, areastruct.height, -newpx, 0); XClearArea(dpy, areastruct.areawin, 0, 0, -newpx, areastruct.height, FALSE); } #endif } /*------------------------------------------------------*/ /* End the horizontal scroll and refresh entire screen */ /*------------------------------------------------------*/ void endhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) { long newx; short savex = areastruct.pcorner->x; newx = (long)(event->x * ((float)topobject->bbox.width / areastruct.width) + topobject->bbox.lowerleft.x - 0.5 * ((float)areastruct.width / (*areastruct.vscale))); areastruct.pcorner->x = (short)newx; if ((newx << 1) != (long)((short)(newx << 1)) || checkbounds() == -1) { areastruct.pcorner->x = savex; Wprintf("Reached boundary: cannot pan further"); } else Wprintf(" "); areastruct.lastbackground = NULL; renderbackground(); drawhbar(bar, NULL, NULL); drawarea(bar, NULL, NULL); } /*------------------------------------------------------*/ /* Simultaneously scroll the screen and vertical */ /* bar when dragging the mouse in the scrollbar area */ /*------------------------------------------------------*/ void panvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) { long newy, newpy; short savey = areastruct.pcorner->y; if (eventmode == SELAREA_MODE) return; newy = (int)((areastruct.height - event->y) * ((float)topobject->bbox.height / areastruct.height) + topobject->bbox.lowerleft.y - 0.5 * ((float)areastruct.height / (*areastruct.vscale))); areastruct.pcorner->y = (short)newy; drawvbar(bar, NULL, NULL); areastruct.pcorner->y = savey; #ifdef DOUBLEBUFFER if ((newpy = (long)(newy - savey) * (*areastruct.vscale)) == 0) return; SetFunction(dpy, areastruct.gc, GXcopy); if (newpy > 0) { XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, 0, 0, areastruct.width, areastruct.height - newpy, 0, newpy); XClearArea(dpy, areastruct.areawin, 0, 0, areastruct.width, newpy, FALSE); } else { XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, 0, -newpy, areastruct.width, areastruct.height + newpy, 0, 0); XClearArea(dpy, areastruct.areawin, 0, areastruct.height + newpy, areastruct.width, -newpy, FALSE); } #endif } /*------------------------------------------------------*/ /* Pan the screen to follow the cursor position */ /*------------------------------------------------------*/ void trackpan(int x, int y) { long newpx, newpy; XPoint newpos; short savey = areastruct.pcorner->y; short savex = areastruct.pcorner->x; newpos.x = areastruct.origin.x - x; newpos.y = y - areastruct.origin.y; areastruct.pcorner->x += newpos.x / (*areastruct.vscale); areastruct.pcorner->y += newpos.y / (*areastruct.vscale); drawhbar(areastruct.scrollbarh, NULL, NULL); drawvbar(areastruct.scrollbarv, NULL, NULL); #ifdef DOUBLEBUFFER newpx = (long)areastruct.pcorner->x * (*areastruct.vscale); newpy = (long)areastruct.pcorner->y * (*areastruct.vscale); SetFunction(dpy, areastruct.gc, GXcopy); /* To-do: Clear or repaint areas not in copy region */ /* XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, 0, 0, areastruct.width, areastruct.height, newpx, areastruct.height - newpy); */ drawarea(NULL, NULL, NULL); #endif areastruct.pcorner->x = savex; areastruct.pcorner->y = savey; } /*------------------------------------------------------*/ /* End the vertical scroll and refresh entire screen */ /*------------------------------------------------------*/ void endvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) { long newy; short savey = areastruct.pcorner->y; newy = (int)((areastruct.height - event->y) * ((float)topobject->bbox.height / areastruct.height) + topobject->bbox.lowerleft.y - 0.5 * ((float)areastruct.height / (*areastruct.vscale))); areastruct.pcorner->y = (short)newy; if ((newy << 1) != (long)((short)(newy << 1)) || checkbounds() == -1) { areastruct.pcorner->y = savey; Wprintf("Reached boundary: cannot pan further"); } else Wprintf(" "); areastruct.lastbackground = NULL; renderbackground(); drawvbar(bar, NULL, NULL); drawarea(bar, NULL, NULL); } /*--------------------------------------------------------------------*/ /* Zoom functions-- zoom box, zoom in, zoom out, and pan. */ /*--------------------------------------------------------------------*/ void postzoom() { Wprintf(" "); areastruct.lastbackground = NULL; renderbackground(); newmatrix(); #ifdef USE_WIN32_COM win32_fire_event(ZoomChanged); #endif } /*--------------------------------------------------------------------*/ void zoomin(int x, int y) { float savescale; XPoint ucenter, ncenter, savell; savescale = *areastruct.vscale; savell.x = areastruct.pcorner->x; savell.y = areastruct.pcorner->y; /* zoom-box function: corners are in areastruct.save and areastruct.origin */ if (eventmode == SELAREA_MODE) { float delxscale, delyscale; /* select box has lower-left corner in .origin, upper-right in .save */ /* ignore if zoom box is size zero */ if (areastruct.save.x == areastruct.origin.x || areastruct.save.y == areastruct.origin.y) { Wprintf("Zoom box of size zero: Ignoring."); eventmode = NORMAL_MODE; return; } /* determine whether x or y is limiting factor in zoom */ delxscale = (areastruct.width / (*areastruct.vscale)) / abs(areastruct.save.x - areastruct.origin.x); delyscale = (areastruct.height / (*areastruct.vscale)) / abs(areastruct.save.y - areastruct.origin.y); (*areastruct.vscale) *= min(delxscale, delyscale); areastruct.pcorner->x = min(areastruct.origin.x, areastruct.save.x) - (areastruct.width / (*areastruct.vscale) - abs(areastruct.save.x - areastruct.origin.x)) / 2; areastruct.pcorner->y = min(areastruct.origin.y, areastruct.save.y) - (areastruct.height / (*areastruct.vscale) - abs(areastruct.save.y - areastruct.origin.y)) / 2; eventmode = NORMAL_MODE; } else { window_to_user(areastruct.width / 2, areastruct.height / 2, &ucenter); (*areastruct.vscale) *= areastruct.zoomfactor; window_to_user(areastruct.width / 2, areastruct.height / 2, &ncenter); areastruct.pcorner->x += (ucenter.x - ncenter.x); areastruct.pcorner->y += (ucenter.y - ncenter.y); } /* check for minimum scale */ if (checkbounds() == -1) { areastruct.pcorner->x = savell.x; areastruct.pcorner->y = savell.y; (*areastruct.vscale) = savescale; Wprintf("At minimum scale: cannot scale further"); /* this is a rare case where an object gets out-of-bounds */ if (checkbounds() == -1) { if (beeper) XBell(dpy, 100); Wprintf("Unable to scale: Delete out-of-bounds object!"); } return; } else if (eventmode == MOVE_MODE || eventmode == COPY_MODE) drag(x, y); invalidate_graphics(topobject); postzoom(); } /*--------------------------------------------------------------------*/ void zoominrefresh(int x, int y) { zoomin(x, y); refresh(NULL, NULL, NULL); } /*--------------------------------------------------------------------*/ void zoomout(int x, int y) { float savescale; XPoint ucenter, ncenter, savell; XlPoint newll; savescale = (*areastruct.vscale); savell.x = areastruct.pcorner->x; savell.y = areastruct.pcorner->y; /* zoom-box function, analogous to that for zoom-in */ if (eventmode == SELAREA_MODE) { float delxscale, delyscale, scalefac; /* ignore if zoom box is size zero */ if (areastruct.save.x == areastruct.origin.x || areastruct.save.y == areastruct.origin.y) { Wprintf("Zoom box of size zero: Ignoring."); eventmode = NORMAL_MODE; return; } /* determine whether x or y is limiting factor in zoom */ delxscale = abs(areastruct.save.x - areastruct.origin.x) / (areastruct.width / (*areastruct.vscale)); delyscale = abs(areastruct.save.y - areastruct.origin.y) / (areastruct.height / (*areastruct.vscale)); scalefac = min(delxscale, delyscale); (*areastruct.vscale) *= scalefac; /* compute lower-left corner of (reshaped) select box */ if (delxscale < delyscale) { newll.y = min(areastruct.save.y, areastruct.origin.y); newll.x = (areastruct.save.x + areastruct.origin.x - (abs(areastruct.save.y - areastruct.origin.y) * areastruct.width / areastruct.height)) / 2; } else { newll.x = min(areastruct.save.x, areastruct.origin.x); newll.y = (areastruct.save.y + areastruct.origin.y - (abs(areastruct.save.x - areastruct.origin.x) * areastruct.height / areastruct.width)) / 2; } /* extrapolate to find new lower-left corner of screen */ newll.x = areastruct.pcorner->x - (int)((float)(newll.x - areastruct.pcorner->x) / scalefac); newll.y = areastruct.pcorner->y - (int)((float)(newll.y - areastruct.pcorner->y) / scalefac); eventmode = NORMAL_MODE; } else { window_to_user(areastruct.width / 2, areastruct.height / 2, &ucenter); (*areastruct.vscale) /= areastruct.zoomfactor; window_to_user(areastruct.width / 2, areastruct.height / 2, &ncenter); newll.x = (long)areastruct.pcorner->x + (long)(ucenter.x - ncenter.x); newll.y = (long)areastruct.pcorner->y + (long)(ucenter.y - ncenter.y); } areastruct.pcorner->x = (short)newll.x; areastruct.pcorner->y = (short)newll.y; if ((newll.x << 1) != (long)(areastruct.pcorner->x << 1) || (newll.y << 1) != (long)(areastruct.pcorner->y << 1) || checkbounds() == -1) { (*areastruct.vscale) = savescale; areastruct.pcorner->x = savell.x; areastruct.pcorner->y = savell.y; Wprintf("At maximum scale: cannot scale further."); return; } else if (eventmode == MOVE_MODE || eventmode == COPY_MODE) drag(x, y); invalidate_graphics(topobject); postzoom(); } /*--------------------------------------------------------------------*/ void zoomoutrefresh(int x, int y) { zoomout(x, y); refresh(NULL, NULL, NULL); } /*--------------------------------------*/ /* Call to XWarpPointer */ /*--------------------------------------*/ void warppointer(int x, int y) { XWarpPointer(dpy, None, areastruct.areawin, 0, 0, 0, 0, x, y); } /*--------------------------------------------------------------*/ /* ButtonPress handler during center pan */ /* x and y are cursor coordinates. */ /* If ptype is 1-4 (directional), then "value" is a fraction of */ /* the screen to scroll. */ /*--------------------------------------------------------------*/ void panbutton(u_int ptype, int x, int y, float value) { Window pwin; int xpos, ypos, newllx, newlly; XPoint savell, newpos; Dimension hwidth = areastruct.width >> 1, hheight = areastruct.height >> 1; savell.x = areastruct.pcorner->x; savell.y = areastruct.pcorner->y; switch(ptype) { case 1: xpos = hwidth - (hwidth * 2 * value); ypos = hheight; break; case 2: xpos = hwidth + (hwidth * 2 * value); ypos = hheight; break; case 3: xpos = hwidth; ypos = hheight - (hheight * 2 * value); break; case 4: xpos = hwidth; ypos = hheight + (hheight * 2 * value); break; case 5: xpos = x; ypos = y; break; case 6: /* "pan follow" */ if (eventmode == PAN_MODE) finish_op(XCF_Finish, x, y); else if (eventmode == NORMAL_MODE) { eventmode = PAN_MODE; areastruct.save.x = x; areastruct.save.y = y; u2u_snap(&areastruct.save); areastruct.origin = areastruct.save; #ifdef TCL_WRAPPER Tk_CreateEventHandler(areastruct.area, PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else xcAddEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)xlib_drag, NULL); #endif } return; break; default: /* "pan here" */ xpos = x; ypos = y; warppointer(hwidth, hheight); break; } xpos -= hwidth; ypos = hheight - ypos; newllx = (int)areastruct.pcorner->x + (int)((float)xpos / (*areastruct.vscale)); newlly = (int)areastruct.pcorner->y + (int)((float)ypos / (*areastruct.vscale)); areastruct.pcorner->x = (short) newllx; areastruct.pcorner->y = (short) newlly; if ((newllx << 1) != (long)(areastruct.pcorner->x << 1) || (newlly << 1) != (long)(areastruct.pcorner->y << 1) || checkbounds() == -1) { areastruct.pcorner->x = savell.x; areastruct.pcorner->x = savell.y; Wprintf("Reached bounds: cannot pan further."); return; } else if (eventmode == MOVE_MODE || eventmode == COPY_MODE) drag(x, y); postzoom(); } /*--------------------------------------------------------------*/ void panrefresh(u_int ptype, int x, int y, float value) { panbutton(ptype, x, y, value); refresh(NULL, NULL, NULL); } /*----------------------------------------------------------------*/ /* Check for out-of-bounds before warping pointer, and pan window */ /* if necessary. */ /*----------------------------------------------------------------*/ void checkwarp(XPoint *userpt) { XPoint wpoint; XButtonEvent event; user_to_window(*userpt, &wpoint); if (wpoint.x < 0 || wpoint.y < 0 || wpoint.x > areastruct.width || wpoint.y > areastruct.height) { panrefresh(5, wpoint.x, wpoint.y, 0); wpoint.x = areastruct.width >> 1; wpoint.y = areastruct.height >> 1; snap(wpoint.x, wpoint.y, userpt); } warppointer(wpoint.x, wpoint.y); } /*--------------------------------------------------------------*/ /* Return next edit point on a polygon */ /*--------------------------------------------------------------*/ int checkcycle(short points, short dir) { int tmppt = areastruct.editcycle + dir; if (tmppt < 0) tmppt += points; tmppt %= points; return tmppt; } /*--------------------------------------------------------------*/ /* Change to next edit point on a polygon */ /*--------------------------------------------------------------*/ void nextpolycycle(polyptr nextpoly, short dir) { XPoint *polypt; areastruct.editcycle = checkcycle(nextpoly->number, dir); finddir(nextpoly); printeditbindings(); polypt = nextpoly->points + areastruct.editcycle; checkwarp(polypt); } /*--------------------------------------------------------------*/ /* Change to next edit cycle on a spline */ /*--------------------------------------------------------------*/ void nextsplinecycle(splineptr nextspline, short dir) { areastruct.editcycle = checkcycle(4, dir); if (areastruct.editcycle == 1 || areastruct.editcycle == 2) Wprintf("Adjust control point"); else Wprintf("Adjust endpoint position"); checkwarp(&nextspline->ctrl[areastruct.editcycle]); } /*--------------------------------------------------------------*/ /* Change to next edit cycle on an arc */ /*--------------------------------------------------------------*/ void nextarccycle(arcptr nextarc, short dir) { XPoint curang; double rad; areastruct.editcycle = checkcycle(4, dir); switch(areastruct.editcycle) { case 0: curang.x = nextarc->position.x + abs(nextarc->radius); curang.y = nextarc->position.y; if (abs(nextarc->radius) != nextarc->yaxis) Wprintf("Adjust ellipse size"); else Wprintf("Adjust arc radius"); break; case 3: curang.x = nextarc->position.x; curang.y = nextarc->position.y + nextarc->yaxis; Wprintf("Adjust ellipse minor axis"); break; case 1: rad = (double)(nextarc->angle1 * RADFAC); curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad); curang.y = nextarc->position.y + nextarc->yaxis * sin(rad); Wprintf("Adjust arc endpoint"); break; case 2: rad = (double)(nextarc->angle2 * RADFAC); curang.x = nextarc->position.x + abs(nextarc->radius) * cos(rad); curang.y = nextarc->position.y + nextarc->yaxis * sin(rad); Wprintf("Adjust arc endpoint"); break; } checkwarp(&curang); } /*--------------------------*/ /* Register a "press" event */ /*--------------------------*/ #ifdef TCL_WRAPPER xcTimeOutProc makepress(caddr_t clientdata) #else xcTimeOutProc makepress(caddr_t clientdata, xcIntervalId *id) #endif { int keywstate = (int)((pointertype)clientdata); /* Button/Key was pressed long enough to make a "press", not a "tap" */ areastruct.time_id = 0; pressmode = keywstate; eventdispatch(keywstate | HOLD_MASK, areastruct.save.x, areastruct.save.y); return NULL; } /*------------------------------------------------------*/ /* Handle button events as if they were keyboard events */ /*------------------------------------------------------*/ void buttonhandler(xcWidget w, caddr_t clientdata, XButtonEvent *event) { XKeyEvent *kevent = (XKeyEvent *)event; if (event->type == ButtonPress) kevent->type = KeyPress; else kevent->type = KeyRelease; switch (event->button) { case Button1: kevent->state |= Button1Mask; break; case Button2: kevent->state |= Button2Mask; break; case Button3: kevent->state |= Button3Mask; break; case Button4: kevent->state |= Button4Mask; break; case Button5: kevent->state |= Button5Mask; break; } keyhandler(w, clientdata, kevent); } /*----------------------------------------------------------------------*/ /* Edit element operations */ /*----------------------------------------------------------------------*/ void edit_next() { genericptr *keygen = EDITPART; splineptr keyspline; arcptr keyarc; polyptr lwire; if (IS_PATH(*keygen)) keygen = (*((pathptr *)EDITPART))->plist + areastruct.editsubpart; switch(ELEMENTTYPE(*keygen)) { case POLYGON: lwire = TOPOLY(keygen); nextpolycycle(lwire, 1); polyeditpush(lwire); break; case SPLINE: keyspline = TOSPLINE(keygen); nextsplinecycle(keyspline, -1); splineeditpush(keyspline); break; case ARC: keyarc = TOARC(keygen); nextarccycle(keyarc, 1); arceditpush(keyarc); break; } } void poly_edit_op(op) { genericptr *keygen = EDITPART; polyptr lwire, *newpoly; XPoint *lpoint, *npoint; if (IS_PATH(*keygen)) keygen = (*((pathptr *)EDITPART))->plist + areastruct.editsubpart; switch(ELEMENTTYPE(*keygen)) { case POLYGON: { lwire = TOPOLY(keygen); /* Break the polygon at the point, if the point isn't an endpoint */ if (op == XCF_Edit_Break) { if (areastruct.editcycle == lwire->number - 1 || areastruct.editcycle == 0) return; UDrawPolygon(lwire); lwire->style |= UNCLOSED; NEW_POLY(newpoly, topobject); topobject->parts++; (*newpoly)->style = lwire->style; (*newpoly)->color = lwire->color; (*newpoly)->width = lwire->width; (*newpoly)->number = lwire->number - areastruct.editcycle; (*newpoly)->points = (XPoint *)malloc((*newpoly)->number * sizeof(XPoint)); (*newpoly)->passed = NULL; lpoint = lwire->points + areastruct.editcycle; for (npoint = (*newpoly)->points; npoint < (*newpoly)->points + (*newpoly)->number; npoint++) { npoint->x = lpoint->x; npoint->y = (lpoint++)->y; } lwire->number = areastruct.editcycle + 1; areastruct.editcycle = 0; reset(areastruct.editstack, NORMAL); UDrawPolygon(lwire); lwire = (*newpoly); UDrawPolygon(lwire); incr_changes(topobject); if (!nonnetwork(lwire)) invalidate_netlist(topobject); } /* Remove a point from the polygon */ else if (op == XCF_Edit_Delete) { if (lwire->number < 3) return; UDrawPolygon(lwire); if (lwire->number == 3 && !(lwire->style & UNCLOSED)) lwire->style |= UNCLOSED; lwire->number--; for (lpoint = lwire->points + areastruct.editcycle; lpoint < lwire->points + lwire->number; lpoint++) *lpoint = *(lpoint + 1); UDrawPolygon(lwire); nextpolycycle(lwire, -1); } /* Add a point to the polygon */ else if (op == XCF_Edit_Insert || op == XCF_Edit_Append) { UDrawPolygon(lwire); lwire->number++; lwire->points = (XPoint *)realloc(lwire->points, lwire->number * sizeof(XPoint)); for (lpoint = lwire->points + lwire->number - 1; lpoint > lwire-> points + areastruct.editcycle; lpoint--) *lpoint = *(lpoint - 1); UDrawPolygon(lwire); if (op == XCF_Edit_Append) nextpolycycle(lwire, 1); } /* Parameterize the position of a polygon point */ else if (op == XCF_Edit_Param) { makenumericalp(keygen, P_POSITION_X, NULL); makenumericalp(keygen, P_POSITION_Y, NULL); } } } } /*----------------------------------------------------------------------*/ /* Handle attachment of edited elements to nearby elements */ /*----------------------------------------------------------------------*/ void attach_to() { /* Conditions: One element is selected, key "A" is pressed. */ /* Then there must exist a spline, polygon, or arc to attach to. */ if (areastruct.selects <= 1) { short *refsel; if (attachto == 1) { attachto = 0; /* toggle value */ Wprintf("Unconstrained moving"); } else { attachto = 1; if ((refsel = select_element(SPLINE|ARC|POLYGON)) != NULL) { /* transfer refsel over to (global) refselect */ refselect = *(refsel + areastruct.selects - 1); areastruct.selects--; SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(SELTOCOLOR(refsel)); easydraw(refselect, DEFAULTCOLOR); /* restore graphics state */ SetFunction(dpy, areastruct.gc, areastruct.gctype); SetForeground(dpy, areastruct.gc, areastruct.gccolor); Wprintf("Constrained attach"); } else { attachto = 0; Wprintf("Nothing found to attach to"); } } } } /*--------------------------------------------------------------*/ /* This function returns TRUE if the indicated function is */ /* compatible with the current eventmode; that is, whether */ /* the function could ever be called from eventdispatch() */ /* given the existing eventmode. */ /* */ /* Note that this function has to match the dispatch functions. */ /*--------------------------------------------------------------*/ Boolean compatible_function(int function) { switch(function) { case XCF_Linebreak: case XCF_Halfspace: case XCF_Quarterspace: case XCF_TabStop: case XCF_TabForward: case XCF_TabBackward: case XCF_Superscript: case XCF_Subscript: case XCF_Normalscript: case XCF_Underline: case XCF_Overline: case XCF_Nextfont: case XCF_Boldfont: case XCF_Italicfont: case XCF_Normalfont: case XCF_ISO_Encoding: case XCF_Special: case XCF_Parameter: return (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) ? TRUE : FALSE; break; case XCF_Justify: return (eventmode == TEXT_MODE || eventmode == ETEXT_MODE || eventmode == MOVE_MODE || eventmode == COPY_MODE) ? TRUE : FALSE; break; case XCF_Edit_Break: case XCF_Edit_Delete: case XCF_Edit_Insert: case XCF_Edit_Append: return (eventmode == EPOLY_MODE || eventmode == EPATH_MODE) ? TRUE : FALSE; break; case XCF_Edit_Next: return (eventmode == EPOLY_MODE || eventmode == EPATH_MODE || eventmode == EARC_MODE || eventmode == ESPLINE_MODE) ? TRUE : FALSE; break; case XCF_Attach: return (eventmode == EPOLY_MODE || eventmode == EPATH_MODE || eventmode == MOVE_MODE || eventmode == COPY_MODE) ? TRUE : FALSE; break; case XCF_Rotate: case XCF_Flip_X: case XCF_Flip_Y: case XCF_Snap: case XCF_Swap: return (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == NORMAL_MODE) ? TRUE : FALSE; break; case XCF_Library_Pop: return (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) ? TRUE : FALSE; break; case XCF_Library_Edit: case XCF_Library_Delete: case XCF_Library_Duplicate: case XCF_Library_Hide: case XCF_Library_Virtual: case XCF_Library_Move: case XCF_Library_Copy: return (eventmode == CATALOG_MODE) ? TRUE : FALSE; break; case XCF_Library_Directory: case XCF_Next_Library: return (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE || eventmode == ASSOC_MODE) ? TRUE : FALSE; break; case XCF_Select: return (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ? TRUE : FALSE; break; case XCF_Pop: return (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == CATALOG_MODE || eventmode == NORMAL_MODE || eventmode == ASSOC_MODE) ? TRUE : FALSE; break; case XCF_Push: return (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ? TRUE : FALSE; break; case XCF_SelectBox: case XCF_Wire: case XCF_Delete: case XCF_Rescale: case XCF_Pin_Label: case XCF_Pin_Global: case XCF_Info_Label: case XCF_Connectivity: case XCF_Box: case XCF_Arc: case XCF_Text: case XCF_Exchange: case XCF_Copy: case XCF_Virtual: case XCF_Page_Directory: case XCF_Join: case XCF_Unjoin: case XCF_Spline: case XCF_Edit: case XCF_Undo: case XCF_Redo: case XCF_Select_Save: case XCF_Unselect: case XCF_Dashed: case XCF_Dotted: case XCF_Solid: case XCF_Prompt: case XCF_Exit: case XCF_Dot: case XCF_Write: case XCF_Netlist: case XCF_Sim: case XCF_SPICE: case XCF_SPICEflat: case XCF_PCB: case XCF_Move: return (eventmode == NORMAL_MODE) ? TRUE : FALSE; break; case XCF_Nothing: case XCF_View: case XCF_Redraw: case XCF_Zoom_In: case XCF_Zoom_Out: case XCF_Pan: case XCF_Double_Snap: case XCF_Halve_Snap: case XCF_SnapTo: case XCF_Page: return TRUE; break; case XCF_Continue_Copy: case XCF_Finish_Copy: return (eventmode == COPY_MODE) ? TRUE : FALSE; case XCF_Finish_Element: return (eventmode == WIRE_MODE || eventmode == BOX_MODE || eventmode == ARC_MODE || eventmode == SPLINE_MODE || eventmode == EPATH_MODE || eventmode == EPOLY_MODE || eventmode == EARC_MODE || eventmode == ESPLINE_MODE) ? TRUE : FALSE; break; case XCF_Continue_Element: case XCF_Cancel_Last: return (eventmode == WIRE_MODE || eventmode == ARC_MODE || eventmode == SPLINE_MODE || eventmode == EPATH_MODE || eventmode == EPOLY_MODE || eventmode == EARC_MODE || eventmode == ESPLINE_MODE) ? TRUE : FALSE; break; case XCF_Finish: return (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE || eventmode == ASSOC_MODE || eventmode == CATALOG_MODE || eventmode == CATTEXT_MODE || eventmode == MOVE_MODE || eventmode == RESCALE_MODE || eventmode == SELAREA_MODE || eventmode == PAN_MODE) ? TRUE : FALSE; break; case XCF_Cancel: return (eventmode == NORMAL_MODE) ? FALSE : TRUE; break; } return FALSE; } /*----------------------------------------------------------------------*/ /* Main event dispatch routine. Call one of the known routines based */ /* on the key binding. Some handling is done by secondary dispatch */ /* routines in other files; when this is done, the key binding is */ /* determined here and the bound operation type passed to the secondary */ /* dispatch routine. */ /*----------------------------------------------------------------------*/ void eventdispatch(int keywstate, int x, int y) { short value; /* For return values from isnbound() */ Boolean r; /* Result of key binding query */ /* Invalid key state returned from getkeysignature(); usually this */ /* means a modifier key pressed by itself. */ if (keywstate == -1) return; /* A few event-specific things */ if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) { /* Add text or controls to currently edited text label */ /* Note that CATTEXT_MODE is not handled here---object */ /* names can only contain normal text characters */ if (r = isbound(keywstate, XCF_Linebreak)) labeltext(RETURN, (char *)1); else if (r = isbound(keywstate, XCF_Halfspace)) labeltext(HALFSPACE, (char *)1); else if (r = isbound(keywstate, XCF_Quarterspace)) labeltext(QTRSPACE, (char *)1); else if (r = isbound(keywstate, XCF_TabStop)) labeltext(TABSTOP, (char *)1); else if (r = isbound(keywstate, XCF_TabForward)) labeltext(TABFORWARD, (char *)1); else if (r = isbound(keywstate, XCF_TabBackward)) labeltext(TABBACKWARD, (char *)1); else if (r = isbound(keywstate, XCF_Superscript)) labeltext(SUPERSCRIPT, (char *)1); else if (r = isbound(keywstate, XCF_Subscript)) labeltext(SUBSCRIPT, (char *)1); else if (r = isbound(keywstate, XCF_Normalscript)) labeltext(NORMALSCRIPT, (char *)1); else if (r = isbound(keywstate, XCF_Underline)) labeltext(UNDERLINE, (char *)1); else if (r = isbound(keywstate, XCF_Overline)) labeltext(OVERLINE, (char *)1); else if (r = isbound(keywstate, XCF_Nextfont)) setfont(NULL, 1000, NULL); else if (r = isbound(keywstate, XCF_Boldfont)) fontstyle(NULL, 1, NULL); else if (r = isbound(keywstate, XCF_Italicfont)) fontstyle(NULL, 2, NULL); else if (r = isbound(keywstate, XCF_Normalfont)) fontstyle(NULL, 0, NULL); else if (r = isbound(keywstate, XCF_ISO_Encoding)) fontencoding(NULL, 2, NULL); else if (r = isnbound(keywstate, XCF_Justify, &value)) rejustify(value); else if (r = isbound(keywstate, XCF_Parameter)) insertparam(); else if (r = isbound(keywstate, XCF_Special)) r = dospecial(); if (r) return; } if (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE || eventmode == ETEXT_MODE) { r = labeltext(keywstate, NULL); if (r) return; } /* Ways of changing functionality while in edit mode */ /* (this ought to be substantially improved) */ if (eventmode == EPOLY_MODE || eventmode == EPATH_MODE) { if (r = isbound(keywstate, XCF_Edit_Break)) poly_edit_op(XCF_Edit_Break); else if (r = isbound(keywstate, XCF_Edit_Delete)) poly_edit_op(XCF_Edit_Delete); else if (r = isbound(keywstate, XCF_Edit_Insert)) poly_edit_op(XCF_Edit_Insert); else if (r = isbound(keywstate, XCF_Edit_Append)) poly_edit_op(XCF_Edit_Append); else if (r = isbound(keywstate, XCF_Edit_Next)) edit_next(); else if (r = isbound(keywstate, XCF_Attach)) attach_to(); if (r) return; } else if (eventmode == EARC_MODE || eventmode == ESPLINE_MODE) { if (isbound(keywstate, XCF_Edit_Next)) { edit_next(); return; } } if (eventmode == MOVE_MODE || eventmode == COPY_MODE) { /* A few events can occur in MOVE and COPY2 modes, while */ /* a "drag" operation is in progress. */ snap(x, y, &areastruct.save); if (r = isnbound(keywstate, XCF_Rotate, &value)) elementrotate(value); else if (r = isbound(keywstate, XCF_Flip_X)) elementflip(); else if (r = isbound(keywstate, XCF_Flip_Y)) elementvflip(); else if (r = isbound(keywstate, XCF_Snap)) snapelement(); else if (r = isbound(keywstate, XCF_Pop)) popobject(NULL, 0, NULL); else if (r = isbound(keywstate, XCF_Push)) pushobject(NULL); else if (r = isnbound(keywstate, XCF_Justify, &value)) rejustify(value); else if (r = isbound(keywstate, XCF_Swap)) swapschem(0, -1); else if (r = isbound(keywstate, XCF_Attach)) attach_to(); if (r) return; } if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) { r = FALSE; if (is_library(topobject) >= 0) { if (r = isbound(keywstate, XCF_Next_Library)) changecat(); else if (r = isbound(keywstate, XCF_Library_Directory)) startcatalog(NULL, LIBLIB, NULL); if (r) return; } if (r = isbound(keywstate, XCF_Library_Pop)) { catalog_op(XCF_Library_Pop, x, y); } else if (r = isbound(keywstate, XCF_Pop)) { eventmode = NORMAL_MODE; catreturn(); } else if (r = isbound(keywstate, XCF_Finish)) { catalog_op(XCF_Library_Pop, x, y); } if (r) return; } if (eventmode == CATALOG_MODE) { /* First set of modes is defined only for the object libraries */ r = FALSE; if (is_library(topobject) >= 0) { if (r = isbound(keywstate, XCF_Library_Copy)) { catalog_op(XCF_Library_Copy, x, y); } else if (r = isbound(keywstate, XCF_Library_Edit)) { window_to_user(x, y, &areastruct.save); unselect_all(); select_element(LABEL); if (areastruct.selects == 1) edit(x, y); } else if (r = isbound(keywstate, XCF_Select)) { catalog_op(XCF_Select, x, y); } else if (r = isbound(keywstate, XCF_Library_Delete)) { catalog_op(XCF_Select, x, y); catdelete(); } else if (r = isbound(keywstate, XCF_Library_Duplicate)) { catalog_op(XCF_Select, x, y); copycat(); } else if (r = isbound(keywstate, XCF_Library_Hide)) { catalog_op(XCF_Select, x, y); cathide(); } else if (r = isbound(keywstate, XCF_Library_Virtual)) { catalog_op(XCF_Select, x, y); catvirtualcopy(); } else if (r = isbound(keywstate, XCF_Push)) { window_to_user(x, y, &areastruct.save); eventmode = NORMAL_MODE; pushobject(NULL); } if (r) return; } /* These macros are defined for all but the LIBLIB page (to which */ /* it will be extended at a later date). */ if (areastruct.topinstance != xobjs.libtop[LIBLIB]) { if (r = isbound(keywstate, XCF_Library_Move)) { catmove(x, y); } else if (r = isbound(keywstate, XCF_Select)) { catalog_op(XCF_Select, x, y); } if (r) return; } /* Drop through if no actions executed */ } if (eventmode == NORMAL_MODE) { /* The following events are restricted to normal mode */ window_to_user(x, y, &areastruct.save); if (r = isbound(keywstate, XCF_Select)) select_add_element(ALL_TYPES); else if (r = isbound(keywstate, XCF_Move)) { eventmode = MOVE_MODE; areastruct.editcycle = 0; if (areastruct.selects == 0) select_element(ALL_TYPES); u2u_snap(&areastruct.save); areastruct.origin = areastruct.save; if (areastruct.selects > 0) { XDefineCursor(dpy, areastruct.areawin, ARROW); #ifdef TCL_WRAPPER Tk_CreateEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #endif } } else if (r = isbound(keywstate, XCF_SelectBox)) { startselect(); } else if (r = isbound(keywstate, XCF_Wire)) { u2u_snap(&areastruct.save); startwire(areastruct.save); eventmode = WIRE_MODE; } else if (r = isbound(keywstate, XCF_Delete)) deletebutton(x, y); else if (r = isbound(keywstate, XCF_Pop)) popobject(NULL, 0, NULL); else if (r = isbound(keywstate, XCF_Push)) pushobject(NULL); else if (r = isbound(keywstate, XCF_Swap)) swapschem(0, -1); else if (r = isbound(keywstate, XCF_Rescale)) { eventmode = RESCALE_MODE; UDrawRescaleBox(&areastruct.save); #ifdef TCL_WRAPPER Tk_CreateEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else xcAddEventHandler(areastruct.area, ButtonMotionMask, False, (xcEventHandler)xlib_drag, NULL); #endif } else if (r = isbound(keywstate, XCF_Pin_Label)) { eventmode = TEXT_MODE; textbutton(LOCAL, x, y); } else if (r = isbound(keywstate, XCF_Pin_Global)) { eventmode = TEXT_MODE; textbutton(GLOBAL, x, y); } else if (r = isbound(keywstate, XCF_Info_Label)) { eventmode = TEXT_MODE; textbutton(INFO, x, y); } else if (r = isbound(keywstate, XCF_Connectivity)) connectivity(NULL, NULL, NULL); else if (r = isnbound(keywstate, XCF_Rotate, &value)) elementrotate(value); else if (r = isbound(keywstate, XCF_Flip_X)) elementflip(); else if (r = isbound(keywstate, XCF_Flip_Y)) elementvflip(); else if (r = isbound(keywstate, XCF_Box)) boxbutton(x, y); else if (r = isbound(keywstate, XCF_Arc)) arcbutton(x, y); else if (r = isbound(keywstate, XCF_Text)) { eventmode = TEXT_MODE; textbutton(NORMAL, x, y); } else if (r = isbound(keywstate, XCF_Snap)) snapelement(); else if (r = isbound(keywstate, XCF_Exchange)) exchange(); else if (r = isbound(keywstate, XCF_Delete)) deletebutton(x, y); else if (r = isbound(keywstate, XCF_Copy)) copy_op(XCF_Copy, x, y); else if (r = isbound(keywstate, XCF_Virtual)) copyvirtual(); else if (r = isbound(keywstate, XCF_Next_Library)) changecat(); else if (r = isbound(keywstate, XCF_Library_Directory)) startcatalog(NULL, LIBLIB, NULL); else if (r = isbound(keywstate, XCF_Page_Directory)) startcatalog(NULL, PAGELIB, NULL); else if (r = isbound(keywstate, XCF_Join)) join(); else if (r = isbound(keywstate, XCF_Unjoin)) unjoin(); else if (r = isbound(keywstate, XCF_Spline)) splinebutton(x, y); else if (r = isbound(keywstate, XCF_Edit)) edit(x, y); else if (r = isbound(keywstate, XCF_Undo)) undo_action(); else if (r = isbound(keywstate, XCF_Redo)) redo_action(); else if (r = isbound(keywstate, XCF_Select_Save)) #ifdef TCL_WRAPPER Tcl_Eval(xcinterp, "xcircuit::promptmakeobject"); #else selectsave(NULL, NULL, NULL); #endif else if (r = isbound(keywstate, XCF_Unselect)) select_add_element(-ALL_TYPES); else if (r = isbound(keywstate, XCF_Dashed)) setline(NULL, DASHED, NULL); else if (r = isbound(keywstate, XCF_Dotted)) setline(NULL, DOTTED, NULL); else if (r = isbound(keywstate, XCF_Solid)) setline(NULL, NORMAL, NULL); else if (r = isbound(keywstate, XCF_Prompt)) docommand(); else if (r = isbound(keywstate, XCF_Nothing)) DoNothing(NULL, NULL, NULL); else if (r = isbound(keywstate, XCF_Exit)) quitcheck(NULL, NULL, NULL); else if (r = isnbound(keywstate, XCF_Justify, &value)) rejustify(value); else if (r = isbound(keywstate, XCF_Dot)) { snap(x, y, &areastruct.save); drawdot(areastruct.save.x, areastruct.save.y); drawarea(NULL, NULL, NULL); } else if (r = isbound(keywstate, XCF_Write)) { #ifdef TCL_WRAPPER Tcl_Eval(xcinterp, "xcircuit::promptsavepage"); #else outputpopup(NULL, NULL, NULL); #endif } else if (isbound(keywstate, XCF_Netlist)) callwritenet(NULL, 0, NULL); /* These are mostly for diagnostics; there's no particular */ /* point in one-keystroke netlist generation. */ else if (r = isbound(keywstate, XCF_Sim)) writenet(topobject, "sim", "sim"); else if (r = isbound(keywstate, XCF_SPICE)) writenet(topobject, "spice", "spc"); else if (r = isbound(keywstate, XCF_SPICEflat)) writenet(topobject, "flatspice", "fspc"); else if (r = isbound(keywstate, XCF_PCB)) writenet(topobject, "pcb", "pcbnet"); if (r) return; } if (eventmode == COPY_MODE) { if (r = isbound(keywstate, XCF_Continue_Copy)) copy_op(XCF_Continue_Copy, x, y); else if (r = isbound(keywstate, XCF_Finish_Copy)) copy_op(XCF_Finish_Copy, x, y); if (r) return; } if (eventmode == BOX_MODE) { if (r = isbound(keywstate, XCF_Finish_Element)) finish_op(XCF_Finish_Element, x, y); if (r) return; } else if (eventmode == WIRE_MODE || eventmode == ARC_MODE || eventmode == SPLINE_MODE || eventmode == EPATH_MODE || eventmode == EARC_MODE || eventmode == EPOLY_MODE || eventmode == ESPLINE_MODE) { if (r = isbound(keywstate, XCF_Finish_Element)) finish_op(XCF_Finish_Element, x, y); else if (r = isbound(keywstate, XCF_Continue_Element)) continue_op(XCF_Continue_Element, x, y); else if (r = isbound(keywstate, XCF_Cancel_Last)) { finish_op(XCF_Cancel_Last, x, y); } if (r) return; } /*-----------------------------------------------------------*/ /* The following events are allowed in any mode, or else the */ /* specific mode is handled by the called routine. */ /*-----------------------------------------------------------*/ if (isbound(keywstate, XCF_View)) zoomview(NULL, NULL, NULL); else if (isbound(keywstate, XCF_Redraw)) drawarea(NULL, NULL, NULL); else if (isbound(keywstate, XCF_Zoom_In)) zoominrefresh(x, y); else if (isbound(keywstate, XCF_Zoom_Out)) zoomoutrefresh(x, y); else if (isnbound(keywstate, XCF_Pan, &value)) panrefresh(value, x, y, 0.3); else if (isbound(keywstate, XCF_Double_Snap)) setsnap(1); else if (isbound(keywstate, XCF_Halve_Snap)) setsnap(-1); else if (isbound(keywstate, XCF_SnapTo)) if (areastruct.snapto) { areastruct.snapto = False; Wprintf("Snap-to off"); } else { areastruct.snapto = True; Wprintf("Snap-to on"); } else if (isnbound(keywstate, XCF_Page, &value)) { if (value < 0 || value > xobjs.pages) Wprintf("Page out of range."); else newpage(value - 1); } /* These are the most general functions---parse them last */ else if (isbound(keywstate, XCF_Finish)) finish_op(XCF_Finish, x, y); else if (isbound(keywstate, XCF_Cancel)) finish_op(XCF_Cancel, x, y); /* Final---flag a warning if the key combination is unknown */ else if (!ismacro(keywstate)) { char *keystring = key_to_string(keywstate); #ifdef HAVE_PYTHON if (python_key_command(keywstate) < 0) { #endif sprintf(_STR, "Key \'%s\' is not bound to a macro", keystring); Wprintf(_STR); #ifdef HAVE_PYTHON } #endif free(keystring); } } /*------------------------------------------------------*/ /* Get a canonical signature for a button/key event */ /*------------------------------------------------------*/ int getkeysignature(XKeyEvent *event) { KeySym keypressed; int keywstate; /* KeySym with prepended state information */ short value; /* For return values from isnbound() */ #ifdef _MSC_VER if (event->keycode == 0 && event->state == 0) return -1; #endif XLookupString(event, _STR, 150, &keypressed, NULL); /* Ignore Shift, Control, Caps Lock, and Meta (Alt) keys */ /* when pressed alone. */ if (keypressed == XK_Control_L || keypressed == XK_Control_R || keypressed == XK_Alt_L || keypressed == XK_Alt_R || keypressed == XK_Caps_Lock || keypressed == XK_Shift_L || keypressed == XK_Shift_R) return -1; /* Only keep key state information pertaining to Shift, Caps Lock, */ /* Control, and Alt (Meta) */ keywstate = (keypressed & 0xffff); /* ASCII values already come upper/lowercase; we only want to register */ /* a Shift key if it's a non-ASCII key or another modifier is in effect */ keywstate |= (((LockMask | ControlMask | Mod1Mask) & event->state) << 16); if (keywstate > 255) keywstate |= ((ShiftMask & event->state) << 16); /* Treat button events and key events in the same way by setting */ /* a key state for buttons */ if (keypressed == 0) keywstate |= (((Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask | ShiftMask) & event->state) << 16); return keywstate; } /*------------------------*/ /* Handle keyboard inputs */ /*------------------------*/ void keyhandler(xcWidget w, caddr_t clientdata, XKeyEvent *event) { int keywstate; /* KeySym with prepended state information */ int j, func; #ifdef TCL_WRAPPER if (popups > 0) return; #else if (popups > 0 && help_up == 0) return; #endif if ((event->type == KeyRelease) || (event->type == ButtonRelease)) { /* Register a "tap" event if a key or button was released */ /* while a timeout event is pending. */ if (areastruct.time_id != 0) { xcRemoveTimeOut(areastruct.time_id); areastruct.time_id = 0; keywstate = getkeysignature(event); } else { keywstate = getkeysignature(event); if ((pressmode != 0) && (keywstate == pressmode)) { /* Events that require hold & drag (namely, MOVE_MODE) */ /* must be resolved here. Call finish_op() to ensure */ /* that we restore xcircuit to a state of sanity. */ finish_op(XCF_Finish, event->x, event->y); pressmode = 0; } return; /* Ignore all other release events */ } } /* Check if any bindings match key/button "hold". If so, then start */ /* the timer and wait for key release or timeout. */ else { keywstate = getkeysignature(event); if (keywstate != -1) { /* This is not particularly great coding here, but we need */ /* to establish whether a HOLD modifier binding would apply */ /* in the current eventmode. */ j = 0; while (1) { func = boundfunction(keywstate | HOLD_MASK, j++); if (func == -1) break; else if (compatible_function(func)) { areastruct.save.x = event->x; areastruct.save.y = event->y; areastruct.time_id = xcAddTimeOut(app, PRESSTIME, (xcTimeOutProc)makepress, (ClientData)((pointertype)keywstate)); return; } } } } eventdispatch(keywstate, event->x, event->y); } /*--------------------------------*/ /* Set snap spacing from keyboard */ /*--------------------------------*/ void setsnap(short direction) { float oldsnap = xobjs.pagelist[areastruct.page]->snapspace; char buffer[50]; if (direction > 0) xobjs.pagelist[areastruct.page]->snapspace *= 2; else { if (oldsnap >= 2.0) xobjs.pagelist[areastruct.page]->snapspace /= 2; else { measurestr(xobjs.pagelist[areastruct.page]->snapspace, buffer); sprintf(_STR, "Snap space at minimum value of %s", buffer); Wprintf(_STR); } } if (xobjs.pagelist[areastruct.page]->snapspace != oldsnap) { measurestr(xobjs.pagelist[areastruct.page]->snapspace, buffer); sprintf(_STR, "Snap spacing set to %s", buffer); Wprintf(_STR); drawarea(NULL, NULL, NULL); } } /*-----------------------------------------*/ /* Reposition an object onto the snap grid */ /*-----------------------------------------*/ void snapelement() { short *selectobj; if (!checkselect(ALL_TYPES)) return; SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { easydraw(*selectobj, DOFORALL); switch(SELECTTYPE(selectobj)) { case OBJINST: { objinstptr snapobj = SELTOOBJINST(selectobj); u2u_snap(&snapobj->position); invalidate_netlist(topobject); } break; case GRAPHIC: { graphicptr snapg = SELTOGRAPHIC(selectobj); u2u_snap(&snapg->position); invalidate_netlist(topobject); } break; case LABEL: { labelptr snaplabel = SELTOLABEL(selectobj); u2u_snap(&snaplabel->position); if (snaplabel->pin == LOCAL || snaplabel->pin == GLOBAL) invalidate_netlist(topobject); } break; case POLYGON: { polyptr snappoly = SELTOPOLY(selectobj); pointlist snappoint; for (snappoint = snappoly->points; snappoint < snappoly->points + snappoly->number; snappoint++) u2u_snap(snappoint); if (!nonnetwork(snappoly)) invalidate_netlist(topobject); } break; case ARC: { arcptr snaparc = SELTOARC(selectobj); u2u_snap(&snaparc->position); if (areastruct.snapto) { snaparc->radius = (snaparc->radius / xobjs.pagelist[areastruct.page]->snapspace) * xobjs.pagelist[areastruct.page]->snapspace; snaparc->yaxis = (snaparc->yaxis / xobjs.pagelist[areastruct.page]->snapspace) * xobjs.pagelist[areastruct.page]->snapspace; } calcarc(snaparc); } break; case SPLINE: { splineptr snapspline = SELTOSPLINE(selectobj); short i; for (i = 0; i < 4; i++) u2u_snap(&snapspline->ctrl[i]); calcspline(snapspline); } break; } if (eventmode != NORMAL_MODE) { SetForeground(dpy, areastruct.gc, SELECTCOLOR); easydraw(*selectobj, DOFORALL); } } if (eventmode == NORMAL_MODE) unselect_all(); } /*----------------------------------------------*/ /* Routines to print the cursor position */ /*----------------------------------------------*/ /*----------------------------------------------*/ /* fast integer power-of-10 routine */ /*----------------------------------------------*/ int ipow10(int a) { int i; char istr[12]; switch (a) { case 0: return 1; break; case 1: return 10; break; case 2: return 100; break; case 3: return 1000; break; default: istr[0] = '1'; for (i = 1; i < a + 1; i++) istr[i] = '0'; istr[i] = '\0'; return atoi(istr); break; } } /*--------------------------------------------------*/ /* find greatest common factor between two integers */ /*--------------------------------------------------*/ int calcgcf(int a, int b) { register int mod; if ((mod = a % b) == 0) return (b); else return (calcgcf(b, mod)); } /*--------------------------------------------------------------*/ /* generate a fraction from a float, if possible */ /* fraction returned as a string (must be allocated beforehand) */ /*--------------------------------------------------------------*/ void fraccalc(float xyval, char *fstr) { short i, t, rept; int ip, mant, divisor, denom, numer, rpart; double fp; char num[10], *nptr = &num[2], *sptr; ip = (int)xyval; fp = fabs(xyval - ip); /* write fractional part and grab mantissa as integer */ sprintf(num, "%1.7f", fp); num[8] = '\0'; /* no rounding up! */ sscanf(nptr, "%d", &mant); if (mant != 0) { /* search for repeating substrings */ for (i = 1; i <= 3; i++) { rept = 1; nptr = &num[8] - i; while ((sptr = nptr - rept * i) >= &num[2]) { for (t = 0; t < i; t++) if (*(sptr + t) != *(nptr + t)) break; if (t != i) break; else rept++; } if (rept > 1) break; } nptr = &num[8] - i; sscanf(nptr, "%d", &rpart); /* rpart is repeating part of mantissa */ if (i > 3 || rpart == 0) { /* no repeat */ divisor = calcgcf(1000000, mant); denom = 1000000 / divisor; } else { /* repeat */ int z, p, fd; *nptr = '\0'; sscanf(&num[2], "%d", &z); p = ipow10(i) - 1; mant = z * p + rpart; fd = ipow10(nptr - &num[2]) * p; divisor = calcgcf(fd, mant); denom = fd / divisor; } numer = mant / divisor; if (denom > 1024) sprintf(fstr, "%5.3f", xyval); else if (ip == 0) sprintf(fstr, "%hd/%hd", (xyval > 0) ? numer : -numer, denom); else sprintf(fstr, "%hd %hd/%hd", ip, numer, denom); } else sprintf(fstr, "%hd", ip); } /*------------------------------------------------------------------------------*/ /* Print the position of the cursor in the upper right-hand message window */ /*------------------------------------------------------------------------------*/ void printpos(short xval, short yval) { float f1, f2; float oscale, iscale = (float)xobjs.pagelist[areastruct.page]->drawingscale.y / (float)xobjs.pagelist[areastruct.page]->drawingscale.x; int llen, lwid; u_char wlflag = 0; XPoint *tpoint, *npoint; char *sptr; /* For polygons, print the length (last line of a wire or polygon) or */ /* length and width (box only) */ if (eventmode == BOX_MODE || eventmode == EPOLY_MODE || eventmode == WIRE_MODE) { polyptr lwire = (eventmode == BOX_MODE) ? TOPOLY(ENDPART) : TOPOLY(EDITPART); if ((eventmode == EPOLY_MODE) && (lwire->number > 2)) { /* sanity check on areastruct.editcycle */ if (areastruct.editcycle >= lwire->number) areastruct.editcycle = 0; tpoint = lwire->points + areastruct.editcycle; npoint = lwire->points + checkcycle(lwire->number, 1); llen = wirelength(tpoint, npoint); npoint = lwire->points + checkcycle(lwire->number, -1); lwid = wirelength(tpoint, npoint); wlflag = 3; if (lwire->style & UNCLOSED) { /* unclosed polys */ if (areastruct.editcycle == 0) wlflag = 1; else if (areastruct.editcycle == lwire->number - 1) { wlflag = 1; llen = lwid; } } if ((npoint->y - tpoint->y) == 0) { /* swap width and length */ int tmp = lwid; lwid = llen; llen = tmp; } } else { tpoint = lwire->points + lwire->number - 1; llen = wirelength(tpoint - 1, tpoint); wlflag = 1; } } else if (eventmode == ARC_MODE || eventmode == EARC_MODE) { arcptr larc = (eventmode == ARC_MODE) ? TOARC(ENDPART) : TOARC(EDITPART); llen = larc->radius; if (abs(larc->radius) != larc->yaxis) { lwid = larc->yaxis; wlflag = 3; } else wlflag = 1; } switch (xobjs.pagelist[areastruct.page]->coordstyle) { case INTERNAL: sprintf(_STR, "%g, %g", xval * iscale, yval * iscale); sptr = _STR + strlen(_STR); if (wlflag) { if (wlflag & 2) sprintf(sptr, " (%g x %g)", llen * iscale, lwid * iscale); else sprintf(sptr, " (length %g)", llen * iscale); } break; case DEC_INCH: oscale = xobjs.pagelist[areastruct.page]->outscale * INCHSCALE; f1 = ((float)(xval) * iscale * oscale) / 72.0; f2 = ((float)(yval) * iscale * oscale) / 72.0; sprintf(_STR, "%5.3f, %5.3f in", f1, f2); sptr = _STR + strlen(_STR); if (wlflag) { f1 = ((float)(llen) * iscale * oscale) / 72.0; if (wlflag & 2) { f2 = ((float)(lwid) * iscale * oscale) / 72.0; sprintf(sptr, " (%5.3f x %5.3f in)", f1, f2); } else sprintf(sptr, " (length %5.3f in)", f1); } break; case FRAC_INCH: { char fstr1[30], fstr2[30]; oscale = xobjs.pagelist[areastruct.page]->outscale * INCHSCALE; fraccalc((((float)(xval) * iscale * oscale) / 72.0), fstr1); fraccalc((((float)(yval) * iscale * oscale) / 72.0), fstr2); sprintf(_STR, "%s, %s in", fstr1, fstr2); sptr = _STR + strlen(_STR); if (wlflag) { fraccalc((((float)(llen) * iscale * oscale) / 72.0), fstr1); if (wlflag & 2) { fraccalc((((float)(lwid) * iscale * oscale) / 72.0), fstr2); sprintf(sptr, " (%s x %s in)", fstr1, fstr2); } else sprintf(sptr, " (length %s in)", fstr1); } } break; case CM: oscale = xobjs.pagelist[areastruct.page]->outscale * CMSCALE; f1 = ((float)(xval) * iscale * oscale) / IN_CM_CONVERT; f2 = ((float)(yval) * iscale * oscale) / IN_CM_CONVERT; sprintf(_STR, "%5.3f, %5.3f cm", f1, f2); sptr = _STR + strlen(_STR); if (wlflag) { f1 = ((float)(llen) * iscale * oscale) / IN_CM_CONVERT; if (wlflag & 2) { f2 = ((float)(lwid) * iscale * oscale) / IN_CM_CONVERT; sprintf(sptr, " (%5.3f x %5.3f cm)", f1, f2); } else sprintf(sptr, " (length %5.3f cm)", f1); } break; } W1printf(_STR); } /*---------------------------------------------------*/ /* Find nearest point of intersection of the cursor */ /* position to a wire and move there. */ /*---------------------------------------------------*/ void findwirex(XPoint *endpt1, XPoint *endpt2, XPoint *userpt, XPoint *newpos, int *rot) { long xsq, ysq, zsq; float frac; xsq = sqwirelen(endpt1, endpt2); ysq = sqwirelen(endpt1, userpt); zsq = sqwirelen(endpt2, userpt); frac = 0.5 + (float)(ysq - zsq) / (float)(xsq << 1); if (frac > 1) frac = 1; else if (frac < 0) frac = 0; newpos->x = endpt1->x + (int)((endpt2->x - endpt1->x) * frac); newpos->y = endpt1->y + (int)((endpt2->y - endpt1->y) * frac); *rot = 180 + (int)(INVRFAC * atan2((double)(endpt1->x - endpt2->x), (double)(endpt1->y - endpt2->y))); /* make adjustment for nearest-integer calculation */ /* ((*rot)++, (*rot)-- works if (360 / RSTEPS >= 2)) */ /* ? */ if (*rot > 0) (*rot)++; else if (*rot < 0) (*rot)--; } /*----------------------------------------------------------------*/ /* Find the closest point of attachment from the pointer position */ /* to the "refselect" element. */ /*----------------------------------------------------------------*/ void findattach(XPoint *newpos, int *rot, XPoint *userpt) { XPoint *endpt1, *endpt2; float frac; double tmpang; /* find point of intersection and slope */ if (SELECTTYPE(&refselect) == ARC) { arcptr aarc = SELTOARC(&refselect); float tmpdeg; tmpang = atan2((double)(userpt->y - aarc->position.y) * (double) (abs(aarc->radius)), (double)(userpt->x - aarc->position.x) * (double)aarc->yaxis); /* don't follow the arc beyond its endpoints */ tmpdeg = (float)(tmpang * INVRFAC); if (tmpdeg < 0) tmpdeg += 360; if (((aarc->angle2 > 360) && (tmpdeg > aarc->angle2 - 360) && (tmpdeg < aarc->angle1)) || ((aarc->angle1 < 0) && (tmpdeg > aarc->angle2) && (tmpdeg < aarc->angle1 + 360)) || ((aarc->angle1 >= 0) && (aarc->angle2 <= 360) && ((tmpdeg > aarc->angle2) || (tmpdeg < aarc->angle1)))) { float testd1 = aarc->angle1 - tmpdeg; float testd2 = tmpdeg - aarc->angle2; if (testd1 < 0) testd1 += 360; if (testd2 < 0) testd2 += 360; /* if arc is closed, attach to the line between the endpoints */ if (!(aarc->style & UNCLOSED)) { XPoint end1, end2; tmpang = (double) aarc->angle1 / INVRFAC; end1.x = aarc->position.x + abs(aarc->radius) * cos(tmpang); end1.y = aarc->position.y + aarc->yaxis * sin(tmpang); tmpang = (double) aarc->angle2 / INVRFAC; end2.x = aarc->position.x + abs(aarc->radius) * cos(tmpang); end2.y = aarc->position.y + aarc->yaxis * sin(tmpang); findwirex(&end1, &end2, userpt, newpos, rot); return; } else tmpang = (double)((testd1 < testd2) ? aarc->angle1 : aarc->angle2) / INVRFAC; } /* get position in user coordinates nearest to the intersect pt */ newpos->x = aarc->position.x + abs(aarc->radius) * cos(tmpang); newpos->y = aarc->position.y + aarc->yaxis * sin(tmpang); /* rotation of object is normal to the curve of the ellipse */ *rot = 90 - (int)(INVRFAC * tmpang); if (*rot < 0) *rot += 360; } else if (SELECTTYPE(&refselect) == SPLINE) { splineptr aspline = SELTOSPLINE(&refselect); frac = findsplinemin(aspline, userpt); findsplinepos(aspline, frac, newpos, rot); } else if (SELECTTYPE(&refselect) == POLYGON) { polyptr apoly = SELTOPOLY(&refselect); XPoint *testpt, *minpt, *nxtpt; long mindist = 1000000, testdist; for (testpt = apoly->points; testpt < apoly->points + apoly->number - 1; testpt++) { testdist = finddist(testpt, testpt + 1, userpt); if (testdist < mindist) { mindist = testdist; minpt = testpt; nxtpt = testpt + 1; } } if (!(apoly->style & UNCLOSED)) { testdist = finddist(testpt, apoly->points, userpt); if (testdist < mindist) { mindist = testdist; minpt = testpt; nxtpt = apoly->points; } } endpt1 = minpt; endpt2 = nxtpt; findwirex(endpt1, endpt2, userpt, newpos, rot); } } /*--------------------------------------------*/ /* Find closest point in a path to the cursor */ /*--------------------------------------------*/ XPoint *pathclosepoint(pathptr dragpath, XPoint *newpos) { XPoint *rpoint; genericptr *cpoint; short mpoint; int mdist = 1000000, tdist; for (cpoint = dragpath->plist; cpoint < dragpath->plist + dragpath->parts; cpoint++) { switch(ELEMENTTYPE(*cpoint)) { case ARC: tdist = wirelength(&(TOARC(cpoint)->position), newpos); if (tdist < mdist) { mdist = tdist; rpoint = &(TOARC(cpoint)->position); } break; case POLYGON: mpoint = closepoint(TOPOLY(cpoint), newpos); tdist = wirelength(TOPOLY(cpoint)->points + mpoint, newpos); if (tdist < mdist) { mdist = tdist; rpoint = TOPOLY(cpoint)->points + mpoint; } break; case SPLINE: tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[0]), newpos); if (tdist < mdist) { mdist = tdist; rpoint = &(TOSPLINE(cpoint)->ctrl[0]); } tdist = wirelength(&(TOSPLINE(cpoint)->ctrl[3]), newpos); if (tdist < mdist) { mdist = tdist; rpoint = &(TOSPLINE(cpoint)->ctrl[3]); } break; } } return rpoint; } /*-------------------------------------------*/ /* Drag a selected element around the screen */ /*-------------------------------------------*/ void drag(int x, int y) { XEvent again; Boolean eventcheck = False; XPoint userpt; short deltax, deltay; int locx, locy; locx = x; locy = y; /* flush out multiple pointermotion events from the event queue */ /* use only the last motion event */ while (XCheckWindowEvent(dpy, areastruct.areawin, PointerMotionMask | Button1MotionMask, &again) == True) eventcheck = True; if (eventcheck) { XButtonEvent *event = (XButtonEvent *)(&again); locx = (int)event->x; locy = (int)event->y; } /* Determine if this event is supposed to be handled by */ /* trackselarea(), or whether we should not be here at all */ /* (button press and mouse movement in an unsupported mode) */ if (eventmode == SELAREA_MODE) { trackselarea(); return; } else if (eventmode == RESCALE_MODE) { trackrescale(); return; } else if (eventmode == PAN_MODE) { trackpan(locx, locy); return; } else if (eventmode != MOVE_MODE && eventmode != COPY_MODE) return; snap(locx, locy, &userpt); deltax = userpt.x - areastruct.save.x; deltay = userpt.y - areastruct.save.y; if (deltax == 0 && deltay == 0) return; areastruct.save.x = userpt.x; areastruct.save.y = userpt.y; /* set up the graphics state for moving a selected object */ XSetXORFg(SELECTCOLOR, BACKGROUND); SetFunction(dpy, areastruct.gc, GXxor); placeselects(deltax, deltay, &userpt); /* restore graphics state */ SetForeground(dpy, areastruct.gc, areastruct.gccolor); SetFunction(dpy, areastruct.gc, areastruct.gctype); /* print the position and other useful measurements */ printpos(userpt.x, userpt.y); } /*------------------------------------------------------*/ /* Wrapper for drag() for xlib callback compatibility. */ /*------------------------------------------------------*/ void xlib_drag(xcWidget w, caddr_t clientdata, XEvent *event) { XButtonEvent *bevent = (XButtonEvent *)event; drag(bevent->x, bevent->y); } /*----------------------------------------------*/ /* Rotate an element of a path */ /*----------------------------------------------*/ void elemrotate(genericptr *genobj, short direction) { XPoint negpt, *newpts = (XPoint *)NULL; negpt.x = -areastruct.save.x; negpt.y = -areastruct.save.y; switch(ELEMENTTYPE(*genobj)) { case(ARC):{ arcptr rotatearc = TOARC(genobj); rotatearc->angle1 -= (float)(direction); rotatearc->angle2 -= (float)(direction); if (rotatearc->angle1 >= 360) { rotatearc->angle1 -= 360; rotatearc->angle2 -= 360; } else if (rotatearc->angle2 <= 0) { rotatearc->angle1 += 360; rotatearc->angle2 += 360; } newpts = (XPoint *)malloc(sizeof(XPoint)); UTransformPoints(&rotatearc->position, newpts, 1, negpt, 1.0, 0); UTransformPoints(newpts, &rotatearc->position, 1, areastruct.save, 1.0, direction); calcarc(rotatearc); }break; case(SPLINE):{ splineptr rotatespline = TOSPLINE(genobj); newpts = (XPoint *)malloc(4 * sizeof(XPoint)); UTransformPoints(rotatespline->ctrl, newpts, 4, negpt, 1.0, 0); UTransformPoints(newpts, rotatespline->ctrl, 4, areastruct.save, 1.0, direction); calcspline(rotatespline); }break; case(POLYGON):{ polyptr rotatepoly = TOPOLY(genobj); newpts = (XPoint *)malloc(rotatepoly->number * sizeof(XPoint)); UTransformPoints(rotatepoly->points, newpts, rotatepoly->number, negpt, 1.0, 0); UTransformPoints(newpts, rotatepoly->points, rotatepoly->number, areastruct.save, 1.0, direction); }break; } if (newpts) free(newpts); } /*------------------------------------------------------*/ /* Rotate an element or group of elements */ /* Objects and labels, if selected singly, rotate */ /* about their position point. All other elements, */ /* and groups, rotate about the cursor point. */ /*------------------------------------------------------*/ void elementrotate(short direction) { short *selectobj, ld; Boolean single = False; Boolean need_refresh = False; XPoint newpt, negpt; negpt.x = -areastruct.save.x; negpt.y = -areastruct.save.y; if (!checkselect(ALL_TYPES)) return; if (areastruct.selects == 1) single = True; u2u_snap(&areastruct.save); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { /* erase the element */ if (!need_refresh) { SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); easydraw(*selectobj, DOFORALL); } switch(SELECTTYPE(selectobj)) { case(OBJINST):{ objinstptr rotateobj = SELTOOBJINST(selectobj); rotateobj->rotation += direction; while (rotateobj->rotation >= 360) rotateobj->rotation -= 360; while (rotateobj->rotation <= 0) rotateobj->rotation += 360; if (!single) { UTransformPoints(&rotateobj->position, &newpt, 1, negpt, 1.0, 0); UTransformPoints(&newpt, &rotateobj->position, 1, areastruct.save, 1.0, direction); } }break; case(LABEL):{ labelptr rotatetext = SELTOLABEL(selectobj); rotatetext->rotation += direction; while (rotatetext->rotation >= 360) rotatetext->rotation -= 360; while (rotatetext->rotation <= 0) rotatetext->rotation += 360; if (!single) { UTransformPoints(&rotatetext->position, &newpt, 1, negpt, 1.0, 0); UTransformPoints(&newpt, &rotatetext->position, 1, areastruct.save, 1.0, direction); } }break; case(GRAPHIC):{ graphicptr rotateg = SELTOGRAPHIC(selectobj); rotateg->rotation += direction; while (rotateg->rotation >= 360) rotateg->rotation -= 360; while (rotateg->rotation <= 0) rotateg->rotation += 360; rotateg->valid = FALSE; if (!single) { UTransformPoints(&rotateg->position, &newpt, 1, negpt, 1.0, 0); UTransformPoints(&newpt, &rotateg->position, 1, areastruct.save, 1.0, direction); } need_refresh = True; }break; case POLYGON: case ARC: case SPLINE:{ genericptr *genpart = topobject->plist + *selectobj; register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, *genpart); elemrotate(genpart, direction); }break; case PATH:{ genericptr *genpart; pathptr rotatepath = SELTOPATH(selectobj); register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, rotatepath); for (genpart = rotatepath->plist; genpart < rotatepath->plist + rotatepath->parts; genpart++) elemrotate(genpart, direction); }break; } /* redisplay the element */ if ((eventmode != NORMAL_MODE) && !need_refresh) { SetForeground(dpy, areastruct.gc, SELECTCOLOR); easydraw(*selectobj, DOFORALL); } } /* This takes care of all selected instances and labels in one go, */ /* because we only need to know the origin and amount of rotation. */ register_for_undo(XCF_Rotate, UNDO_MORE, areastruct.topinstance, &areastruct.save, (int)direction); if (eventmode == NORMAL_MODE) unselect_all(); pwriteback(areastruct.topinstance); calcbbox(areastruct.topinstance); if (need_refresh) drawarea(NULL, NULL, NULL); } /*----------------------------------------------*/ /* Rescale the current edit element to the */ /* dimensions of the rescale box. */ /*----------------------------------------------*/ void elementrescale() { } /*-------------------------------------------------*/ /* Edit an element in an element-dependent fashion */ /*-------------------------------------------------*/ void edit(int x, int y) { short *selectobj; if (areastruct.selects != 1) selectobj = select_element(LABEL | POLYGON | SPLINE | ARC | PATH); else selectobj = areastruct.selectlist; if (areastruct.selects != 1) { if (areastruct.selects > 1) Wprintf("Select one only to edit"); unselect_all(); return; } areastruct.editpart = *selectobj; XDefineCursor (dpy, areastruct.areawin, EDCURSOR); switch(SELECTTYPE(selectobj)) { case LABEL: { labelptr *lastlabel = (labelptr *)EDITPART; short curfont; XPoint tmppt; TextExtents tmpext; /* save the old string, including parameters */ register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, *lastlabel); unselect_all(); /* fill any NULL instance parameters with the default value */ copyparams(areastruct.topinstance, areastruct.topinstance); /* place text cursor line at point nearest the cursor */ /* unless textend is set. . . */ if (textend == 0) { window_to_user(x, y, &areastruct.save); InvTransformPoints(&areastruct.save, &tmppt, 1, (*lastlabel)->position, (*lastlabel)->scale, (*lastlabel)->rotation); tmpext = ULength((*lastlabel)->string, areastruct.topinstance, 0.0, 0, NULL); tmppt.x += ((*lastlabel)->justify & NOTLEFT ? ((*lastlabel)->justify & RIGHT ? tmpext.width : tmpext.width >> 1) : 0); tmppt.y += ((*lastlabel)->justify & NOTBOTTOM ? ((*lastlabel)->justify & TOP ? tmpext.ascent : (tmpext.ascent + tmpext.base) >> 1) : tmpext.base); if ((*lastlabel)->pin) pinadjust((*lastlabel)->justify, &tmppt.x, NULL, -1); tmpext = ULength((*lastlabel)->string, areastruct.topinstance, 0.0, 0, &tmppt); textpos = tmpext.width; } /* find current font */ curfont = findcurfont(textpos, (*lastlabel)->string, areastruct.topinstance); /* change menu buttons accordingly */ setfontmarks(curfont, (*lastlabel)->justify); tmpext = ULength((*lastlabel)->string, areastruct.topinstance, (*lastlabel)->scale, 0, NULL); areastruct.origin.x = (*lastlabel)->position.x + ((*lastlabel)-> justify & NOTLEFT ? ((*lastlabel)->justify & RIGHT ? 0 : tmpext.width / 2) : tmpext.width); areastruct.origin.y = (*lastlabel)->position.y + ((*lastlabel)-> justify & NOTBOTTOM ? ((*lastlabel)->justify & TOP ? -tmpext.ascent : -(tmpext.ascent + tmpext.base) / 2) : -tmpext.base); if ((*lastlabel)->pin) pinadjust((*lastlabel)->justify, &(areastruct.origin.x), &(areastruct.origin.y), 1); UDrawTLine(*lastlabel); if (eventmode == CATALOG_MODE) eventmode = CATTEXT_MODE; else eventmode = ETEXT_MODE; XDefineCursor(dpy, areastruct.areawin, TEXTPTR); /* write the text at the bottom */ charreport(*lastlabel); } break; case PATH: { /* choose editcycle type etc. from the type of the first */ /* part encountered (for now?). */ window_to_user(x, y, &areastruct.save); pathedit((*((pathptr *)EDITPART))->plist + areastruct.editsubpart, PATH); } break; case POLYGON: case ARC: case SPLINE: window_to_user(x, y, &areastruct.save); pathedit(EDITPART, 0); break; } } /*------------------------------------------------------------------*/ /* edit() routine for path parts */ /*------------------------------------------------------------------*/ void pathedit(genericptr *editpart, short mode) { switch(ELEMENTTYPE(*editpart)) { case POLYGON: { polyptr *lastpoly = (polyptr *)editpart; XPoint *savept; register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, *lastpoly); unselect_all(); /* determine which point of polygon is closest to cursor */ areastruct.editcycle = closepoint(*lastpoly, &areastruct.save); savept = (*lastpoly)->points + areastruct.editcycle; /* Push onto the editstack */ polyeditpush(*lastpoly); /* remember our postion for pointer restore */ areastruct.origin = areastruct.save; checkwarp(savept); XcSetXORFg((*lastpoly)->color, BACKGROUND); XcSetFunction(GXxor); finddir(*lastpoly); xcAddEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackpoly, NULL); eventmode = (mode == PATH) ? EPATH_MODE : EPOLY_MODE; printeditbindings(); printpos(savept->x, savept->y); } break; case SPLINE: { splineptr *lastspline = (splineptr *)editpart; XPoint *curpt; register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, *lastspline); unselect_all(); /* find which point is closest to the cursor */ areastruct.editcycle = (wirelength(&(*lastspline)->ctrl[0], &areastruct.save) < wirelength(&(*lastspline)->ctrl[3], &areastruct.save)) ? 0 : 3; curpt = &(*lastspline)->ctrl[areastruct.editcycle]; /* Push onto the editstack */ splineeditpush(*lastspline); /* remember our postion for pointer restore */ areastruct.origin = areastruct.save; checkwarp(curpt); UDrawXLine((*lastspline)->ctrl[0], (*lastspline)->ctrl[1]); UDrawXLine((*lastspline)->ctrl[3], (*lastspline)->ctrl[2]); XcSetXORFg((*lastspline)->color, BACKGROUND); XcSetFunction(GXxor); xcAddEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackspline, NULL); eventmode = (mode == PATH) ? EPATH_MODE : ESPLINE_MODE; } break; case ARC: { arcptr *lastarc = (arcptr *)editpart; XPoint curpt; float tmpratio, tlen; register_for_undo(XCF_Edit, UNDO_MORE, areastruct.topinstance, *lastarc); unselect_all(); /* find a part of the arc close to the pointer */ tlen = (float)wirelength(&areastruct.save, &((*lastarc)->position)); tmpratio = (float)(abs((*lastarc)->radius)) / tlen; curpt.x = (*lastarc)->position.x + tmpratio * (areastruct.save.x - (*lastarc)->position.x); tmpratio = (float)(*lastarc)->yaxis / tlen; curpt.y = (*lastarc)->position.y + tmpratio * (areastruct.save.y - (*lastarc)->position.y); areastruct.editcycle = 0; saveratio = (double)((*lastarc)->yaxis) / (double)(abs((*lastarc)->radius)); /* Push onto the editstack */ arceditpush(*lastarc); checkwarp(&curpt); UDrawXLine(curpt, (*lastarc)->position); areastruct.save.x = curpt.x; /* for redrawing dotted edit line */ areastruct.save.y = curpt.y; XcSetXORFg((*lastarc)->color, BACKGROUND); XcSetFunction(GXxor); xcAddEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackarc, NULL); eventmode = (mode == PATH) ? EPATH_MODE : EARC_MODE; printpos(curpt.x, curpt.y); } break; } } /*-------------------------------------------------*/ /* Raise/Lower an object */ /*-------------------------------------------------*/ void xc_raise(short *selectno) { genericptr *raiseobj, *genobj, temp; raiseobj = topobject->plist + *selectno; temp = *raiseobj; for (genobj = topobject->plist + *selectno; genobj < topobject->plist + topobject->parts - 1; genobj++) *genobj = *(genobj + 1); *(topobject->plist + topobject->parts - 1) = temp; *selectno = topobject->parts - 1; } /*-------------------------------------------------*/ void xc_lower(short *selectno) { genericptr *lowerobj, *genobj, temp; lowerobj = topobject->plist + *selectno; temp = *lowerobj; for (genobj = topobject->plist + topobject->parts - 2; genobj >= topobject->plist; genobj--) *(genobj + 1) = *genobj; *(topobject->plist) = temp; *selectno = 0; } /*------------------------------------------------------*/ /* Generate a virtual copy of an object instance in the */ /* user library. This is like the library virtual copy */ /* except that it allows the user to generate a library */ /* copy of an existing instance, without having to make */ /* a copy of the master library instance and edit it. */ /* copyvirtual() also allows the library virtual */ /* instance to take on a specific rotation or flip */ /* value, which cannot be done with the library virtual */ /* copy function. */ /*------------------------------------------------------*/ void copyvirtual() { short *selectno, created = 0; objinstptr vcpobj, libinst; for (selectno = areastruct.selectlist; selectno < areastruct.selectlist + areastruct.selects; selectno++) { if (SELECTTYPE(selectno) == OBJINST) { vcpobj = SELTOOBJINST(selectno); libinst = addtoinstlist(USERLIB - LIBRARY, vcpobj->thisobject, TRUE); instcopy(libinst, vcpobj); created++; } } if (created == 0) { Wprintf("No object instances selected for virtual copy!"); } else { unselect_all(); composelib(USERLIB); } } /*------------------------------------------------------*/ /* Exchange the list position (drawing order) of two */ /* elements, or move the position of one element to the */ /* top or bottom. */ /*------------------------------------------------------*/ void exchange() { short *selectno = areastruct.selectlist; genericptr *exchobj, *exchobj2, temp; if (areastruct.selects > 2 || areastruct.selects == 0) { Wprintf("Select 1 or 2 objects"); return; } if (areastruct.selects == 1) { /* lower if on top; raise otherwise */ if (*selectno == topobject->parts - 1) xc_lower(selectno); else xc_raise(selectno); } else { /* exchange the two objects */ exchobj = topobject->plist + *selectno; exchobj2 = topobject->plist + *(selectno + 1); temp = *exchobj; *exchobj = *exchobj2; *exchobj2 = temp; } incr_changes(topobject); clearselects(); drawarea(NULL, NULL, NULL); } /*--------------------------------------------------------*/ /* Flip an element horizontally (POLYGON, ARC, or SPLINE) */ /*--------------------------------------------------------*/ void elhflip(genericptr *genobj) { switch(ELEMENTTYPE(*genobj)) { case POLYGON:{ polyptr flippoly = TOPOLY(genobj); pointlist ppoint; for (ppoint = flippoly->points; ppoint < flippoly->points + flippoly->number; ppoint++) ppoint->x = (areastruct.save.x << 1) - ppoint->x; }break; case ARC:{ arcptr fliparc = TOARC(genobj); float tmpang = 180 - fliparc->angle1; fliparc->angle1 = 180 - fliparc->angle2; fliparc->angle2 = tmpang; if (fliparc->angle2 < 0) { fliparc->angle1 += 360; fliparc->angle2 += 360; } fliparc->radius = -fliparc->radius; fliparc->position.x = (areastruct.save.x << 1) - fliparc->position.x; calcarc(fliparc); }break; case SPLINE:{ splineptr flipspline = TOSPLINE(genobj); int i; for (i = 0; i < 4; i++) flipspline->ctrl[i].x = (areastruct.save.x << 1) - flipspline->ctrl[i].x; calcspline(flipspline); }break; } } /*--------------------------------------------------------*/ /* Flip an element vertically (POLYGON, ARC, or SPLINE) */ /*--------------------------------------------------------*/ void elvflip(genericptr *genobj) { switch(ELEMENTTYPE(*genobj)) { case POLYGON:{ polyptr flippoly = TOPOLY(genobj); pointlist ppoint; for (ppoint = flippoly->points; ppoint < flippoly->points + flippoly->number; ppoint++) ppoint->y = (areastruct.save.y << 1) - ppoint->y; }break; case ARC:{ arcptr fliparc = TOARC(genobj); float tmpang = 360 - fliparc->angle1; fliparc->angle1 = 360 - fliparc->angle2; fliparc->angle2 = tmpang; if (fliparc->angle1 >= 360) { fliparc->angle1 -= 360; fliparc->angle2 -= 360; } fliparc->radius = -fliparc->radius; fliparc->position.y = (areastruct.save.y << 1) - fliparc->position.y; calcarc(fliparc); }break; case SPLINE:{ splineptr flipspline = TOSPLINE(genobj); int i; for (i = 0; i < 4; i++) flipspline->ctrl[i].y = (areastruct.save.y << 1) - flipspline->ctrl[i].y; calcspline(flipspline); }break; } } /*------------------------------------------------------*/ /* Horizontally flip an element */ /*------------------------------------------------------*/ void elementflip() { short *selectobj; Boolean single = False; if (!checkselect(ALL_TYPES)) return; if (areastruct.selects == 1) single = True; u2u_snap(&areastruct.save); register_for_undo(XCF_Flip_X, UNDO_MORE, areastruct.topinstance, &areastruct.save); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { /* erase the object */ SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); easydraw(*selectobj, DOFORALL); switch(SELECTTYPE(selectobj)) { case LABEL:{ labelptr fliplab = SELTOLABEL(selectobj); if ((fliplab->justify & (RIGHT | NOTLEFT)) != NOTLEFT) fliplab->justify ^= (RIGHT | NOTLEFT); if (!single) fliplab->position.x = (areastruct.save.x << 1) - fliplab->position.x; }break; case GRAPHIC:{ graphicptr flipg = SELTOGRAPHIC(selectobj); flipg->scale = -flipg->scale; flipg->valid = FALSE; if (!single) flipg->position.x = (areastruct.save.x << 1) - flipg->position.x; }break; case OBJINST:{ objinstptr flipobj = SELTOOBJINST(selectobj); flipobj->scale = -flipobj->scale; if (!single) flipobj->position.x = (areastruct.save.x << 1) - flipobj->position.x; }break; case POLYGON: case ARC: case SPLINE: elhflip(topobject->plist + *selectobj); break; case PATH:{ genericptr *genpart; pathptr flippath = SELTOPATH(selectobj); for (genpart = flippath->plist; genpart < flippath->plist + flippath->parts; genpart++) elhflip(genpart); }break; } if (eventmode != NORMAL_MODE) { SetForeground(dpy, areastruct.gc, SELECTCOLOR); easydraw(*selectobj, DOFORALL); } } if (eventmode == NORMAL_MODE) { unselect_all(); incr_changes(topobject); invalidate_netlist(topobject); } pwriteback(areastruct.topinstance); calcbbox(areastruct.topinstance); } /*----------------------------------------------*/ /* Vertically flip an element */ /*----------------------------------------------*/ void elementvflip() /* flip and rotate by 180 degrees */ { short *selectobj; short fsign; Boolean single = False; if (!checkselect(ALL_TYPES)) return; if (areastruct.selects == 1) single = True; u2u_snap(&areastruct.save); register_for_undo(XCF_Flip_Y, UNDO_MORE, areastruct.topinstance, &areastruct.save); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { /* erase the object */ SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); easydraw(*selectobj, DOFORALL); switch(SELECTTYPE(selectobj)) { case(LABEL):{ labelptr fliplab = SELTOLABEL(selectobj); if ((fliplab->justify & (TOP | NOTBOTTOM)) != NOTBOTTOM) fliplab->justify ^= (TOP | NOTBOTTOM); if (!single) fliplab->position.y = (areastruct.save.y << 1) - fliplab->position.y; } break; case(OBJINST):{ objinstptr flipobj = SELTOOBJINST(selectobj); flipobj->scale = -(flipobj->scale); flipobj->rotation += 180; while (flipobj->rotation >= 360) flipobj->rotation -= 360; if (!single) flipobj->position.y = (areastruct.save.y << 1) - flipobj->position.y; }break; case(GRAPHIC):{ graphicptr flipg = SELTOGRAPHIC(selectobj); flipg->scale = -(flipg->scale); flipg->rotation += 180; while (flipg->rotation >= 360) flipg->rotation -= 360; if (!single) flipg->position.y = (areastruct.save.y << 1) - flipg->position.y; }break; case POLYGON: case ARC: case SPLINE: elvflip(topobject->plist + *selectobj); break; case PATH:{ genericptr *genpart; pathptr flippath = SELTOPATH(selectobj); for (genpart = flippath->plist; genpart < flippath->plist + flippath->parts; genpart++) elvflip(genpart); }break; } if (eventmode != NORMAL_MODE) { SetForeground(dpy, areastruct.gc, SELECTCOLOR); easydraw(*selectobj, DOFORALL); } } if (eventmode == NORMAL_MODE) { unselect_all(); incr_changes(topobject); invalidate_netlist(topobject); } pwriteback(areastruct.topinstance); calcbbox(areastruct.topinstance); } /*----------------------------------------*/ /* ButtonPress handler during delete mode */ /*----------------------------------------*/ void deletebutton(int x, int y) { if (checkselect(ALL_TYPES)) { standard_element_delete(ERASE); calcbbox(areastruct.topinstance); } } /*----------------------------------------------------------------------*/ /* Process of element deletion. Remove one element from the indicated */ /* object. */ /*----------------------------------------------------------------------*/ void delete_one_element(objinstptr thisinstance, genericptr thiselement) { objectptr thisobject; genericptr *genobj; Boolean pinchange = False; thisobject = thisinstance->thisobject; /* The netlist contains pointers to elements which no longer */ /* exist on the page, so we should remove them from the netlist. */ if (RemoveFromNetlist(thisobject, thiselement)) pinchange = True; for (genobj = thisobject->plist; genobj < thisobject->plist + thisobject->parts; genobj++) if (*genobj == thiselement) break; if (genobj == thisobject->plist + thisobject->parts) return; for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++) *(genobj - 1) = *genobj; thisobject->parts--; if (pinchange) setobjecttype(thisobject); incr_changes(thisobject); calcbbox(thisinstance); invalidate_netlist(thisobject); /* freenetlist(thisobject); */ } /*----------------------------------------------------------------------*/ /* Process of element deletion. Remove everything in the selection */ /* list from the indicated object, and return a new object containing */ /* only the deleted elements. */ /* */ /* if drawmode is DRAW, we erase the objects as we remove them. */ /* */ /* Note that if "slist" is areastruct.selectlist, it is freed by this */ /* routine (calls freeselects()), but not otherwise. */ /*----------------------------------------------------------------------*/ objectptr delete_element(objinstptr thisinstance, short *slist, int selects, short drawmode) { short *selectobj; objectptr delobj, thisobject; genericptr *genobj; Boolean pinchange = False; if (slist == NULL || selects == 0) return NULL; thisobject = thisinstance->thisobject; delobj = (objectptr) malloc(sizeof(object)); initmem(delobj); if (drawmode) { SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); } for (selectobj = slist; selectobj < slist + selects; selectobj++) { genobj = thisobject->plist + *selectobj; if (drawmode) easydraw(*selectobj, DOFORALL); PLIST_INCR(delobj); *(delobj->plist + delobj->parts) = *genobj; delobj->parts++; /* The netlist contains pointers to elements which no longer */ /* exist on the page, so we should remove them from the netlist. */ if (RemoveFromNetlist(thisobject, *genobj)) pinchange = True; for (++genobj; genobj < thisobject->plist + thisobject->parts; genobj++) *(genobj - 1) = *genobj; thisobject->parts--; reviseselect(slist, selects, selectobj); } if (pinchange) setobjecttype(thisobject); if (slist == areastruct.selectlist) freeselects(); incr_changes(thisobject); /* Treat as one change */ calcbbox(thisinstance); invalidate_netlist(thisobject); /* freenetlist(thisobject); */ if (drawmode) { SetForeground(dpy, areastruct.gc, FOREGROUND); drawarea(NULL, NULL, NULL); } return delobj; } /*----------------------------------------------------------------------*/ /* Wrapper for delete_element(). Remember this deletion for the undo */ /* function. */ /*----------------------------------------------------------------------*/ void standard_element_delete(short drawmode) { objectptr delobj; register_for_undo(XCF_Select, UNDO_MORE, areastruct.topinstance, areastruct.selectlist, areastruct.selects); delobj = delete_element(areastruct.topinstance, areastruct.selectlist, areastruct.selects, drawmode); register_for_undo(XCF_Delete, UNDO_DONE, areastruct.topinstance, delobj, (int)drawmode); } /*----------------------------------------------------------------------*/ /* Another wrapper for delete_element(), in which we do not save the */ /* deletion as an undo event. However, the returned object is saved */ /* on areastruct.editstack, so that the objects can be grabbed. This */ /* allows objects to be carried across pages and through the hierarchy. */ /*----------------------------------------------------------------------*/ void delete_for_xfer(short drawmode, short *slist, int selects) { if (selects > 0) { reset(areastruct.editstack, DESTROY); areastruct.editstack = delete_element(areastruct.topinstance, slist, selects, drawmode); } } /*----------------------------------------------------------------------*/ /* Yet another wrapper for delete_element(), in which we destroy the */ /* object returned and free all associated memory. */ /*----------------------------------------------------------------------*/ void delete_noundo(short drawmode) { objectptr delobj; delobj = delete_element(areastruct.topinstance, areastruct.selectlist, areastruct.selects, drawmode); if (delobj != NULL) reset(delobj, DESTROY); } /*----------------------------------------------------------------------*/ /* Undelete last deleted elements and return a selectlist of the */ /* elements. If "olist" is non-NULL, then the undeleted elements are */ /* placed into the object of thisinstance in the order given by olist, */ /* and a copy of olist is returned. If "olist" is NULL, then the */ /* undeleted elements are placed at the end of thisinstance->thisobject */ /* ->plist, and a new selection list is returned. If "olist" is non- */ /* NULL, then the size of olist had better match the number of objects */ /* in delobj! It is up to the calling routine to check this. */ /*----------------------------------------------------------------------*/ short *xc_undelete(objinstptr thisinstance, objectptr delobj, short mode, short *olist) { objectptr thisobject; genericptr *regen; short *slist, count, position, i; thisobject = thisinstance->thisobject; slist = (short *)malloc(delobj->parts * sizeof(short)); count = 0; if (mode) { SetFunction(dpy, areastruct.gc, GXcopy); } for (regen = delobj->plist; regen < delobj->plist + delobj->parts; regen++) { PLIST_INCR(thisobject); if (olist == NULL) { *(slist + count) = thisobject->parts; *(ENDPART) = *regen; } else { *(slist + count) = *(olist + count); for (i = thisobject->parts; i > *(olist + count); i--) *(thisobject->plist + i) = *(thisobject->plist + i - 1); *(thisobject->plist + i) = *regen; } thisobject->parts++; if (mode) { XTopSetForeground((*regen)->color); easydraw(*(slist + count), DEFAULTCOLOR); } count++; /* If the element has passed parameters (eparam), then we have to */ /* check if the key exists in the new parent object. If not, */ /* delete the parameter. */ if ((*regen)->passed) { eparamptr nextepp, epp = (*regen)->passed; while (epp != NULL) { nextepp = epp->next; if (!match_param(thisobject, epp->key)) { if (epp == (*regen)->passed) (*regen)->passed = nextepp; free_element_param(*regen, epp); } epp = nextepp; } } /* Likewise, string parameters must be checked in labels because */ /* they act like element parameters. */ if (IS_LABEL(*regen)) { labelptr glab = TOLABEL(regen); stringpart *gstr, *lastpart = NULL; for (gstr = glab->string; gstr != NULL; gstr = lastpart->nextpart) { if (gstr->type == PARAM_START) { if (!match_param(thisobject, gstr->data.string)) { free(gstr->data.string); if (lastpart) lastpart->nextpart = gstr->nextpart; else glab->string = gstr->nextpart; free(gstr); gstr = (lastpart) ? lastpart : glab->string; } } lastpart = gstr; } } } incr_changes(thisobject); /* treat as one change */ calcbbox(thisinstance); invalidate_netlist(thisobject); /* flush the delete buffer but don't delete the elements */ reset(delobj, SAVE); if (delobj != areastruct.editstack) free(delobj); return slist; } /*----------------------------*/ /* select save object handler */ /*----------------------------*/ void printname(objectptr curobject) { Dimension swidth, swidth2, sarea; char tmpname[256]; char editstr[10], pagestr[10]; char *sptr = tmpname; #ifndef TCL_WRAPPER Arg wargs[1]; #endif short ispage; /* print full string to make message widget proper size */ strcpy(editstr, ((ispage = is_page(curobject)) >= 0) ? "Editing: " : ""); if (strstr(curobject->name, "Page") == NULL && (ispage >= 0)) sprintf(pagestr, " (p. %d)", areastruct.page + 1); else pagestr[0] = '\0'; sprintf(_STR, "%s%s%s", editstr, curobject->name, pagestr); W2printf(_STR); /* Tcl doesn't update width changes immediately. . . what to do? */ /* (i.e., Tk_Width(message2) gives the original width) */ /* TODO */ #ifndef TCL_WRAPPER XtSetArg(wargs[0], XtNwidth, &sarea); XtGetValues(message2, wargs, 1); /* in the remote case that the string is longer than message widget, */ /* truncate the string and denote the truncation with an ellipsis (...) */ strcpy(tmpname, curobject->name); swidth2 = XTextWidth(appdata.xcfont, editstr, strlen(editstr)); swidth = XTextWidth(appdata.xcfont, tmpname, strlen(tmpname)); if ((swidth + swidth2) > sarea) { char *ip; while ((swidth + swidth2) > sarea) { sptr++; swidth = XTextWidth(appdata.xcfont, sptr, strlen(sptr)); } for(ip = sptr; ip < sptr + 3 && *ip != '\0'; ip++) *ip = '.'; sprintf(_STR, "Editing: %s", sptr); W2printf(_STR); } #endif } /* List of strings which appear in the profile and cannot be used as object names---these are the "most likely" offenders */ #define NUMSTRS 40 static char *psstrings[] = {"add", "and", "arc", "arcn", "array", "bitshift", "bop", "copy", "curveto", "dict", "elb", "ele", "ellipse", "eq", "exch", "exec", "fill", "if", "index", "label", "mul", "ne", "neg", "nellip", "not", "or", "pellip", "polyc", "polygon", "pop", "repeat", "roll", "save", "scale", "setgray", "spline", "sub", "transform", "scb", "sce", "xor"}; /*--------------------------------------------------------------*/ /* Make sure that a string does not conflict with postscript */ /* names (commands and definitions found in xcircps2.pro). */ /*--------------------------------------------------------------*/ char *checkpostscriptname(char *teststring, int *errret, objectptr newobj) { int i, j; short dupl; /* flag a duplicate string */ objectptr *libobj; int errtype = 0; char *sptr, *pptr; float dfloat; /* dummy floating-point for syntax check */ aliasptr aref; slistptr sref; /* The first character cannot be "/", which is an "immediately */ /* evaluated name" */ sptr = teststring; while (*sptr == '/') sptr++; /* copy the string from the first valid character */ pptr = (char *)malloc(strlen(sptr) + 2); strcpy(pptr, sptr); /* Check for illegal characters which have syntactical meaning in */ /* PostScript, and the presence of nonprintable characters or */ /* whitespace. */ for (sptr = pptr; *sptr != '\0'; sptr++) { if (*sptr == '/' || *sptr == '}' || *sptr == '{' || *sptr == ']' || *sptr == '[' || *sptr == ')' || *sptr == '(' || *sptr == '<' || *sptr == '>') { *sptr = '_'; errtype = 1; } else if ((!isprint(*sptr)) || isspace(*sptr)) { *sptr = '_'; errtype = 1; } } /* The whole string cannot be interpreted as a number (integer */ /* or floating-point). Appending an underscore suffices to make */ /* the name legal. */ if (sscanf(pptr, "%f", &dfloat) == 1) { int flen; sscanf(pptr, "%f%n", &dfloat, &flen); /* any leftover characters? */ if (flen == strlen(pptr)) { *sptr++ = '_'; *sptr = '\0'; errtype = 2; } } do { dupl = 0; for (i = 0; i < NUMSTRS; i++) if (!strcmp(pptr, psstrings[i])) { pptr = (char *)realloc(pptr, strlen(psstrings[i]) + 2); sprintf(pptr, "_%s", psstrings[i]); dupl = 1; errtype = 3; break; } if (newobj != NULL) { for (i = 0; i < xobjs.numlibs; i++) { for (j = 0; j < xobjs.userlibs[i].number; j++) { libobj = xobjs.userlibs[i].library + j; if (*libobj == newobj) continue; if (!strcmp(pptr, (*libobj)->name)) { pptr = (char *)realloc(pptr, strlen((*libobj)->name) + 2); sprintf(pptr, "_%s", (*libobj)->name); dupl = 1; errtype = 4; } } } /* If we're in the middle of a file load, the name cannot be */ /* the same as an alias, either. */ if (aliastop != NULL) { for (aref = aliastop; aref != NULL; aref = aref->next) { for (sref = aref->aliases; sref != NULL; sref = sref->next) { if (!strcmp(pptr, sref->alias)) { pptr = (char *)realloc(pptr, strlen(sref->alias) + 2); sprintf(pptr, "_%s", sref->alias); dupl = 1; errtype = 4; } } } } } } while (dupl == 1); if (errret) *errret = errtype; return pptr; } /*--------------------------------------------------------------*/ /* Make sure that name for new object does not conflict with */ /* existing object definitions or PostScript commands/defs */ /* found in xcircps2.pro */ /* Return: True if name required change, False otherwise */ /*--------------------------------------------------------------*/ Boolean checkname(objectptr newobj) { int errtype; char *pptr; /* Check for empty string */ if (strlen(newobj->name) == 0) { Wprintf("Blank object name changed to default"); sprintf(newobj->name, "user_object"); } pptr = checkpostscriptname(newobj->name, &errtype, newobj); /* Change name if necessary and report what conflict required the change */ if (strcmp(pptr, newobj->name)) { switch (errtype) { case 0: Wprintf("Created new object"); break; case 1: Wprintf("Replaced illegal character in name with underscore"); break; case 2: Wprintf("Name cannot be an integer number: appended an underscore"); break; case 3: Wprintf("Name conflicts with PostScript reserved word:" " prepended an underscore"); break; case 4: Wprintf("Altered name to avoid conflict with existing object"); break; } strncpy(newobj->name, pptr, 79); } free(pptr); return (errtype == 0) ? False : True; } /*------------------------------------------------------------*/ /* Find the object "dot" in the builtin library, if it exists */ /*------------------------------------------------------------*/ objectptr finddot() { objectptr dotobj; short i, j; for (i = 0; i < xobjs.numlibs; i++) { for (j = 0; j < xobjs.userlibs[i].number; j++) { dotobj = *(xobjs.userlibs[i].library + j); if (!strcmp(dotobj->name, "dot")) { return dotobj; } } } return (objectptr)NULL; } /*--------------------------------------*/ /* Add value origin to all points */ /*--------------------------------------*/ void movepoints(genericptr *ssgen, short deltax, short deltay) { switch(ELEMENTTYPE(*ssgen)) { case(ARC):{ fpointlist sspoints; TOARC(ssgen)->position.x += deltax; TOARC(ssgen)->position.y += deltay; for (sspoints = TOARC(ssgen)->points; sspoints < TOARC(ssgen)->points + TOARC(ssgen)->number; sspoints++) { sspoints->x += deltax; sspoints->y += deltay; } }break; case(POLYGON):{ pointlist sspoints; for (sspoints = TOPOLY(ssgen)->points; sspoints < TOPOLY(ssgen)->points + TOPOLY(ssgen)->number; sspoints++) { sspoints->x += deltax; sspoints->y += deltay; } }break; case(SPLINE):{ fpointlist sspoints; short j; for (sspoints = TOSPLINE(ssgen)->points; sspoints < TOSPLINE(ssgen)->points + INTSEGS; sspoints++) { sspoints->x += deltax; sspoints->y += deltay; } for (j = 0; j < 4; j++) { TOSPLINE(ssgen)->ctrl[j].x += deltax; TOSPLINE(ssgen)->ctrl[j].y += deltay; } }break; } } #ifndef TCL_WRAPPER void xlib_makeobject(xcWidget w, caddr_t nulldata) { domakeobject(-1); } #endif /* !TCL_WRAPPER */ /*----------------------------------------------------------------*/ /* Set the name for a new user-defined object and make the object */ /*----------------------------------------------------------------*/ void domakeobject(int libnum) { objectptr *newobj; objinstptr *newinst; genericptr *ssgen; oparamptr ops, newop; eparamptr epp, newepp; stringpart *sptr; XPoint origin; short loclibnum = libnum; if (libnum == -1) loclibnum = USERLIB - LIBRARY; /* make room for new entry in library list */ xobjs.userlibs[loclibnum].library = (objectptr *) realloc(xobjs.userlibs[loclibnum].library, (xobjs.userlibs[loclibnum].number + 1) * sizeof(objectptr)); newobj = xobjs.userlibs[loclibnum].library + xobjs.userlibs[loclibnum].number; *newobj = delete_element(areastruct.topinstance, areastruct.selectlist, areastruct.selects, NORMAL); if (*newobj == NULL) return; xobjs.userlibs[loclibnum].number++; /* Create the instance of this object so we can compute a bounding box */ NEW_OBJINST(newinst, topobject); topobject->parts++; instancedefaults(*newinst, *newobj, 0, 0); calcbbox(*newinst); /* find closest snap point to bbox center and make this the obj. center */ if (areastruct.center) { origin.x = (*newobj)->bbox.lowerleft.x + (*newobj)->bbox.width / 2; origin.y = (*newobj)->bbox.lowerleft.y + (*newobj)->bbox.height / 2; } else { origin.x = origin.y = 0; } u2u_snap(&origin); instancedefaults(*newinst, *newobj, origin.x, origin.y); for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts; ssgen++) { switch(ELEMENTTYPE(*ssgen)) { case(OBJINST): TOOBJINST(ssgen)->position.x -= origin.x; TOOBJINST(ssgen)->position.y -= origin.y; break; case(GRAPHIC): TOGRAPHIC(ssgen)->position.x -= origin.x; TOGRAPHIC(ssgen)->position.y -= origin.y; break; case(LABEL): TOLABEL(ssgen)->position.x -= origin.x; TOLABEL(ssgen)->position.y -= origin.y; break; case(PATH):{ genericptr *pathlist; for (pathlist = TOPATH(ssgen)->plist; pathlist < TOPATH(ssgen)->plist + TOPATH(ssgen)->parts; pathlist++) { movepoints(pathlist, -origin.x, -origin.y); } }break; default: movepoints(ssgen, -origin.x, -origin.y); break; } } for (ssgen = (*newobj)->plist; ssgen < (*newobj)->plist + (*newobj)->parts; ssgen++) { for (epp = (*ssgen)->passed; epp != NULL; epp = epp->next) { ops = match_param(topobject, epp->key); newop = copyparameter(ops); newop->next = (*newobj)->params; (*newobj)->params = newop; /* Generate an indirect parameter reference from child to parent */ newepp = make_new_eparam(epp->key); newepp->pdata.refkey = strdup(epp->key); newepp->next = (*newinst)->passed; (*newinst)->passed = newepp; } if (IS_LABEL(*ssgen)) { /* Also need to check for substring parameters in labels */ for (sptr = TOLABEL(ssgen)->string; sptr != NULL; sptr = sptr->nextpart) { if (sptr->type == PARAM_START) { ops = match_param(topobject, sptr->data.string); newop = copyparameter(ops); newop->next = (*newobj)->params; (*newobj)->params = newop; /* Generate an indirect parameter reference from child to parent */ newepp = make_new_eparam(sptr->data.string); newepp->pdata.refkey = strdup(sptr->data.string); newepp->next = (*newinst)->passed; (*newinst)->passed = newepp; } } } } /* any parameters in the top-level object that used by the selected */ /* elements must be copied into the new object. */ /* put new object back into place */ (*newobj)->hidden = False; (*newobj)->schemtype = SYMBOL; calcbbox(*newinst); incr_changes(*newobj); /* (netlist invalidation was taken care of by delete_element() */ /* Also, incr_changes(topobject) was done by delete_element()) */ SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground((*newinst)->color); UDrawObject(*newinst, SINGLE, (*newinst)->color, NULL); /* copy name from popup prompt buffer and check */ strcpy((*newobj)->name, _STR2); checkname(*newobj); /* generate library instance for this object (bounding box */ /* should be default, so don't do calcbbox() on it) */ addtoinstlist(loclibnum, *newobj, FALSE); /* recompile the user catalog */ composelib(loclibnum + LIBRARY); } #ifndef TCL_WRAPPER /*-------------------------------------------*/ /* Make a user object from selected elements */ /*-------------------------------------------*/ void selectsave(xcWidget w, caddr_t clientdata, caddr_t calldata) { buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave)); if (areastruct.selects == 0) return; /* nothing was selected */ /* Get a name for the new object */ eventmode = NORMAL_MODE; popdata->dataptr = NULL; popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */ popupprompt(w, "Enter name for new object:", "\0", xlib_makeobject, popdata, NULL); } #endif /*-----------------------------*/ /* Edit-stack support routines */ /*-----------------------------*/ void arceditpush(arcptr lastarc) { arcptr *newarc; NEW_ARC(newarc, areastruct.editstack); areastruct.editstack->parts++; arccopy(*newarc, lastarc); } /*--------------------------------------*/ void splineeditpush(splineptr lastspline) { splineptr *newspline; NEW_SPLINE(newspline, areastruct.editstack); areastruct.editstack->parts++; splinecopy(*newspline, lastspline); } /*--------------------------------------*/ void polyeditpush(polyptr lastpoly) { polyptr *newpoly; NEW_POLY(newpoly, areastruct.editstack); areastruct.editstack->parts++; polycopy(*newpoly, lastpoly); } /*-----------------------------*/ /* Copying support routines */ /*-----------------------------*/ pointlist copypoints(pointlist points, int number) { pointlist rpoints, cpoints, newpoints; rpoints = (pointlist) malloc(number * sizeof(XPoint)); for (newpoints = rpoints, cpoints = points; newpoints < rpoints + number; newpoints++, cpoints++) { newpoints->x = cpoints->x; newpoints->y = cpoints->y; } return rpoints; } /*------------------------------------------*/ void graphiccopy(graphicptr newg, graphicptr copyg) { newg->source = copyg->source; newg->position.x = copyg->position.x; newg->position.y = copyg->position.y; newg->rotation = copyg->rotation; newg->scale = copyg->scale; newg->color = copyg->color; newg->passed = NULL; copyalleparams((genericptr)newg, (genericptr)copyg); newg->valid = FALSE; newg->target = NULL; newg->clipmask = (Pixmap)NULL; } /*------------------------------------------*/ void labelcopy(labelptr newtext, labelptr copytext) { newtext->string = stringcopy(copytext->string); newtext->position.x = copytext->position.x; newtext->position.y = copytext->position.y; newtext->rotation = copytext->rotation; newtext->scale = copytext->scale; newtext->justify = copytext->justify; newtext->color = copytext->color; newtext->passed = NULL; copyalleparams((genericptr)newtext, (genericptr)copytext); newtext->pin = copytext->pin; } /*------------------------------------------*/ void arccopy(arcptr newarc, arcptr copyarc) { newarc->style = copyarc->style; newarc->color = copyarc->color; newarc->position.x = copyarc->position.x; newarc->position.y = copyarc->position.y; newarc->radius = copyarc->radius; newarc->yaxis = copyarc->yaxis; newarc->angle1 = copyarc->angle1; newarc->angle2 = copyarc->angle2; newarc->width = copyarc->width; newarc->passed = NULL; copyalleparams((genericptr)newarc, (genericptr)copyarc); calcarc(newarc); } /*------------------------------------------*/ void polycopy(polyptr newpoly, polyptr copypoly) { newpoly->style = copypoly->style; newpoly->color = copypoly->color; newpoly->width = copypoly->width; newpoly->number = copypoly->number; newpoly->points = copypoints(copypoly->points, copypoly->number); newpoly->passed = NULL; copyalleparams((genericptr)newpoly, (genericptr)copypoly); } /*------------------------------------------*/ void splinecopy(splineptr newspline, splineptr copyspline) { short i; newspline->style = copyspline->style; newspline->color = copyspline->color; newspline->width = copyspline->width; for (i = 0; i < 4; i++) { newspline->ctrl[i].x = copyspline->ctrl[i].x; newspline->ctrl[i].y = copyspline->ctrl[i].y; } for (i = 0; i < INTSEGS; i++) { newspline->points[i].x = copyspline->points[i].x; newspline->points[i].y = copyspline->points[i].y; } newspline->passed = NULL; copyalleparams((genericptr)newspline, (genericptr)copyspline); } /*--------------------------------------------------------------*/ /* Copy an object instance */ /*--------------------------------------------------------------*/ void instcopy(objinstptr newobj, objinstptr copyobj) { newobj->position.x = copyobj->position.x; newobj->position.y = copyobj->position.y; newobj->rotation = copyobj->rotation; newobj->scale = copyobj->scale; newobj->thisobject = copyobj->thisobject; newobj->color = copyobj->color; newobj->bbox.lowerleft.x = copyobj->bbox.lowerleft.x; newobj->bbox.lowerleft.y = copyobj->bbox.lowerleft.y; newobj->bbox.width = copyobj->bbox.width; newobj->bbox.height = copyobj->bbox.height; newobj->passed = NULL; copyalleparams((genericptr)newobj, (genericptr)copyobj); newobj->params = NULL; copyparams(newobj, copyobj); /* If the parameters are the same, the bounding box should be, too. */ if (copyobj->schembbox != NULL) { newobj->schembbox = (BBox *)malloc(sizeof(BBox)); newobj->schembbox->lowerleft.x = copyobj->schembbox->lowerleft.x; newobj->schembbox->lowerleft.y = copyobj->schembbox->lowerleft.y; newobj->schembbox->width = copyobj->schembbox->width; newobj->schembbox->height = copyobj->schembbox->height; } else newobj->schembbox = NULL; } /*--------------------------------------------------------------*/ /* The method for removing objects from a list is to add the */ /* value REMOVE_TAG to the type of each object needing to be */ /* removed, and then calling this routine. */ /*--------------------------------------------------------------*/ void delete_tagged(objectptr thisobject) { Boolean tagged = True; int i, j; genericptr *pgen; short *sobj; while (tagged) { tagged = False; for (i = 0; i < thisobject->parts; i++) { pgen = thisobject->plist + i; if ((*pgen)->type & REMOVE_TAG) { (*pgen)->type &= (~REMOVE_TAG); tagged = True; free_single(*pgen); free(*pgen); for (j = i + 1; j < thisobject->parts; j++) *(thisobject->plist + j - 1) = *(thisobject->plist + j); thisobject->parts--; /* If we destroy elements in the current window, we need to */ /* make sure that the selection list is updated appropriately. */ if ((thisobject == topobject) && (areastruct.selects > 0)) { for (sobj = areastruct.selectlist; sobj < areastruct.selectlist + areastruct.selects; sobj++) if (*sobj > i) (*sobj)--; } /* Also ensure that this element is not referenced in any */ /* netlist. If it is, remove it and mark the netlist as */ /* invalid. */ remove_netlist_element(thisobject, *pgen); } } } } /*-----------------------------------------------------------------*/ /* For copying: Check if an object is about to be placed directly */ /* on top of the same object. If so, delete the one underneath. */ /*-----------------------------------------------------------------*/ void checkoverlap() { short *sobj, *cobj; genericptr *sgen, *pgen; Boolean tagged = False; /* Work through the select list */ for (sobj = areastruct.selectlist; sobj < areastruct.selectlist + areastruct.selects; sobj++) { sgen = topobject->plist + (*sobj); /* For each object being copied, compare it against every object */ /* on the current page (except self). Flag if it's the same. */ for (pgen = topobject->plist; pgen < topobject->plist + topobject->parts; pgen++) { if (pgen == sgen) continue; if (compare_single(sgen, pgen)) { /* Make sure that this object is not part of the selection, */ /* else chaos will reign. */ for (cobj = areastruct.selectlist; cobj < areastruct.selectlist + areastruct.selects; cobj++) { if (pgen == topobject->plist + (*cobj)) break; } /* Tag it for future deletion and prevent further compares */ if (cobj == areastruct.selectlist + areastruct.selects) { tagged = True; (*pgen)->type |= REMOVE_TAG; } } } } if (tagged) { /* Delete the tagged elements */ Wprintf("Duplicate object deleted"); delete_tagged(topobject); } } /*--------------------------------------------------------------*/ /* Direct placement of elements. Assumes that the selectlist */ /* contains all the elements to be positioned. "deltax" and */ /* "deltay" are relative x and y positions to move the */ /* elements. */ /*--------------------------------------------------------------*/ void placeselects(short deltax, short deltay, XPoint *userpt) { short *dragselect; XPoint newpos; int rot; short closest; short doattach = (userpt == NULL) ? 0 : attachto; /* under attachto condition, keep element attached to */ /* the refselect element. */ if (doattach) findattach(&newpos, &rot, userpt); for (dragselect = areastruct.selectlist; dragselect < areastruct.selectlist + areastruct.selects; dragselect++) { switch(SELECTTYPE(dragselect)) { case OBJINST: { objinstptr dragobject = SELTOOBJINST(dragselect); UDrawObject(dragobject, SINGLE, DOFORALL, NULL); if (doattach) { dragobject->position.x = newpos.x; dragobject->position.y = newpos.y; while (rot >= 360) rot -= 360; while (rot < 0) rot += 360; dragobject->rotation = rot; } else { dragobject->position.x += deltax; dragobject->position.y += deltay; } UDrawObject(dragobject, SINGLE, DOFORALL, NULL); } break; case GRAPHIC: { graphicptr dragg = SELTOGRAPHIC(dragselect); UDrawGraphic(dragg); dragg->position.x += deltax; dragg->position.y += deltay; UDrawGraphic(dragg); } break; case LABEL: { labelptr draglabel = SELTOLABEL(dragselect); UDrawString(draglabel, DOFORALL, areastruct.topinstance); if (draglabel->pin == False) UDrawX(draglabel); if (doattach) { draglabel->position.x = newpos.x; draglabel->position.y = newpos.y; draglabel->rotation = rot; } else { draglabel->position.x += deltax; draglabel->position.y += deltay; } UDrawString(draglabel, DOFORALL, areastruct.topinstance); if (draglabel->pin == False) UDrawX(draglabel); } break; case PATH: { pathptr dragpath = SELTOPATH(dragselect); genericptr *pathlist; UDrawPath(dragpath); if (doattach) { XPoint *pdelta = pathclosepoint(dragpath, &newpos); deltax = newpos.x - pdelta->x; deltay = newpos.y - pdelta->y; } for (pathlist = dragpath->plist; pathlist < dragpath->plist + dragpath->parts; pathlist++) { movepoints(pathlist, deltax, deltay); } UDrawPath(dragpath); } break; case POLYGON: { polyptr dragpoly = SELTOPOLY(dragselect); pointlist dragpoints; UDrawPolygon(dragpoly); if (doattach) { closest = closepoint(dragpoly, &newpos); deltax = newpos.x - dragpoly->points[closest].x; deltay = newpos.y - dragpoly->points[closest].y; } for (dragpoints = dragpoly->points; dragpoints < dragpoly->points + dragpoly->number; dragpoints++) { dragpoints->x += deltax; dragpoints->y += deltay; } UDrawPolygon(dragpoly); } break; case SPLINE: { splineptr dragspline = SELTOSPLINE(dragselect); short j; fpointlist dragpoints; UDrawSpline(dragspline); if (doattach) { closest = (wirelength(&dragspline->ctrl[0], &newpos) > wirelength(&dragspline->ctrl[3], &newpos)) ? 3 : 0; deltax = newpos.x - dragspline->ctrl[closest].x; deltay = newpos.y - dragspline->ctrl[closest].y; } for (dragpoints = dragspline->points; dragpoints < dragspline-> points + INTSEGS; dragpoints++) { dragpoints->x += deltax; dragpoints->y += deltay; } for (j = 0; j < 4; j++) { dragspline->ctrl[j].x += deltax; dragspline->ctrl[j].y += deltay; } UDrawSpline(dragspline); } break; case ARC: { arcptr dragarc = SELTOARC(dragselect); fpointlist dragpoints; UDrawArc(dragarc); if (doattach) { deltax = newpos.x - dragarc->position.x; deltay = newpos.y - dragarc->position.y; } dragarc->position.x += deltax; dragarc->position.y += deltay; for (dragpoints = dragarc->points; dragpoints < dragarc-> points + dragarc->number; dragpoints++) { dragpoints->x += deltax; dragpoints->y += deltay; } UDrawArc(dragarc); } break; } } } /*----------------------------------------------------------------------*/ /* Copy handler. Assumes that the selectlist contains the elements */ /* to be copied, and that the initial position of the copy is held */ /* in areastruct.save. */ /*----------------------------------------------------------------------*/ void createcopies() { short *selectobj; if (!checkselect(ALL_TYPES)) return; u2u_snap(&areastruct.save); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { switch(SELECTTYPE(selectobj)) { case LABEL: { /* copy label */ labelptr copytext = SELTOLABEL(selectobj); labelptr *newtext; NEW_LABEL(newtext, topobject); labelcopy(*newtext, copytext); } break; case OBJINST: { /* copy object instance */ objinstptr copyobj = SELTOOBJINST(selectobj); objinstptr *newobj; NEW_OBJINST(newobj, topobject); instcopy(*newobj, copyobj); } break; case GRAPHIC: { /* copy graphic instance */ graphicptr copyg = SELTOGRAPHIC(selectobj); graphicptr *newg; NEW_GRAPHIC(newg, topobject); graphiccopy(*newg, copyg); } break; case PATH: { /* copy path */ pathptr copypath = SELTOPATH(selectobj); pathptr *newpath; genericptr *genpart; NEW_PATH(newpath, topobject); (*newpath)->style = copypath->style; (*newpath)->color = copypath->color; (*newpath)->width = copypath->width; (*newpath)->passed = NULL; copyalleparams((genericptr)*newpath, (genericptr)copypath); (*newpath)->parts = 0; (*newpath)->plist = (genericptr *)malloc(copypath->parts * sizeof(genericptr)); for (genpart = copypath->plist; genpart < copypath->plist + copypath->parts; genpart++) { switch(ELEMENTTYPE(*genpart)){ case ARC: { arcptr copyarc = TOARC(genpart); arcptr *newarc; NEW_ARC(newarc, (*newpath)); arccopy(*newarc, copyarc); (*newpath)->parts++; } break; case POLYGON: { polyptr copypoly = TOPOLY(genpart); polyptr *newpoly; NEW_POLY(newpoly, (*newpath)); polycopy(*newpoly, copypoly); (*newpath)->parts++; } break; case SPLINE: { splineptr copyspline = TOSPLINE(genpart); splineptr *newspline; NEW_SPLINE(newspline, (*newpath)); splinecopy(*newspline, copyspline); (*newpath)->parts++; } break; } } } break; case ARC: { /* copy arc */ arcptr copyarc = SELTOARC(selectobj); arcptr *newarc; NEW_ARC(newarc, topobject); arccopy(*newarc, copyarc); } break; case POLYGON: { /* copy polygons */ polyptr copypoly = SELTOPOLY(selectobj); polyptr *newpoly; NEW_POLY(newpoly, topobject); polycopy(*newpoly, copypoly); } break; case SPLINE: { /* copy spline */ splineptr copyspline = SELTOSPLINE(selectobj); splineptr *newspline; NEW_SPLINE(newspline, topobject); splinecopy(*newspline, copyspline); } break; } /* change selection from the old to the new object */ *selectobj = topobject->parts; topobject->parts++; } } /*--------------------------------------------------------------*/ /* Function which initiates interactive placement of copied */ /* elements. */ /*--------------------------------------------------------------*/ void copydrag() { short *selectobj; if (areastruct.selects > 0) { /* Put all selected objects into the "select" color */ SetFunction(dpy, areastruct.gc, GXxor); for (selectobj = areastruct.selectlist; selectobj < areastruct.selectlist + areastruct.selects; selectobj++) { XSetXORFg(SELTOCOLOR(selectobj), BACKGROUND); easydraw(*selectobj, DOFORALL); } if (eventmode == NORMAL_MODE) { XDefineCursor(dpy, areastruct.areawin, COPYCURSOR); eventmode = COPY_MODE; #ifdef TCL_WRAPPER Tk_CreateEventHandler(areastruct.area, PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else XtAddEventHandler(areastruct.area, PointerMotionMask, False, (XtEventHandler)xlib_drag, NULL); #endif } incr_changes(topobject); /* treat as a single change */ invalidate_netlist(topobject); } } /*-----------------------------------------------------------*/ /* Copy handler for copying from a button push or key event. */ /*-----------------------------------------------------------*/ void copy_op(int op, int x, int y) { short *csel; if (op == XCF_Copy) { window_to_user(x, y, &areastruct.save); createcopies(); /* This function does all the hard work */ copydrag(); /* Start interactive placement */ } else { eventmode = NORMAL_MODE; attachto = 0; Wprintf(""); #ifdef TCL_WRAPPER xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)xctk_drag, NULL); #else xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)xlib_drag, NULL); #endif XDefineCursor(dpy, areastruct.areawin, DEFAULTCURSOR); u2u_snap(&areastruct.save); if (op == XCF_Cancel) { delete_noundo(NORMAL); /* Redraw the screen so that an object directly under */ /* the delete object won't get painted black */ drawarea(NULL, NULL, NULL); } else if (op == XCF_Finish_Copy) { /* If selected objects are the only ones on the page, */ /* then do a full bbox calculation. */ if (topobject->parts == areastruct.selects) calcbbox(areastruct.topinstance); else calcbboxselect(); checkoverlap(); register_for_undo(XCF_Copy, UNDO_MORE, areastruct.topinstance, areastruct.selectlist, areastruct.selects); unselect_all(); } else { /* XCF_Continue_Copy */ SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, SELECTCOLOR); for (csel = areastruct.selectlist; csel < areastruct.selectlist + areastruct.selects; csel++) easydraw(*csel, DOFORALL); if (topobject->parts == areastruct.selects) calcbbox(areastruct.topinstance); else calcbboxselect(); checkoverlap(); register_for_undo(XCF_Copy, UNDO_DONE, areastruct.topinstance, areastruct.selectlist, areastruct.selects); createcopies(); copydrag(); /* Start interactive placement again */ } } } /*----------------------------------------------*/ /* Check for more than one button being pressed */ /*----------------------------------------------*/ Boolean checkmultiple(XButtonEvent *event) { int state = Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask; state &= event->state; /* ((x - 1) & x) is always non-zero if x has more than one bit set */ return (((state - 1) & state) == 0) ? False : True; } /*----------------------------------------------------------------------*/ /* Operation continuation---dependent upon the ongoing operation. */ /* This operation only pertains to a few event modes for which */ /* continuation of action makes sense---drawing wires (polygons), and */ /* editing polygons, arcs, splines, and paths. */ /*----------------------------------------------------------------------*/ void continue_op(int op, int x, int y) { XPoint ppos; if (eventmode != EARC_MODE && eventmode != ARC_MODE) { window_to_user(x, y, &areastruct.save); } snap(x, y, &ppos); printpos(ppos.x, ppos.y); switch(eventmode) { case(COPY_MODE): copy_op(op, x, y); break; case(WIRE_MODE): wire_op(op, x, y); break; case(EPATH_MODE): path_op((*((pathptr *)EDITPART))->plist + areastruct.editsubpart, op, x, y); break; case(BOX_MODE): case(EPOLY_MODE): case (ARC_MODE): case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE): path_op(EDITPART, op, x, y); break; } } /*--------------------------------------------------------------*/ /* Finish or cancel an operation. This forces a return to */ /* "normal" mode, with whatever other side effects are caused */ /* by the operation. */ /*--------------------------------------------------------------*/ void finish_op(int op, int x, int y) { labelptr curlabel; if (eventmode != EARC_MODE && eventmode != ARC_MODE) { window_to_user(x, y, &areastruct.save); } switch(eventmode) { case(BOX_MODE): case(EPOLY_MODE): case (ARC_MODE): case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE): path_op(EDITPART, op, x, y); break; case(EPATH_MODE): path_op((*((pathptr *)EDITPART))->plist + areastruct.editsubpart, op, x, y); break; case (FONTCAT_MODE): case (EFONTCAT_MODE): fontcat_op(op, x, y); eventmode = (eventmode == FONTCAT_MODE) ? TEXT_MODE : ETEXT_MODE; XDefineCursor (dpy, areastruct.areawin, TEXTPTR); break; case(ASSOC_MODE): case(CATALOG_MODE): catalog_op(op, x, y); break; case(WIRE_MODE): wire_op(op, x, y); break; case(COPY_MODE): copy_op(op, x, y); break; case(TEXT_MODE): curlabel = TOLABEL(EDITPART); UDrawTLine(curlabel); if (op == XCF_Cancel) { redrawtext(curlabel); freelabel(curlabel->string); free(curlabel); curlabel = NULL; } else { topobject->parts++; singlebbox(EDITPART); incr_changes(topobject); if (curlabel->pin != NORMAL) invalidate_netlist(topobject); } setdefaultfontmarks(); eventmode = NORMAL_MODE; break; case(ETEXT_MODE): case(CATTEXT_MODE): curlabel = TOLABEL(EDITPART); UDrawTLine(curlabel); if (op == XCF_Cancel) { /* restore the original text */ undrawtext(curlabel); undo_action(); redrawtext(curlabel); if (eventmode == CATTEXT_MODE) eventmode = CATALOG_MODE; Wprintf(""); setdefaultfontmarks(); } else textreturn(); /* Generate "return" key character */ textend = 0; break; case(MOVE_MODE): u2u_snap(&areastruct.save); #ifdef TCL_WRAPPER Tk_DeleteEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else xcRemoveEventHandler(areastruct.area, ButtonMotionMask, FALSE, (xcEventHandler)xlib_drag, NULL); #endif if (op == XCF_Cancel) { /* If we came from the library with an object instance, in */ /* MOVE_MODE, then "cancel" should delete the element. */ /* Otherwise, put the position of the element back to what */ /* it was before we started the move. The difference is */ /* indicated by the value of areastruct.editcycle. */ if (areastruct.editcycle == 1) delete_noundo(NORMAL); else placeselects(areastruct.origin.x - areastruct.save.x, areastruct.origin.y - areastruct.save.y, NULL); drawarea(NULL, NULL, NULL); } else { if (areastruct.selects > 0) { register_for_undo(XCF_Move, UNDO_MORE, areastruct.topinstance, (int)(areastruct.save.x - areastruct.origin.x), (int)(areastruct.save.y - areastruct.origin.y)); pwriteback(areastruct.topinstance); incr_changes(topobject); invalidate_netlist(topobject); } Wprintf(""); /* full calc needed: move may shrink bbox */ calcbbox(areastruct.topinstance); checkoverlap(); } clearselects(); attachto = 0; eventmode = NORMAL_MODE; break; case(RESCALE_MODE): UDrawRescaleBox(&areastruct.save); #ifdef TCL_WRAPPER Tk_DeleteEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #endif elementrescale(); eventmode = NORMAL_MODE; break; case(SELAREA_MODE): UDrawBox(areastruct.origin, areastruct.save); #ifdef TCL_WRAPPER Tk_DeleteEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #endif /* Zero-width boxes act like a normal selection. Otherwise, */ /* proceed with the area select. */ if ((areastruct.origin.x == areastruct.save.x) && (areastruct.origin.y == areastruct.save.y)) select_add_element(ALL_TYPES); else selectarea(); break; case(PAN_MODE): u2u_snap(&areastruct.save); #ifdef TCL_WRAPPER Tk_DeleteEventHandler(areastruct.area, PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)xlib_drag, NULL); #endif if (op != XCF_Cancel) panbutton((u_int) 5, (areastruct.width >> 1) - (x - areastruct.origin.x), (areastruct.height >> 1) - (y - areastruct.origin.y), 0); break; } /* Remove any selections */ if ((eventmode == SELAREA_MODE) || (eventmode == PAN_MODE)) eventmode = NORMAL_MODE; else unselect_all(); if (eventmode == NORMAL_MODE) { /* Return any highlighted networks to normal */ highlightnetlist(topobject, areastruct.topinstance, 0); XDefineCursor(dpy, areastruct.areawin, DEFAULTCURSOR); } #ifdef DOUBLEBUFFER drawarea(NULL, NULL, NULL); #endif } /*--------------------------------------------------------------*/ /* Operations for path components */ /*--------------------------------------------------------------*/ void path_op(genericptr *editpart, int op, int x, int y) { switch(ELEMENTTYPE(*editpart)) { case(POLYGON): { if (eventmode == BOX_MODE) { polyptr newbox; newbox = TOPOLY(editpart); UDrawPolygon(newbox); /* prevent length and/or width zero boxes */ if (newbox->points->x != (newbox->points + 2)->x && (newbox->points + 1)->y != (newbox->points + 3)->y) { if (op != XCF_Cancel) { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newbox->color); topobject->parts++; UDrawPolygon(newbox); incr_changes(topobject); if (!nonnetwork(newbox)) invalidate_netlist(topobject); register_for_undo(XCF_Box, UNDO_DONE, areastruct.topinstance, newbox); } else { free_single((genericptr)newbox); free(newbox); } } else { free_single((genericptr)newbox); free(newbox); } xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackbox, NULL); eventmode = NORMAL_MODE; } else { /* EPOLY_MODE or EPATH_MODE */ polyptr newpoly, editpoly; short dotrack = True; newpoly = TOPOLY(editpart); attachto = 0; if (op != XCF_Continue_Element) { dotrack = False; UDrawPolygon(newpoly); } if (op == XCF_Continue_Element) { nextpolycycle(newpoly, 1); polyeditpush(newpoly); } else if (op == XCF_Finish_Element) { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newpoly->color); UDrawPolygon(newpoly); } else { XPoint warppt; free_single((genericptr)newpoly); if (areastruct.editstack->parts > 0) { if (op == XCF_Cancel) { editpoly = TOPOLY(areastruct.editstack->plist); polycopy(newpoly, editpoly); reset(areastruct.editstack, NORMAL); } else { editpoly = TOPOLY(areastruct.editstack->plist + areastruct.editstack->parts - 1); polycopy(newpoly, editpoly); free_single((genericptr)editpoly); free(editpoly); areastruct.editstack->parts--; } if (areastruct.editstack->parts > 0) { dotrack = True; nextpolycycle(newpoly, -1); } else { XcSetFunction(GXcopy); XcSetForeground(newpoly->color); user_to_window(areastruct.origin, &warppt); warppointer(warppt.x, warppt.y); } UDrawPolygon(newpoly); } else { topobject->parts--; free(newpoly); } } pwriteback(areastruct.topinstance); if (!dotrack) { /* Free the editstack */ reset(areastruct.editstack, NORMAL); xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackpoly, NULL); eventmode = NORMAL_MODE; } } } break; case(ARC): { arcptr newarc, editarc; short dotrack = True; newarc = TOARC(editpart); if (op != XCF_Continue_Element) { dotrack = False; UDrawArc(newarc); UDrawXLine(areastruct.save, newarc->position); } if (op == XCF_Continue_Element) { nextarccycle(newarc, 1); arceditpush(newarc); } /* prevent radius zero arcs */ else if (newarc->radius != 0 && newarc->yaxis != 0 && (newarc->angle1 != newarc->angle2)) { if (op == XCF_Finish_Element) { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newarc->color); if (eventmode == ARC_MODE) { topobject->parts++; incr_changes(topobject); register_for_undo(XCF_Arc, UNDO_DONE, areastruct.topinstance, newarc); } UDrawArc(newarc); } else { /* restore previous arc from edit stack */ free_single((genericptr)newarc); if (areastruct.editstack->parts > 0) { if (op == XCF_Cancel) { editarc = TOARC(areastruct.editstack->plist); arccopy(newarc, editarc); reset(areastruct.editstack, NORMAL); } else { editarc = TOARC(areastruct.editstack->plist + areastruct.editstack->parts - 1); arccopy(newarc, editarc); free_single((genericptr)editarc); free(editarc); areastruct.editstack->parts--; } if (areastruct.editstack->parts > 0) { dotrack = True; nextarccycle(newarc, -1); UDrawXLine(areastruct.save, newarc->position); } else { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newarc->color); if (eventmode != ARC_MODE) { XPoint warppt; user_to_window(areastruct.origin, &warppt); warppointer(warppt.x, warppt.y); } } UDrawArc(newarc); } } } else { if (eventmode != ARC_MODE) topobject->parts--; free_single((genericptr)newarc); free(newarc); } pwriteback(areastruct.topinstance); if (!dotrack) { /* Free the editstack */ reset(areastruct.editstack, NORMAL); xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackarc, NULL); eventmode = NORMAL_MODE; } } break; case(SPLINE): { splineptr newspline, editspline; short dotrack = True; newspline = TOSPLINE(editpart); if (op != XCF_Continue_Element) { UDrawEditSpline(newspline); dotrack = False; } if (op == XCF_Continue_Element) { nextsplinecycle(newspline, -1); splineeditpush(newspline); } /* unlikely but possible to create zero-length splines */ else if (newspline->ctrl[0].x != newspline->ctrl[3].x || newspline->ctrl[0].x != newspline->ctrl[1].x || newspline->ctrl[0].x != newspline->ctrl[2].x || newspline->ctrl[0].y != newspline->ctrl[3].y || newspline->ctrl[0].y != newspline->ctrl[1].y || newspline->ctrl[0].y != newspline->ctrl[2].y) { if (op == XCF_Finish_Element) { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newspline->color); if (eventmode == SPLINE_MODE) { topobject->parts++; incr_changes(topobject); register_for_undo(XCF_Spline, UNDO_DONE, areastruct.topinstance, newspline); } UDrawSpline(newspline); } else { /* restore previous spline from edit stack */ free_single((genericptr)newspline); if (areastruct.editstack->parts > 0) { if (op == XCF_Cancel) { editspline = TOSPLINE(areastruct.editstack->plist); splinecopy(newspline, editspline); reset(areastruct.editstack, NORMAL); } else { editspline = TOSPLINE(areastruct.editstack->plist + areastruct.editstack->parts - 1); splinecopy(newspline, editspline); free_single((genericptr)editspline); free(editspline); areastruct.editstack->parts--; } if (areastruct.editstack->parts > 0) { dotrack = True; nextsplinecycle(newspline, 1); UDrawEditSpline(newspline); } else { SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(newspline->color); if (eventmode != SPLINE_MODE) { XPoint warppt; user_to_window(areastruct.origin, &warppt); warppointer(warppt.x, warppt.y); } UDrawSpline(newspline); } } } } else { if (eventmode != SPLINE_MODE) topobject->parts--; free_single((genericptr)newspline); free(newspline); } pwriteback(areastruct.topinstance); if (!dotrack) { /* Free the editstack */ reset(areastruct.editstack, NORMAL); xcRemoveEventHandler(areastruct.area, PointerMotionMask, False, (xcEventHandler)trackspline, NULL); eventmode = NORMAL_MODE; } } break; } /* singlebbox(editpart); */ calcbbox(areastruct.topinstance); } /*-------------------------------------------------------*/ /* Recalculate values for a drawing-area widget resizing */ /*-------------------------------------------------------*/ void resizearea(xcWidget w, caddr_t clientdata, caddr_t calldata) { #ifndef TCL_WRAPPER Arg wargs[2]; #endif XEvent discard; int savewidth = areastruct.width, saveheight = areastruct.height; if ((dpy != NULL) && xcIsRealized(areastruct.area)) { #ifdef TCL_WRAPPER areastruct.width = Tk_Width(w); areastruct.height = Tk_Height(w); #else XtSetArg(wargs[0], XtNwidth, &areastruct.width); XtSetArg(wargs[1], XtNheight, &areastruct.height); XtGetValues(areastruct.area, wargs, 2); #endif if (areastruct.width != savewidth || areastruct.height != saveheight) { #ifdef DOUBLEBUFFER if (dbuf != (Pixmap)NULL) XFreePixmap(dpy, dbuf); dbuf = XCreatePixmap(dpy, areastruct.areawin, areastruct.width, areastruct.height, DefaultDepthOfScreen(xcScreen(w))); #endif reset_gs(); /* Re-compose the directores to match the new dimensions */ composelib(LIBLIB); composelib(PAGELIB); #ifdef XC_WIN32 for (savewidth=0; savewidth> 1), -1.0 / (float)(height >> 1), 1.0); glTranslated(-(width >> 1), -(height >> 1), 0); glClearColor(1.0, 1.0, 1.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_BLEND); glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE_SMOOTH); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE); */ /* ? */ glEnable(GL_POLYGON_SMOOTH); SetFunction(dpy, areastruct.gc, GXcopy); #else XSetFunction(dpy, areastruct.gc, GXcopy); #ifdef DOUBLEBUFFER tmpwin = areastruct.areawin; win = (Window)dbuf; areastruct.areawin = win; if (xobjs.pagelist[areastruct.page]->background.name == (char *)NULL || (copybackground() < 0)) { XSetForeground(dpy, areastruct.gc, BACKGROUND); XFillRectangle(dpy, win, areastruct.gc, 0, 0, areastruct.width, areastruct.height); } #else win = areastruct.areawin; if (xobjs.pagelist[areastruct.page]->background.name != (char *)NULL) copybackground(); else XClearWindow(dpy, win); #endif #endif /* OPENGL */ SetThinLineAttributes(dpy, areastruct.gc, 0, LineSolid, CapRound, JoinBevel); /* draw GRIDCOLOR lines for grid; mark axes in AXESCOLOR */ if (eventmode != CATALOG_MODE && eventmode != ASSOC_MODE && eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE) { spc = xobjs.pagelist[areastruct.page]->gridspace * (*areastruct.vscale); if (areastruct.gridon && spc > 8) { fpart = (float)(-areastruct.pcorner->x) / xobjs.pagelist[areastruct.page]->gridspace; x = xobjs.pagelist[areastruct.page]->gridspace * (fpart - (float)((int)fpart)) * (*areastruct.vscale); fpart = (float)(-areastruct.pcorner->y) / xobjs.pagelist[areastruct.page]->gridspace; y = xobjs.pagelist[areastruct.page]->gridspace * (fpart - (float)((int)fpart)) * (*areastruct.vscale); SetForeground(dpy, areastruct.gc, GRIDCOLOR); for (i = x; i < (float)areastruct.width; i += spc) DrawLine (dpy, win, areastruct.gc, (int)(i + 0.5), 0, (int)(i + 0.5), areastruct.height); for (j = (float)areastruct.height - y; j > 0; j -= spc) DrawLine (dpy, win, areastruct.gc, 0, (int)(j - 0.5), areastruct.width, (int)(j - 0.5)); }; if (areastruct.axeson) { XPoint zeropt; zeropt.x = zeropt.y = 0; SetForeground(dpy, areastruct.gc, AXESCOLOR); user_to_window(zeropt, &originpt); DrawLine (dpy, win, areastruct.gc, originpt.x, 0, originpt.x, areastruct.height); DrawLine (dpy, win, areastruct.gc, 0, originpt.y, areastruct.width, originpt.y); } /* bounding box goes beneath everything except grid/axis lines */ UDrawBBox(); /* draw a little red dot at each snap-to point */ spc2 = xobjs.pagelist[areastruct.page]->snapspace * (*areastruct.vscale); if (areastruct.snapto && spc2 > 8) { float x2, y2; fpart = (float)(-areastruct.pcorner->x) / xobjs.pagelist[areastruct.page]->snapspace; x2 = xobjs.pagelist[areastruct.page]->snapspace * (fpart - (float)((int)fpart)) * (*areastruct.vscale); fpart = (float)(-areastruct.pcorner->y) / xobjs.pagelist[areastruct.page]->snapspace; y2 = xobjs.pagelist[areastruct.page]->snapspace * (fpart - (float)((int)fpart)) * (*areastruct.vscale); #if defined(TCL_WRAPPER) && defined(XC_WIN32) && defined(DOUBLEBUFFER) { HDC hdc = CreateCompatibleDC(NULL); SelectObject(hdc, Tk_GetHWND(win)); #endif SetForeground(dpy, areastruct.gc, SNAPCOLOR); for (i = x2; i < areastruct.width; i += spc2) for (j = areastruct.height - y2; j > 0; j -= spc2) #if defined(TCL_WRAPPER) && defined(XC_WIN32) && defined(DOUBLEBUFFER) SetPixelV(hdc, (int)(i + 0.5), (int)(j - 0.05), areastruct.gc->foreground); #endif DrawPoint (dpy, win, areastruct.gc, (int)(i + 0.5), (int)(j - 0.5)); #if defined(TCL_WRAPPER) && defined(XC_WIN32) && defined(DOUBLEBUFFER) DeleteDC(hdc); } #endif }; SetBackground(dpy, areastruct.gc, BACKGROUND); /* Determine the transformation matrix for the topmost object */ /* and draw the hierarchy above the current edit object (if */ /* "edit-in-place" is selected). */ if (areastruct.editinplace == True) { if (areastruct.stack != NULL) { pushlistptr lastlist, thislist; Matrix mtmp; UPushCTM(); /* save our current state */ /* It's easiest if we first push the current page onto the stack, */ /* then we don't need to treat the top-level page separately. We */ /* pop it at the end. */ push_stack(&areastruct.stack, areastruct.topinstance); thislist = areastruct.stack; while ((thislist != NULL) && (is_library(thislist->thisinst->thisobject) < 0)) { /* Invert the transformation matrix of the instance on the stack */ /* to get the proper transformation matrix of the drawing one */ /* up in the hierarchy. */ UResetCTM(&mtmp); UPreMultCTM(&mtmp, thislist->thisinst->position, thislist->thisinst->scale, thislist->thisinst->rotation); InvertCTM(&mtmp); UPreMultCTMbyMat(DCTM, &mtmp); lastlist = thislist; thislist = thislist->next; /* The following will be true for moves between schematics and symbols */ if ((thislist != NULL) && (thislist->thisinst->thisobject->symschem == lastlist->thisinst->thisobject)) break; } if (lastlist != NULL) { pushlistptr stack = NULL; SetForeground(dpy, areastruct.gc, OFFBUTTONCOLOR); UDrawObject(lastlist->thisinst, SINGLE, DOFORALL, &stack); /* This shouldn't happen, but just in case. . . */ if (stack) free_stack(&stack); } pop_stack(&areastruct.stack); /* restore the original stack state */ UPopCTM(); /* restore the original matrix state */ } } } /* draw all of the elements on the screen */ SetForeground(dpy, areastruct.gc, FOREGROUND); /* Initialize hierstack */ if (areastruct.hierstack) free_stack(&areastruct.hierstack); UDrawObject(areastruct.topinstance, TOPLEVEL, FOREGROUND, &areastruct.hierstack); if (areastruct.hierstack) free_stack(&areastruct.hierstack); /* draw the highlighted netlist, if any */ if (checkvalid(topobject) != -1) if (topobject->highlight.netlist != NULL) highlightnetlist(topobject, areastruct.topinstance, 1); /* draw selected elements in the SELECTION color */ /* special case---single label partial text selected */ if ((areastruct.selects == 1) && SELECTTYPE(areastruct.selectlist) == LABEL && textend > 0 && textpos > textend) { labelptr drawlabel = SELTOLABEL(areastruct.selectlist); UDrawString(drawlabel, DOSUBSTRING, areastruct.topinstance); } else draw_all_selected(); /* fast copy of buffer */ #ifdef OPENGL glXSwapBuffers(dpy, (GLXDrawable)areastruct.areawin); /* Draw interactive elements into the front buffer */ glDrawBuffer(GL_FRONT); #else #ifdef DOUBLEBUFFER areastruct.areawin = tmpwin; SetFunction(dpy, areastruct.gc, GXcopy); XCopyArea(dpy, dbuf, areastruct.areawin, areastruct.gc, 0, 0, areastruct.width, areastruct.height, 0, 0); #endif #endif /* OPENGL */ /* draw pending elements, if any */ if (eventmode != NORMAL_MODE) { SetFunction(dpy, areastruct.gc, GXcopy); if (eventmode == TEXT_MODE) { labelptr newlabel = TOLABEL(EDITPART); UDrawString(newlabel, newlabel->color, areastruct.topinstance); UDrawTLine(newlabel); } else if (eventmode == ETEXT_MODE || eventmode == CATTEXT_MODE) { labelptr newlabel = TOLABEL(EDITPART); UDrawTLine(newlabel); } else if (eventmode == SELAREA_MODE) { UDrawBox(areastruct.origin, areastruct.save); } else if (eventmode == RESCALE_MODE) { UDrawRescaleBox(&areastruct.save); } else { SetFunction(dpy, areastruct.gc, GXxor); if (eventmode == BOX_MODE || eventmode == WIRE_MODE) { polyptr newpoly = TOPOLY(EDITPART); XSetXORFg(newpoly->color, BACKGROUND); UDrawPolygon(newpoly); } else if (eventmode == ARC_MODE) { arcptr newarc = TOARC(EDITPART); XSetXORFg(newarc->color, BACKGROUND); UDrawArc(newarc); UDrawXLine(areastruct.save, newarc->position); } else if (eventmode == SPLINE_MODE) { splineptr newspline = TOSPLINE(EDITPART); XSetXORFg(newspline->color, BACKGROUND); UDrawEditSpline(newspline); } } } /* flush out multiple expose/resize events from the event queue */ while (XCheckWindowEvent(dpy, areastruct.areawin, ExposureMask, &discard) == True); /* end by restoring graphics state */ SetForeground(dpy, areastruct.gc, areastruct.gccolor); SetFunction(dpy, areastruct.gc, areastruct.gctype); } /*-------------------------------------------------------------------------*/