diff options
author | Ruben Undheim <ruben.undheim@gmail.com> | 2018-10-20 17:43:57 +0200 |
---|---|---|
committer | Ruben Undheim <ruben.undheim@gmail.com> | 2018-10-20 17:43:57 +0200 |
commit | 04d5d0ea0f65a434e568fe031f6396caec9b3a8d (patch) | |
tree | decc35c37120084c4a55311cb4e801524369ff57 /events.c |
Import Upstream version 3.8.78.dfsg
Diffstat (limited to 'events.c')
-rw-r--r-- | events.c | 6827 |
1 files changed, 6827 insertions, 0 deletions
diff --git a/events.c b/events.c new file mode 100644 index 0000000..75edfd7 --- /dev/null +++ b/events.c @@ -0,0 +1,6827 @@ +/*-------------------------------------------------------------------------*/ +/* events.c --- xcircuit routines handling Xevents and Callbacks */ +/* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ +/*-------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/* written by Tim Edwards, 8/13/93 */ +/*-------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <ctype.h> + +#ifndef XC_WIN32 +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#define XK_MISCELLANY +#define XK_LATIN1 +#include <X11/keysymdef.h> +#else +#ifdef TCL_WRAPPER +#define XK_MISCELLANY +#define XK_LATIN1 +#include <X11/keysymdef.h> +#endif +#endif + +#ifdef OPENGL +#include <GL/gl.h> +#include <GL/glx.h> +#endif /* OPENGL */ + +/*-------------------------------------------------------------------------*/ +/* Local includes */ +/*-------------------------------------------------------------------------*/ + +#ifdef TCL_WRAPPER +#include <tk.h> +#endif + +#include "xcircuit.h" +#include "colordefs.h" + +#define HOLD_MASK (Mod4Mask << 16) + +/*----------------------------------------------------------------------*/ +/* Function prototype declarations */ +/*----------------------------------------------------------------------*/ +#include "prototypes.h" + +/*-------------------------------------------------------------------------*/ +/* Global Variable definitions */ +/*-------------------------------------------------------------------------*/ + +extern XtAppContext app; +extern Display *dpy; +extern int *appcolors; +extern Cursor appcursors[NUM_CURSORS]; +extern Globaldata xobjs; +extern XCWindowData *areawin; +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 + +Boolean was_preselected; + +/*----------------------------------------------------------------------------*/ +/* 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 areawin->editstack. */ +/*--------------------------------------------------------------*/ + +void transferselects() +{ + short locselects; + objinstptr tobj; + XPoint newpos; + + if (areawin->editstack->parts == 0) return; + + if (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == UNDO_MODE || eventmode == CATMOVE_MODE) { + short ps = topobject->parts; + + freeselects(); + + locselects = areawin->editstack->parts; + areawin->selectlist = xc_undelete(areawin->topinstance, + areawin->editstack, (short)NORMAL, (short *)NULL); + areawin->selects = locselects; + + /* Move all selected items to the cursor position */ + newpos = UGetCursor(); + drag((int)newpos.x, (int)newpos.y); + + /* 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) +{ + areawin->vscale = topobject->viewscale; + areawin->pcorner = topobject->pcorner; + newmatrix(); + + if (killselects) clearselects(); + +#ifdef TCL_WRAPPER + if (xobjs.suspend < 0) + XcInternalTagCall(xcinterp, 2, "page", "goto"); +#endif +} + +/*-------------------------------------------------------*/ +/* 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; + xobjs.pagelist[pagenumber]->pageinst = NULL; + + // If we skipped ahead to pagenumber, fill in the pages in between + for (npage = xobjs.pages; npage < pagenumber; npage++) { + xobjs.pagelist[npage] = (Pagedata *)malloc(sizeof(Pagedata)); + xobjs.pagelist[npage]->pageinst = NULL; + } + + xobjs.pages = pagenumber + 1; + makepagebutton(); + } + + if (eventmode == MOVE_MODE || eventmode == COPY_MODE || eventmode == UNDO_MODE) { + delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects); + undo_type = UNDO_MORE; + } + else { + clearselects(); + undo_type = UNDO_DONE; + } + if (areawin->page != pagenumber) + register_for_undo(XCF_Page, undo_type, areawin->topinstance, + areawin->page, pagenumber); + + if (eventmode != ASSOC_MODE) { + areawin->page = pagenumber; + free_stack(&areawin->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); + } + + /* Write back the current view parameters */ + if (areawin->topinstance != NULL) { + topobject->viewscale = areawin->vscale; + topobject->pcorner = areawin->pcorner; + } + + areawin->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[areawin->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, char *clientdata) +{ + pushlistptr newpush; + + newpush = (pushlistptr)malloc(sizeof(pushlist)); + newpush->next = *stackroot; + newpush->clientdata = clientdata; + 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 *selectobj, *savelist; + int saves; + u_char undo_type = UNDO_DONE; + objinstptr pushinst = thisinst; + + savelist = NULL; + saves = 0; + if (eventmode == MOVE_MODE || eventmode == COPY_MODE) { + savelist = areawin->selectlist; + saves = areawin->selects; + areawin->selectlist = NULL; + areawin->selects = 0; + undo_type = UNDO_MORE; + } + + if (pushinst == NULL) { + selectobj = areawin->selectlist; + if (areawin->selects == 0) { + disable_selects(topobject, savelist, saves); + selectobj = select_element(OBJINST); + enable_selects(topobject, savelist, saves); + } + if (areawin->selects == 0) { + Wprintf("No objects selected."); + return; + } + else if (areawin->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, areawin->topinstance, pushinst); + + /* save the address of the current object to the push stack */ + + push_stack(&areawin->stack, areawin->topinstance, NULL); + + topobject->viewscale = areawin->vscale; + topobject->pcorner = areawin->pcorner; + areawin->topinstance = pushinst; + + /* move selected items to the new object */ + + setpage(TRUE); + transferselects(); + 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 (areawin->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) + && ((areawin->stack->thisinst == xobjs.libtop[LIBRARY]) || + (areawin->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, areawin->selectlist, areawin->selects); + } + else if (eventmode != FONTCAT_MODE && eventmode != EFONTCAT_MODE) + 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, areawin->topinstance); + + topobject->viewscale = areawin->vscale; + topobject->pcorner = areawin->pcorner; + areawin->topinstance = areawin->stack->thisinst; + pop_stack(&areawin->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 */ + + if (eventmode == FONTCAT_MODE || eventmode == EFONTCAT_MODE) + setpage(False); + else { + setpage(True); + setsymschem(); + if (eventmode != ASSOC_MODE) + transferselects(); + } + 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) ? areawin->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 = areawin->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) { + Wprintf("Schematic association to object %s", pageobj->symschem->name); + 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 == areawin->page) { + drawarea(areawin->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; + if (xobjs.suspend >= 0) return; + + bwin = xcWindow(bar); + + if (topobject->bbox.width > 0) { + frac = (float) areawin->width / (float) topobject->bbox.width; + rleft = (long)(frac * (float)(areawin->pcorner.x + - topobject->bbox.lowerleft.x)); + rright = rleft + (long)(frac * (float)areawin->width / areawin->vscale); + } + else { + rleft = 0L; + rright = (long)areawin->width; + } + rmid = (rright + rleft) >> 1; + + if (rleft < 0) rleft = 0; + if (rright > areawin->width) rright = areawin->width; + + XSetFunction(dpy, areawin->gc, GXcopy); + XSetForeground(dpy, areawin->gc, BARCOLOR); + if (rmid > 0 && rleft > 0) + XClearArea(dpy, bwin, 0, 0, (int)rleft, SBARSIZE, FALSE); + XFillRectangle(dpy, bwin, areawin->gc, (int)rleft + 1, 1, + (int)(rright - rleft), SBARSIZE - 1); + if (rright > rmid) + XClearArea(dpy, bwin, (int)rright + 1, 0, areawin->width + - (int)rright, SBARSIZE, FALSE); + XClearArea(dpy, bwin, (int)rmid - 1, 1, 3, SBARSIZE, FALSE); + + XSetFunction(dpy, areawin->gc, areawin->gctype); + XSetForeground(dpy, areawin->gc, areawin->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 (xobjs.suspend >= 0) return; + + if (topobject->bbox.height > 0) { + frac = (float)areawin->height / (float)topobject->bbox.height; + rbot = (long)(frac * (float)(topobject->bbox.lowerleft.y + - areawin->pcorner.y + topobject->bbox.height)); + rtop = rbot - (long)(frac * (float)areawin->height / areawin->vscale); + } + else { + rbot = areawin->height; + rtop = 0; + } + rmid = (rtop + rbot) >> 1; + + if (rtop < 0) rtop = 0; + if (rbot > areawin->height) rbot = areawin->height; + + XSetFunction(dpy, areawin->gc, GXcopy); + XSetForeground(dpy, areawin->gc, BARCOLOR); + if (rmid > 0 && rtop > 0) + XClearArea(dpy, bwin, 0, 0, SBARSIZE, (int)rtop, FALSE); + XFillRectangle(dpy, bwin, areawin->gc, 0, (int)rtop + 2, SBARSIZE, + (int)(rbot - rtop)); + if (rbot > rmid) + XClearArea(dpy, bwin, 0, (int)rbot + 1, SBARSIZE, areawin->height + - (int)rbot, FALSE); + XClearArea(dpy, bwin, 0, (int)rmid - 1, SBARSIZE, 3, FALSE); + + XSetFunction(dpy, areawin->gc, areawin->gctype); + XSetForeground(dpy, areawin->gc, areawin->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 = areawin->pcorner.x; + + if (eventmode == SELAREA_MODE) return; + + newx = (long)(event->x * ((float)topobject->bbox.width / + areawin->width) + topobject->bbox.lowerleft.x - 0.5 * + ((float)areawin->width / areawin->vscale)); + areawin->pcorner.x = (short)newx; + drawhbar(bar, NULL, NULL); + areawin->pcorner.x = savex; + +#ifdef DOUBLEBUFFER + if ((newpx = (long)(newx - savex) * areawin->vscale) == 0) return; + SetFunction(dpy, areawin->gc, GXcopy); + if (newpx > 0) { + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, newpx, 0, + areawin->width - newpx, areawin->height, 0, 0); + XClearArea(dpy, areawin->window, areawin->width - newpx, 0, newpx, + areawin->height, FALSE); + } + else { + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, + areawin->width + newpx, areawin->height, -newpx, 0); + XClearArea(dpy, areawin->window, 0, 0, -newpx, areawin->height, FALSE); + } +#endif +} + +/*------------------------------------------------------*/ +/* End the horizontal scroll and refresh entire screen */ +/*------------------------------------------------------*/ + +void endhbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) +{ + long newx; + short savex = areawin->pcorner.x; + + newx = (long)(event->x * ((float)topobject->bbox.width / + areawin->width) + topobject->bbox.lowerleft.x - 0.5 * + ((float)areawin->width / areawin->vscale)); + + areawin->pcorner.x = (short)newx; + + if ((newx << 1) != (long)((short)(newx << 1)) || checkbounds() == -1) { + areawin->pcorner.x = savex; + Wprintf("Reached boundary: cannot pan further"); + } + else + W3printf(" "); + + areawin->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 = areawin->pcorner.y; + + if (eventmode == SELAREA_MODE) return; + + newy = (int)((areawin->height - event->y) * + ((float)topobject->bbox.height / areawin->height) + + topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height / + areawin->vscale)); + areawin->pcorner.y = (short)newy; + drawvbar(bar, NULL, NULL); + areawin->pcorner.y = savey; + +#ifdef DOUBLEBUFFER + if ((newpy = (long)(newy - savey) * areawin->vscale) == 0) return; + SetFunction(dpy, areawin->gc, GXcopy); + if (newpy > 0) { + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, + areawin->width, areawin->height - newpy, 0, newpy); + XClearArea(dpy, areawin->window, 0, 0, areawin->width, newpy, FALSE); + } + else { + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, -newpy, + areawin->width, areawin->height + newpy, 0, 0); + XClearArea(dpy, areawin->window, 0, areawin->height + newpy, + areawin->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 = areawin->pcorner.y; + short savex = areawin->pcorner.x; + + newpos.x = areawin->origin.x - x; + newpos.y = y - areawin->origin.y; + + areawin->pcorner.x += newpos.x / areawin->vscale; + areawin->pcorner.y += newpos.y / areawin->vscale; + + drawhbar(areawin->scrollbarh, NULL, NULL); + drawvbar(areawin->scrollbarv, NULL, NULL); + +#ifdef DOUBLEBUFFER + newpx = (long)areawin->pcorner.x * areawin->vscale; + newpy = (long)areawin->pcorner.y * areawin->vscale; + + SetFunction(dpy, areawin->gc, GXcopy); + /* To-do: Clear or repaint areas not in copy region */ + +/* + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, + areawin->width, areawin->height, newpx, + areawin->height - newpy); +*/ + drawarea(NULL, NULL, NULL); +#endif + + areawin->pcorner.x = savex; + areawin->pcorner.y = savey; + +} + +/*------------------------------------------------------*/ +/* End the vertical scroll and refresh entire screen */ +/*------------------------------------------------------*/ + +void endvbar(xcWidget bar, caddr_t clientdata, XButtonEvent *event) +{ + long newy; + short savey = areawin->pcorner.y; + + newy = (int)((areawin->height - event->y) * + ((float)topobject->bbox.height / areawin->height) + + topobject->bbox.lowerleft.y - 0.5 * ((float)areawin->height / + areawin->vscale)); + + areawin->pcorner.y = (short)newy; + + if ((newy << 1) != (long)((short)(newy << 1)) || checkbounds() == -1) { + areawin->pcorner.y = savey; + Wprintf("Reached boundary: cannot pan further"); + } + else + W3printf(" "); + + areawin->lastbackground = NULL; + renderbackground(); + drawvbar(bar, NULL, NULL); + drawarea(bar, NULL, NULL); +} + +/*--------------------------------------------------------------------*/ +/* Zoom functions-- zoom box, zoom in, zoom out, and pan. */ +/*--------------------------------------------------------------------*/ + +void postzoom() +{ + W3printf(" "); + areawin->lastbackground = NULL; + renderbackground(); + newmatrix(); +} + +/*--------------------------------------------------------------------*/ + +void zoominbox(int x, int y) +{ + float savescale; + float delxscale, delyscale; + XPoint savell; /* ucenter, ncenter, (jdk)*/ + + savescale = areawin->vscale; + savell.x = areawin->pcorner.x; + savell.y = areawin->pcorner.y; + + /* zoom-box function: corners are in areawin->save and areawin->origin */ + /* select box has lower-left corner in .origin, upper-right in .save */ + /* ignore if zoom box is size zero */ + + if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->origin.y) { + Wprintf("Zoom box of size zero: Ignoring."); + eventmode = NORMAL_MODE; + return; + } + + /* determine whether x or y is limiting factor in zoom */ + delxscale = (areawin->width / areawin->vscale) / + abs(areawin->save.x - areawin->origin.x); + delyscale = (areawin->height / areawin->vscale) / + abs(areawin->save.y - areawin->origin.y); + areawin->vscale *= min(delxscale, delyscale); + + areawin->pcorner.x = min(areawin->origin.x, areawin->save.x) - + (areawin->width / areawin->vscale - + abs(areawin->save.x - areawin->origin.x)) / 2; + areawin->pcorner.y = min(areawin->origin.y, areawin->save.y) - + (areawin->height / areawin->vscale - + abs(areawin->save.y - areawin->origin.y)) / 2; + eventmode = NORMAL_MODE; + + /* check for minimum scale */ + + if (checkbounds() == -1) { + areawin->pcorner.x = savell.x; + areawin->pcorner.y = savell.y; + areawin->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; + } + postzoom(); +} + +/*--------------------------------------------------------------------*/ + +void zoomin(int x, int y) +{ + float savescale; + XPoint ucenter, ncenter, savell; + + savescale = areawin->vscale; + savell.x = areawin->pcorner.x; + savell.y = areawin->pcorner.y; + + window_to_user(areawin->width / 2, areawin->height / 2, &ucenter); + areawin->vscale *= areawin->zoomfactor; + window_to_user(areawin->width / 2, areawin->height / 2, &ncenter); + areawin->pcorner.x += (ucenter.x - ncenter.x); + areawin->pcorner.y += (ucenter.y - ncenter.y); + + /* check for minimum scale */ + + if (checkbounds() == -1) { + areawin->pcorner.x = savell.x; + areawin->pcorner.y = savell.y; + areawin->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 || + eventmode == CATMOVE_MODE) + drag(x, y); + + postzoom(); +} + +/*--------------------------------------------------------------------*/ + +void zoominrefresh(int x, int y) +{ + if (eventmode == SELAREA_MODE) + zoominbox(x, y); + else + zoomin(x, y); + refresh(NULL, NULL, NULL); +} + +/*--------------------------------------------------------------------*/ + +void zoomoutbox(int x, int y) +{ + float savescale; + float delxscale, delyscale, scalefac; + XPoint savell; /* ucenter, ncenter, (jdk)*/ + XlPoint newll; + + savescale = areawin->vscale; + savell.x = areawin->pcorner.x; + savell.y = areawin->pcorner.y; + + /* zoom-box function, analogous to that for zoom-in */ + /* ignore if zoom box is size zero */ + + if (areawin->save.x == areawin->origin.x || areawin->save.y == areawin->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(areawin->save.x - areawin->origin.x) / + (areawin->width / areawin->vscale); + delyscale = abs(areawin->save.y - areawin->origin.y) / + (areawin->height / areawin->vscale); + scalefac = min(delxscale, delyscale); + areawin->vscale *= scalefac; + + /* compute lower-left corner of (reshaped) select box */ + if (delxscale < delyscale) { + newll.y = min(areawin->save.y, areawin->origin.y); + newll.x = (areawin->save.x + areawin->origin.x + - (abs(areawin->save.y - areawin->origin.y) * + areawin->width / areawin->height)) / 2; + } + else { + newll.x = min(areawin->save.x, areawin->origin.x); + newll.y = (areawin->save.y + areawin->origin.y + - (abs(areawin->save.x - areawin->origin.x) * + areawin->height / areawin->width)) / 2; + } + + /* extrapolate to find new lower-left corner of screen */ + newll.x = areawin->pcorner.x - (int)((float)(newll.x - + areawin->pcorner.x) / scalefac); + newll.y = areawin->pcorner.y - (int)((float)(newll.y - + areawin->pcorner.y) / scalefac); + + eventmode = NORMAL_MODE; + areawin->pcorner.x = (short)newll.x; + areawin->pcorner.y = (short)newll.y; + + if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1) + != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) { + areawin->vscale = savescale; + areawin->pcorner.x = savell.x; + areawin->pcorner.y = savell.y; + Wprintf("At maximum scale: cannot scale further."); + return; + } + postzoom(); +} + +/*--------------------------------------------------------------------*/ + +void zoomout(int x, int y) +{ + float savescale; + XPoint ucenter, ncenter, savell; + XlPoint newll; + + savescale = areawin->vscale; + savell.x = areawin->pcorner.x; + savell.y = areawin->pcorner.y; + + window_to_user(areawin->width / 2, areawin->height / 2, &ucenter); + areawin->vscale /= areawin->zoomfactor; + window_to_user(areawin->width / 2, areawin->height / 2, &ncenter); + newll.x = (long)areawin->pcorner.x + (long)(ucenter.x - ncenter.x); + newll.y = (long)areawin->pcorner.y + (long)(ucenter.y - ncenter.y); + areawin->pcorner.x = (short)newll.x; + areawin->pcorner.y = (short)newll.y; + + if ((newll.x << 1) != (long)(areawin->pcorner.x << 1) || (newll.y << 1) + != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) { + areawin->vscale = savescale; + areawin->pcorner.x = savell.x; + areawin->pcorner.y = savell.y; + Wprintf("At maximum scale: cannot scale further."); + return; + } + else if (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == CATMOVE_MODE) + drag(x, y); + + postzoom(); +} + +/*--------------------------------------------------------------------*/ + +void zoomoutrefresh(int x, int y) +{ + if (eventmode == SELAREA_MODE) + zoomoutbox(x, y); + else + zoomout(x, y); + refresh(NULL, NULL, NULL); +} + +/*--------------------------------------*/ +/* Call to XWarpPointer */ +/*--------------------------------------*/ + +void warppointer(int x, int y) +{ + XWarpPointer(dpy, None, areawin->window, 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; (jdk) */ + int xpos, ypos, newllx, newlly; + XPoint savell; /* , newpos; (jdk)*/ + Dimension hwidth = areawin->width >> 1, hheight = areawin->height >> 1; + + savell.x = areawin->pcorner.x; + savell.y = areawin->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; + areawin->save.x = x; + areawin->save.y = y; + u2u_snap(&areawin->save); + areawin->origin = areawin->save; +#ifdef TCL_WRAPPER + Tk_CreateEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcAddEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask , 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)areawin->pcorner.x + (int)((float)xpos / areawin->vscale); + newlly = (int)areawin->pcorner.y + (int)((float)ypos / areawin->vscale); + + areawin->pcorner.x = (short) newllx; + areawin->pcorner.y = (short) newlly; + + if ((newllx << 1) != (long)(areawin->pcorner.x << 1) || (newlly << 1) + != (long)(areawin->pcorner.y << 1) || checkbounds() == -1) { + areawin->pcorner.x = savell.x; + areawin->pcorner.x = savell.y; + Wprintf("Reached bounds: cannot pan further."); + return; + } + else if (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == CATMOVE_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; + + user_to_window(*userpt, &wpoint); + + if (wpoint.x < 0 || wpoint.y < 0 || wpoint.x > areawin->width || + wpoint.y > areawin->height) { + panrefresh(5, wpoint.x, wpoint.y, 0); + wpoint.x = areawin->width >> 1; + wpoint.y = areawin->height >> 1; + /* snap(wpoint.x, wpoint.y, userpt); */ + } + warppointer(wpoint.x, wpoint.y); +} + +/*--------------------------------------------------------------*/ +/* Return a pointer to the element containing a reference point */ +/*--------------------------------------------------------------*/ + +genericptr getsubpart(pathptr editpath, int *idx) +{ + pointselect *tmpptr = NULL; + genericptr *pgen; + + if (idx) *idx = 0; + + for (pgen = editpath->plist; pgen < editpath->plist + editpath->parts; pgen++) { + switch (ELEMENTTYPE(*pgen)) { + case POLYGON: + if (TOPOLY(pgen)->cycle != NULL) { + for (tmpptr = TOPOLY(pgen)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (tmpptr->flags & REFERENCE) return *pgen; + } + break; + case SPLINE: + if (TOSPLINE(pgen)->cycle != NULL) { + for (tmpptr = TOSPLINE(pgen)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (tmpptr->flags & REFERENCE) return *pgen; + } + break; + } + if (idx) (*idx)++; + } + return NULL; +} + +/*--------------------------------------------------------------*/ +/* Return a pointer to the current reference point of an */ +/* edited element (polygon, spline, or path) */ +/*--------------------------------------------------------------*/ + +pointselect *getrefpoint(genericptr genptr, XPoint **refpt) +{ + pointselect *tmpptr = NULL; + genericptr *pgen; + + if (refpt) *refpt = NULL; + switch (genptr->type) { + case POLYGON: + if (((polyptr)genptr)->cycle != NULL) { + for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL; + else if (refpt) *refpt = ((polyptr)genptr)->points + tmpptr->number; + } + break; + case SPLINE: + if (((splineptr)genptr)->cycle != NULL) { + for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (!(tmpptr->flags & REFERENCE)) tmpptr = NULL; + else if (refpt) *refpt = &((splineptr)genptr)->ctrl[tmpptr->number]; + } + break; + case PATH: + for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist + + ((pathptr)genptr)->parts; pgen++) { + if ((tmpptr = getrefpoint(*pgen, refpt)) != NULL) + return tmpptr; + } + break; + default: + tmpptr = NULL; + break; + } + return tmpptr; +} + +/*--------------------------------------------------------------*/ +/* Return next edit point on a polygon, arc, or spline. Do not */ +/* update the cycle of the element. */ +/*--------------------------------------------------------------*/ + +int checkcycle(genericptr genptr, short dir) +{ + pointselect *tmpptr; + short tmppt, points; + genericptr *pgen; + + switch (genptr->type) { + case POLYGON: + if (((polyptr)genptr)->cycle == NULL) + tmpptr = NULL; + else { + for (tmpptr = ((polyptr)genptr)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (!(tmpptr->flags & REFERENCE)) tmpptr = ((polyptr)genptr)->cycle; + } + tmppt = (tmpptr == NULL) ? -1 : tmpptr->number; + points = ((polyptr)genptr)->number; + break; + case SPLINE: + if (((splineptr)genptr)->cycle == NULL) + tmpptr = NULL; + else { + for (tmpptr = ((splineptr)genptr)->cycle;; tmpptr++) { + if (tmpptr->flags & REFERENCE) break; + if (tmpptr->flags & LASTENTRY) break; + } + if (!(tmpptr->flags & REFERENCE)) tmpptr = ((splineptr)genptr)->cycle; + } + tmppt = (tmpptr == NULL) ? -1 : tmpptr->number; + points = 4; + break; + case ARC: + tmpptr = ((arcptr)genptr)->cycle; + tmppt = (tmpptr == NULL) ? -1 : tmpptr->number; + points = 4; + break; + case PATH: + for (pgen = ((pathptr)genptr)->plist; pgen < ((pathptr)genptr)->plist + + ((pathptr)genptr)->parts; pgen++) { + if ((tmppt = checkcycle(*pgen, dir)) >= 0) + return tmppt; + } + break; + default: + tmppt = -1; + break; + } + if (tmppt >= 0) { /* Ignore nonexistent cycles */ + tmppt += dir; + if (tmppt < 0) tmppt += points; + tmppt %= points; + } + return tmppt; +} + +/*--------------------------------------------------------------*/ +/* Change to the next part of a path for editing */ +/* For now, |dir| is treated as 1 regardless of its value. */ +/*--------------------------------------------------------------*/ + +void nextpathcycle(pathptr nextpath, short dir) +{ + genericptr ppart = getsubpart(nextpath, NULL); + genericptr *ggen; + XPoint *curpt; + polyptr thispoly; + splineptr thisspline; + pointselect *cptr; + short cycle, newcycle; + + /* Simple cases---don't need to switch elements */ + + switch (ELEMENTTYPE(ppart)) { + case POLYGON: + thispoly = (polyptr)ppart; + cptr = thispoly->cycle; + if (cptr == NULL) return; + curpt = thispoly->points + cptr->number; + newcycle = checkcycle(ppart, dir); + advancecycle(&ppart, newcycle); + if (cptr->number < thispoly->number && cptr->number > 0) { + checkwarp(thispoly->points + cptr->number); + removeothercycles(nextpath, ppart); + updatepath(nextpath); + return; + } + break; + case SPLINE: + thisspline = (splineptr)ppart; + cptr = ((splineptr)ppart)->cycle; + if (cptr == NULL) return; + curpt = &thisspline->ctrl[cptr->number]; + newcycle = checkcycle(ppart, dir); + advancecycle(&ppart, newcycle); + if (cptr->number < 4 && cptr->number > 0) { + checkwarp(&thisspline->ctrl[cptr->number]); + removeothercycles(nextpath, ppart); + updatepath(nextpath); + if (newcycle == 1 || newcycle == 2) + addanticycle(nextpath, thisspline, newcycle); + return; + } + break; + } + + /* Moving on to the next element. . . */ + + /* If dir < 0, go to the penultimate cycle of the last part */ + /* If dir > 0, go to the second cycle of the next part */ + + for (ggen = nextpath->plist; (*ggen != ppart) && + (ggen < nextpath->plist + nextpath->parts); ggen++); + + if (ggen == nextpath->plist + nextpath->parts) return; /* shouldn't happen! */ + + if (dir > 0) + ggen++; + else + ggen--; + + if (ggen < nextpath->plist) + ggen = nextpath->plist + nextpath->parts - 1; + else if (ggen == nextpath->plist + nextpath->parts) + ggen = nextpath->plist; + + removecycle((genericptr *)(&nextpath)); + + /* The next point to edit is the first point in the next segment */ + /* that is not at the same position as the one we were last editing. */ + + switch (ELEMENTTYPE(*ggen)) { + case POLYGON: + thispoly = TOPOLY(ggen); + cycle = (dir > 0) ? 0 : thispoly->number - 1; + addcycle(ggen, cycle, 0); + makerefcycle(thispoly->cycle, cycle); + if ((thispoly->points + cycle)->x == curpt->x && + (thispoly->points + cycle)->y == curpt->y) { + newcycle = checkcycle((genericptr)thispoly, 1); + advancecycle(ggen, newcycle); + cycle = newcycle; + } + checkwarp(thispoly->points + cycle); + break; + case SPLINE: + thisspline = TOSPLINE(ggen); + cycle = (dir > 0) ? 0 : 3; + addcycle(ggen, cycle, 0); + makerefcycle(thisspline->cycle, cycle); + if (thisspline->ctrl[cycle].x == curpt->x && + thisspline->ctrl[cycle].y == curpt->y) { + newcycle = checkcycle((genericptr)thisspline, 1); + advancecycle(ggen, newcycle); + cycle = newcycle; + if (cycle == 1 || cycle == 2) + addanticycle(nextpath, thisspline, cycle); + } + checkwarp(&(thisspline->ctrl[cycle])); + break; + } + updatepath(nextpath); +} + +/*--------------------------------------------------------------*/ +/* Change to next edit point on a polygon */ +/*--------------------------------------------------------------*/ + +void nextpolycycle(polyptr *nextpoly, short dir) +{ + short newcycle; + + newcycle = checkcycle((genericptr)(*nextpoly), dir); + advancecycle((genericptr *)nextpoly, newcycle); + findconstrained(*nextpoly); + printeditbindings(); + + newcycle = (*nextpoly)->cycle->number; + checkwarp((*nextpoly)->points + newcycle); +} + +/*--------------------------------------------------------------*/ +/* Change to next edit cycle on a spline */ +/*--------------------------------------------------------------*/ + +void nextsplinecycle(splineptr *nextspline, short dir) +{ + short newcycle; + newcycle = checkcycle((genericptr)(*nextspline), dir); + advancecycle((genericptr *)nextspline, newcycle); + + if (newcycle == 1 || newcycle == 2) + Wprintf("Adjust control point"); + else + Wprintf("Adjust endpoint position"); + + checkwarp(&(*nextspline)->ctrl[newcycle]); +} + +/*--------------------------------------------------------------*/ +/* Warp pointer to the edit point on an arc. */ +/*--------------------------------------------------------------*/ + +void warparccycle(arcptr nextarc, short cycle) +{ + XPoint curang; + double rad; + + switch(cycle) { + 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 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; + case 3: + curang.x = nextarc->position.x; + curang.y = nextarc->position.y + nextarc->yaxis; + Wprintf("Adjust ellipse minor axis"); + break; + } + checkwarp(&curang); +} + +/*--------------------------------------------------------------*/ +/* Change to next edit cycle on an arc */ +/*--------------------------------------------------------------*/ + +void nextarccycle(arcptr *nextarc, short dir) +{ + short newcycle; + + newcycle = checkcycle((genericptr)(*nextarc), dir); + advancecycle((genericptr *)nextarc, newcycle); + warparccycle(*nextarc, newcycle); +} + +/*------------------------------------------------------*/ +/* Get a numerical response from the keyboard (0-9) */ +/*------------------------------------------------------*/ + +#ifndef TCL_WRAPPER + +short getkeynum() +{ + XEvent event; + XKeyEvent *keyevent = (XKeyEvent *)(&event); + KeySym keypressed; + + for (;;) { + XNextEvent(dpy, &event); + if (event.type == KeyPress) break; + else xcDispatchEvent(&event); + } + XLookupString(keyevent, _STR, 150, &keypressed, NULL); + if (keypressed > XK_0 && keypressed <= XK_9) + return (short)(keypressed - XK_1); + else + return -1; +} + +#endif + +/*--------------------------*/ +/* Register a "press" event */ +/*--------------------------*/ + +#ifdef TCL_WRAPPER +void makepress(ClientData clientdata) +#else +void makepress(XtPointer clientdata, xcIntervalId *id) +#endif +{ + int keywstate = (int)((pointertype)clientdata); + + /* Button/Key was pressed long enough to make a "press", not a "tap" */ + + areawin->time_id = 0; + pressmode = keywstate; + eventdispatch(keywstate | HOLD_MASK, areawin->save.x, areawin->save.y); +} + +/*------------------------------------------------------*/ +/* 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 operations specific to polygons (point manipulation) */ +/*--------------------------------------------------------------*/ + +void poly_edit_op(int op) +{ + genericptr keygen = *(EDITPART); + polyptr lwire; + XPoint *lpoint; + short cycle; + + if (IS_PATH(keygen)) + keygen = getsubpart((pathptr)keygen, NULL); + + switch(ELEMENTTYPE(keygen)) { + case POLYGON: { + lwire = (polyptr)keygen; + + /* Remove a point from the polygon */ + if (op == XCF_Edit_Delete) { + if (lwire->number < 3) return; + UDrawPolygon(lwire, xobjs.pagelist[areawin->page]->wirewidth); + if (lwire->number == 3 && !(lwire->style & UNCLOSED)) + lwire->style |= UNCLOSED; + cycle = checkcycle((genericptr)lwire, 0); + lwire->number--; + for (lpoint = lwire->points + cycle; lpoint < + lwire->points + lwire->number; lpoint++) + *lpoint = *(lpoint + 1); + UDrawPolygon(lwire, xobjs.pagelist[areawin->page]->wirewidth); + nextpolycycle(&lwire, -1); + } + + /* Add a point to the polygon */ + else if (op == XCF_Edit_Insert || op == XCF_Edit_Append) { + UDrawPolygon(lwire, xobjs.pagelist[areawin->page]->wirewidth); + lwire->number++; + lwire->points = (XPoint *)realloc(lwire->points, lwire->number + * sizeof(XPoint)); + cycle = checkcycle((genericptr)lwire, 0); + for (lpoint = lwire->points + lwire->number - 1; lpoint > lwire-> + points + cycle; lpoint--) + *lpoint = *(lpoint - 1); + UDrawPolygon(lwire, xobjs.pagelist[areawin->page]->wirewidth); + if (op == XCF_Edit_Append) + nextpolycycle(&lwire, 1); + } + + /* Parameterize the position of a polygon point */ + else if (op == XCF_Edit_Param) { + cycle = checkcycle((genericptr)lwire, 0); + makenumericalp(&keygen, P_POSITION_X, NULL, cycle); + makenumericalp(&keygen, P_POSITION_Y, NULL, cycle); + } + } + } +} + +/*----------------------------------------------------------------------*/ +/* 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, arc, or label */ + /* to attach to. */ + + if (areawin->selects <= 1) { + short *refsel; + + if (areawin->attachto >= 0) { + areawin->attachto = -1; /* default value---no attachments */ + Wprintf("Unconstrained moving"); + } + else { + int select_prev; + + select_prev = areawin->selects; + refsel = select_add_element(SPLINE|ARC|POLYGON|LABEL|OBJINST); + if ((refsel != NULL) && (areawin->selects > select_prev)) { + + /* transfer refsel over to attachto */ + + areawin->attachto = *(refsel + areawin->selects - 1); + areawin->selects--; + if (areawin->selects == 0) freeselects(); + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(SELTOCOLOR(refsel)); + easydraw(areawin->attachto, DEFAULTCOLOR); + + /* restore graphics state */ + SetFunction(dpy, areawin->gc, areawin->gctype); + SetForeground(dpy, areawin->gc, areawin->gccolor); + + Wprintf("Constrained attach"); + + /* Starting a new wire? */ + if (eventmode == NORMAL_MODE) { + XPoint newpos, userpt; + userpt = UGetCursorPos(); + findattach(&newpos, NULL, &userpt); + startwire(&newpos); + eventmode = WIRE_MODE; + areawin->attachto = -1; + } + } + else { + 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 be carefully written or the */ +/* function dispatch mechanism can put xcircuit into a bad */ +/* state. */ +/*--------------------------------------------------------------*/ + +Boolean compatible_function(int function) +{ + int r = FALSE; + char *funcname; + + switch(function) { + case XCF_Text_Left: case XCF_Text_Right: + case XCF_Text_Home: case XCF_Text_End: + case XCF_Text_Return: case XCF_Text_Delete: + r = (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE || + eventmode == ETEXT_MODE) ? + TRUE : FALSE; + break; + + 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_Font: + case XCF_Boldfont: case XCF_Italicfont: + case XCF_Normalfont: case XCF_ISO_Encoding: + case XCF_Special: case XCF_Text_Split: + case XCF_Text_Up: case XCF_Text_Down: + case XCF_Parameter: + r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE) ? + TRUE : FALSE; + break; + + case XCF_Justify: + r = (eventmode == TEXT_MODE || eventmode == ETEXT_MODE || + eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == NORMAL_MODE) ? + TRUE : FALSE; + break; + + case XCF_Edit_Delete: case XCF_Edit_Insert: case XCF_Edit_Append: + case XCF_Edit_Param: + r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE) ? + TRUE : FALSE; + break; + + case XCF_Edit_Next: + r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE || + eventmode == EINST_MODE || eventmode == EARC_MODE || + eventmode == ESPLINE_MODE) ? + TRUE : FALSE; + break; + + case XCF_Attach: + r = (eventmode == EPOLY_MODE || eventmode == EPATH_MODE || + eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == WIRE_MODE || eventmode == NORMAL_MODE) ? + TRUE : FALSE; + break; + + case XCF_Rotate: case XCF_Flip_X: + case XCF_Flip_Y: + r = (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) ? + TRUE : FALSE; + break; + + case XCF_Snap: case XCF_Swap: + r = (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == NORMAL_MODE) ? + TRUE : FALSE; + break; + + case XCF_Double_Snap: case XCF_Halve_Snap: + case XCF_SnapTo: + r = (eventmode == CATALOG_MODE || eventmode == CATTEXT_MODE || + eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ? + FALSE : TRUE; + break; + + case XCF_Library_Pop: + r = (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: + r = (eventmode == CATALOG_MODE) ? + TRUE : FALSE; + break; + + case XCF_Library_Directory: + r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE || + eventmode == ASSOC_MODE) ? + TRUE : FALSE; + break; + + case XCF_Next_Library: + r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE || + eventmode == ASSOC_MODE || eventmode == CATMOVE_MODE) ? + TRUE : FALSE; + break; + + case XCF_Select: case XCF_Exit: + r = (eventmode == CATALOG_MODE || eventmode == NORMAL_MODE) ? + TRUE : FALSE; + break; + + case XCF_Pop: + r = (eventmode == MOVE_MODE || eventmode == COPY_MODE || + eventmode == CATALOG_MODE || eventmode == NORMAL_MODE || + eventmode == ASSOC_MODE) ? + TRUE : FALSE; + break; + + case XCF_Push: + r = (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_Dot: case XCF_Write: + case XCF_Netlist: case XCF_Sim: + case XCF_SPICE: case XCF_SPICEflat: + case XCF_PCB: case XCF_Move: + r = (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_Page: case XCF_Help: + case XCF_Cancel: case XCF_Prompt: + r = TRUE; + break; + + case XCF_Continue_Copy: + case XCF_Finish_Copy: + r = (eventmode == COPY_MODE) ? + TRUE : FALSE; + break; + + case XCF_Continue_Element: + case XCF_Finish_Element: + r = (eventmode == WIRE_MODE || eventmode == BOX_MODE || + eventmode == ARC_MODE || eventmode == SPLINE_MODE || + eventmode == EPATH_MODE || eventmode == EPOLY_MODE || + eventmode == EARC_MODE || eventmode == ESPLINE_MODE || + eventmode == MOVE_MODE || eventmode == CATMOVE_MODE || + eventmode == EINST_MODE || eventmode == RESCALE_MODE) ? + TRUE : FALSE; + break; + + case XCF_Cancel_Last: + r = (eventmode == WIRE_MODE || eventmode == ARC_MODE || + eventmode == SPLINE_MODE || eventmode == EPATH_MODE || + eventmode == EPOLY_MODE || eventmode == EARC_MODE || + eventmode == EINST_MODE || eventmode == ESPLINE_MODE) ? + TRUE : FALSE; + break; + + case XCF_Finish: + r = (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 || eventmode == NORMAL_MODE || + eventmode == CATMOVE_MODE) ? + TRUE : FALSE; + break; + + default: /* Function type was not handled. */ + funcname = func_to_string(function); + if (funcname == NULL) + Wprintf("Error: \"%s\" is not a known function!"); + else + Wprintf("Error: Function type \"%s\" (%d) not handled by " + "compatible_function()", func_to_string(function), + function); + break; + } + return r; +} + +/*----------------------------------------------------------------------*/ +/* 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. */ +/* */ +/* Return value: 0 if event was handled, -1 if not. */ +/*----------------------------------------------------------------------*/ + +int eventdispatch(int keywstate, int x, int y) +{ + short value; /* For return values from boundfunction() */ + int function; /* What function should be invoked */ + + /* Invalid key state returned from getkeysignature(); usually this */ + /* means a modifier key pressed by itself. */ + + if (keywstate == -1) return -1; + function = boundfunction(areawin->area, keywstate, &value); + + /* Check for ASCII or ISO-Latin1-9 characters in keywstate while in */ + /* a text-entry state. Only the function XCF_Special is allowed in */ + /* text-entry mode, because XCF_Special can be used to enter the */ + /* character bound to the XCF_Special function. */ + + if (keywstate >= 32 && keywstate < 256) { + if (eventmode == CATTEXT_MODE || eventmode == TEXT_MODE || + eventmode == ETEXT_MODE) { + if (function != XCF_Special) + return labeltext(keywstate, NULL); + else if (eventmode != CATTEXT_MODE) { + labelptr elabel = TOLABEL(EDITPART); + if (elabel->justify & LATEXLABEL) + return labeltext(keywstate, NULL); + } + } + } + + if (function > -1) + return functiondispatch(function, value, x, y); + else { + char *keystring = key_to_string(keywstate); +#ifdef HAVE_PYTHON + if (python_key_command(keywstate) < 0) +#endif + Wprintf("Key \'%s\' is not bound to a macro", keystring); + free(keystring); + } + return -1; +} + +/*----------------------------------------------------------------------*/ +/* Dispatch actions by function number. Note that the structure of */ +/* this function is closely tied to the routine compatible_function(). */ +/*----------------------------------------------------------------------*/ + +int functiondispatch(int function, short value, int x, int y) +{ + int result = 0; + + switch (eventmode) { + case MOVE_MODE: + case COPY_MODE: + snap(x, y, &areawin->save); + break; + case NORMAL_MODE: + window_to_user(x, y, &areawin->save); + break; + } + + switch(function) { + case XCF_Page: + if (value < 0 || value > xobjs.pages) + Wprintf("Page %d out of range.", (int)value); + else + newpage(value - 1); + break; + case XCF_Justify: + rejustify(value); + break; + case XCF_Superscript: + labeltext(SUPERSCRIPT, (char *)1); + break; + case XCF_Subscript: + labeltext(SUBSCRIPT, (char *)1); + break; + case XCF_Normalscript: + labeltext(NORMALSCRIPT, (char *)1); + break; + case XCF_Font: + setfont(NULL, 1000, NULL); + break; + case XCF_Boldfont: + fontstyle(NULL, 1, NULL); + break; + case XCF_Italicfont: + fontstyle(NULL, 2, NULL); + break; + case XCF_Normalfont: + fontstyle(NULL, 0, NULL); + break; + case XCF_Underline: + labeltext(UNDERLINE, (char *)1); + break; + case XCF_Overline: + labeltext(OVERLINE, (char *)1); + break; + case XCF_ISO_Encoding: + fontencoding(NULL, 2, NULL); + break; + case XCF_Halfspace: + labeltext(HALFSPACE, (char *)1); + break; + case XCF_Quarterspace: + labeltext(QTRSPACE, (char *)1); + break; + case XCF_Special: + result = dospecial(); + break; +#ifndef TCL_WRAPPER + case XCF_Parameter: + insertparam(); + break; +#endif + case XCF_TabStop: + labeltext(TABSTOP, (char *)1); + break; + case XCF_TabForward: + labeltext(TABFORWARD, (char *)1); + break; + case XCF_TabBackward: + labeltext(TABBACKWARD, (char *)1); + break; + case XCF_Text_Return: + labeltext(TEXT_RETURN, (char *)1); + break; + case XCF_Text_Delete: + labeltext(TEXT_DELETE, (char *)1); + break; + case XCF_Text_Right: + labeltext(TEXT_RIGHT, (char *)1); + break; + case XCF_Text_Left: + labeltext(TEXT_LEFT, (char *)1); + break; + case XCF_Text_Up: + labeltext(TEXT_UP, (char *)1); + break; + case XCF_Text_Down: + labeltext(TEXT_DOWN, (char *)1); + break; + case XCF_Text_Split: + labeltext(TEXT_SPLIT, (char *)1); + break; + case XCF_Text_Home: + labeltext(TEXT_HOME, (char *)1); + break; + case XCF_Text_End: + labeltext(TEXT_END, (char *)1); + break; + case XCF_Linebreak: + labeltext(RETURN, (char *)1); + break; + case XCF_Edit_Param: + case XCF_Edit_Delete: + case XCF_Edit_Insert: + case XCF_Edit_Append: + poly_edit_op(function); + break; + case XCF_Edit_Next: + path_op(*(EDITPART), XCF_Continue_Element, x, y); + break; + case XCF_Attach: + attach_to(); + break; + case XCF_Next_Library: + changecat(); + break; + case XCF_Library_Directory: + startcatalog(NULL, LIBLIB, NULL); + break; + case XCF_Library_Edit: + window_to_user(x, y, &areawin->save); + unselect_all(); + select_element(LABEL); + if (areawin->selects == 1) + edit(x, y); + break; + case XCF_Library_Delete: + catalog_op(XCF_Select, x, y); + catdelete(); + break; + case XCF_Library_Duplicate: + catalog_op(XCF_Select, x, y); + copycat(); + break; + case XCF_Library_Hide: + catalog_op(XCF_Select, x, y); + cathide(); + break; + case XCF_Library_Virtual: + catalog_op(XCF_Select, x, y); + catvirtualcopy(); + break; + case XCF_Page_Directory: + startcatalog(NULL, PAGELIB, NULL); + break; + case XCF_Library_Copy: + case XCF_Library_Pop: + catalog_op(function, x, y); + break; + case XCF_Virtual: + copyvirtual(); + break; + case XCF_Help: + starthelp(NULL, NULL, NULL); + break; + case XCF_Redraw: + drawarea(NULL, NULL, NULL); + break; + case XCF_View: + zoomview(NULL, NULL, NULL); + break; + case XCF_Zoom_In: + zoominrefresh(x, y); + break; + case XCF_Zoom_Out: + zoomoutrefresh(x, y); + break; + case XCF_Pan: + panrefresh(value, x, y, 0.3); + break; + case XCF_Double_Snap: + setsnap(1); + break; + case XCF_Halve_Snap: + setsnap(-1); + break; + case XCF_Write: +#ifdef TCL_WRAPPER + Tcl_Eval(xcinterp, "xcircuit::promptsavepage"); +#else + outputpopup(NULL, NULL, NULL); +#endif + break; + case XCF_Rotate: + elementrotate(value, &areawin->save); + break; + case XCF_Flip_X: + elementflip(&areawin->save); + break; + case XCF_Flip_Y: + elementvflip(&areawin->save); + break; + case XCF_Snap: + snapelement(); + break; + case XCF_SnapTo: + if (areawin->snapto) { + areawin->snapto = False; + Wprintf("Snap-to off"); + } + else { + areawin->snapto = True; + Wprintf("Snap-to on"); + } + break; + case XCF_Pop: + if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) { + eventmode = NORMAL_MODE; + catreturn(); + } + else + popobject(NULL, 0, NULL); + break; + case XCF_Push: + if (eventmode == CATALOG_MODE) { + /* Don't allow push from library directory */ + if ((areawin->topinstance != xobjs.libtop[LIBLIB]) + && (areawin->topinstance != xobjs.libtop[PAGELIB])) { + window_to_user(x, y, &areawin->save); + eventmode = NORMAL_MODE; + pushobject(NULL); + } + } + else + pushobject(NULL); + break; + case XCF_Delete: + deletebutton(x, y); + break; + case XCF_Select: + if (eventmode == CATALOG_MODE) + catalog_op(function, x, y); + else + select_add_element(ALL_TYPES); + break; + case XCF_Box: + boxbutton(x, y); + break; + case XCF_Arc: + arcbutton(x, y); + break; + case XCF_Text: + eventmode = TEXT_MODE; + textbutton(NORMAL, x, y); + break; + case XCF_Exchange: + exchange(); + break; + case XCF_Library_Move: + /* Don't allow from library directory. Then fall through to XCF_Move */ + if (areawin->topinstance == xobjs.libtop[LIBLIB]) break; + case XCF_Move: + if (areawin->selects == 0) { + was_preselected = FALSE; + if (eventmode == CATALOG_MODE) + catalog_op(XCF_Select, x, y); + else + select_element(ALL_TYPES); + } + else was_preselected = TRUE; + + if (areawin->selects > 0) { + eventmode = (eventmode == CATALOG_MODE) ? CATMOVE_MODE : MOVE_MODE; + u2u_snap(&areawin->save); + areawin->origin = areawin->save; + reset_cycles(); + select_connected_pins(); + XDefineCursor(dpy, areawin->window, ARROW); +#ifdef TCL_WRAPPER + Tk_CreateEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, (Tk_EventProc *)xctk_drag, + NULL); +#endif + } + break; + case XCF_Join: + join(); + break; + case XCF_Unjoin: + unjoin(); + break; + case XCF_Spline: + splinebutton(x, y); + break; + case XCF_Edit: + edit(x, y); + break; + case XCF_Undo: + undo_action(); + break; + case XCF_Redo: + redo_action(); + break; + case XCF_Select_Save: +#ifdef TCL_WRAPPER + Tcl_Eval(xcinterp, "xcircuit::promptmakeobject"); +#else + selectsave(NULL, NULL, NULL); +#endif + break; + case XCF_Unselect: + select_add_element(-ALL_TYPES); + break; + case XCF_Dashed: + setelementstyle(NULL, DASHED, NOBORDER | DOTTED | DASHED); + break; + case XCF_Dotted: + setelementstyle(NULL, DOTTED, NOBORDER | DOTTED | DASHED); + break; + case XCF_Solid: + setelementstyle(NULL, NORMAL, NOBORDER | DOTTED | DASHED); + break; + case XCF_Prompt: + docommand(); + break; + case XCF_Dot: + snap(x, y, &areawin->save); + drawdot(areawin->save.x, areawin->save.y); + drawarea(NULL, NULL, NULL); + break; + case XCF_Wire: + u2u_snap(&areawin->save); + startwire(&areawin->save); + eventmode = WIRE_MODE; + break; + case XCF_Nothing: + DoNothing(NULL, NULL, NULL); + break; + case XCF_Exit: + quitcheck(areawin->area, NULL, NULL); + break; + case XCF_Netlist: + callwritenet(NULL, 0, NULL); + break; + case XCF_Swap: + swapschem(0, -1, NULL); + break; + case XCF_Pin_Label: + eventmode = TEXT_MODE; + textbutton(LOCAL, x, y); + break; + case XCF_Pin_Global: + eventmode = TEXT_MODE; + textbutton(GLOBAL, x, y); + break; + case XCF_Info_Label: + eventmode = TEXT_MODE; + textbutton(INFO, x, y); + break; + case XCF_Rescale: + if (checkselect(LABEL | OBJINST | GRAPHIC) == TRUE) { + eventmode = RESCALE_MODE; + UDrawRescaleBox(&areawin->save); +#ifdef TCL_WRAPPER + Tk_CreateEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcAddEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, False, (xcEventHandler)xlib_drag, + NULL); +#endif + } + break; + case XCF_SelectBox: + startselect(); + break; + case XCF_Connectivity: + connectivity(NULL, NULL, NULL); + break; + case XCF_Copy: + case XCF_Continue_Copy: + case XCF_Finish_Copy: + copy_op(function, x, y); + break; + case XCF_Continue_Element: + if (eventmode == CATMOVE_MODE || eventmode == MOVE_MODE || + eventmode == RESCALE_MODE) + finish_op(XCF_Finish, x, y); + else + continue_op(function, x, y); + break; + case XCF_Finish_Element: + case XCF_Cancel_Last: + case XCF_Cancel: + finish_op(function, x, y); + break; + case XCF_Finish: + if (eventmode == CATALOG_MODE || eventmode == ASSOC_MODE) + catalog_op(XCF_Library_Pop, x, y); + else + finish_op(function, x, y); + break; + case XCF_Sim: + writenet(topobject, "flatsim", "sim"); + break; + case XCF_SPICE: + writenet(topobject, "spice", "spc"); + break; + case XCF_PCB: + writenet(topobject, "pcb", "pcbnet"); + break; + case XCF_SPICEflat: + writenet(topobject, "flatspice", "fspc"); + break; + case XCF_Graphic: + Wprintf("Action not handled"); + result = -1; + break; + case XCF_Text_Delete_Param: + Wprintf("Action not handled"); + result = -1; + break; + case XCF_ChangeStyle: + Wprintf("Action not handled"); + result = -1; + break; + } + + /* Ensure that we do not get stuck in suspend mode */ + /* by removing the suspend state whenever a key is */ + /* pressed. */ + + if (xobjs.suspend == 1) { + xobjs.suspend = -1; + refresh(NULL, NULL, NULL); + } + else if (xobjs.suspend != 2) + xobjs.suspend = -1; + + return result; +} + +/*------------------------------------------------------*/ +/* Get a canonical signature for a button/key event */ +/*------------------------------------------------------*/ + +int getkeysignature(XKeyEvent *event) +{ + KeySym keypressed; + int keywstate; /* KeySym with prepended state information */ + +#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); + + /* Convert codes outside the character (0 - 255) range but within */ + /* the ISO-Latin1,...,9 encoding scheme (256-5120). X11 has unique */ + /* keysyms for each character, but the ISO-Latin encodings define */ + /* mappings to the 8-bit (256) character set. */ + + if (keywstate >= 256 && keywstate < 5120) + keywstate = XKeysymToKeycode(dpy, (KeySym)keywstate); + + /* 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 (areawin->time_id != 0) { + xcRemoveTimeOut(areawin->time_id); + areawin->time_id = 0; + keywstate = getkeysignature(event); + eventdispatch(keywstate, areawin->save.x, areawin->save.y); + } + 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) && (xobjs.hold == TRUE)) { + + /* Establish whether a HOLD modifier binding would apply in */ + /* the current eventmode. If so, set the HOLD timer. */ + + j = 0; + func = boundfunction(areawin->area, keywstate | HOLD_MASK, NULL); + if (func != -1) { + areawin->save.x = event->x; + areawin->save.y = event->y; + areawin->time_id = xcAddTimeOut(app, PRESSTIME, + makepress, (ClientData)((pointertype)keywstate)); + return; + } + + } + eventdispatch(keywstate, event->x, event->y); + } +} + +/*--------------------------------*/ +/* Set snap spacing from keyboard */ +/*--------------------------------*/ + +void setsnap(short direction) +{ + float oldsnap = xobjs.pagelist[areawin->page]->snapspace; + char buffer[50]; + + if (direction > 0) xobjs.pagelist[areawin->page]->snapspace *= 2; + else { + if (oldsnap >= 2.0) + xobjs.pagelist[areawin->page]->snapspace /= 2; + else { + measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer); + Wprintf("Snap space at minimum value of %s", buffer); + } + } + if (xobjs.pagelist[areawin->page]->snapspace != oldsnap) { + measurestr(xobjs.pagelist[areawin->page]->snapspace, buffer); + Wprintf("Snap spacing set to %s", buffer); + drawarea(NULL, NULL, NULL); + } +} + +/*-----------------------------------------*/ +/* Reposition an object onto the snap grid */ +/*-----------------------------------------*/ + +void snapelement() +{ + short *selectobj; + Boolean preselected; + + preselected = (areawin->selects > 0) ? TRUE : FALSE; + if (!checkselect(ALL_TYPES)) return; + SetFunction(dpy, areawin->gc, GXcopy); + SetForeground(dpy, areawin->gc, BACKGROUND); + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + easydraw(*selectobj, DOFORALL); + switch(SELECTTYPE(selectobj)) { + case OBJINST: { + objinstptr snapobj = SELTOOBJINST(selectobj); + + u2u_snap(&snapobj->position); + } break; + case GRAPHIC: { + graphicptr snapg = SELTOGRAPHIC(selectobj); + + u2u_snap(&snapg->position); + } break; + case LABEL: { + labelptr snaplabel = SELTOLABEL(selectobj); + + u2u_snap(&snaplabel->position); + } break; + case POLYGON: { + polyptr snappoly = SELTOPOLY(selectobj); + pointlist snappoint; + + for (snappoint = snappoly->points; snappoint < snappoly->points + + snappoly->number; snappoint++) + u2u_snap(snappoint); + } break; + case ARC: { + arcptr snaparc = SELTOARC(selectobj); + + u2u_snap(&snaparc->position); + if (areawin->snapto) { + snaparc->radius = (snaparc->radius / + xobjs.pagelist[areawin->page]->snapspace) * + xobjs.pagelist[areawin->page]->snapspace; + snaparc->yaxis = (snaparc->yaxis / + xobjs.pagelist[areawin->page]->snapspace) * + xobjs.pagelist[areawin->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 (preselected || (eventmode != NORMAL_MODE)) { + SetForeground(dpy, areawin->gc, SELECTCOLOR); + easydraw(*selectobj, DOFORALL); + } + } + select_invalidate_netlist(); + if (eventmode == NORMAL_MODE) + if (!preselected) + 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[areawin->page]->drawingscale.y / + (float)xobjs.pagelist[areawin->page]->drawingscale.x; + int llen, lwid; + u_char wlflag = 0; + XPoint *tpoint, *npoint; + char *sptr; + short cycle; + + /* 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 edit cycle */ + cycle = lwire->cycle->number; + if (cycle < 0 || cycle >= lwire->number) { + advancecycle((genericptr *)(&lwire), 0); + cycle = 0; + } + tpoint = lwire->points + cycle; + npoint = lwire->points + checkcycle((genericptr)lwire, 1); + llen = wirelength(tpoint, npoint); + npoint = lwire->points + checkcycle((genericptr)lwire, -1); + lwid = wirelength(tpoint, npoint); + wlflag = 3; + if (lwire->style & UNCLOSED) { /* unclosed polys */ + if (cycle == 0) + wlflag = 1; + else if (cycle == 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 if (eventmode == BOX_MODE) { + tpoint = lwire->points; + npoint = lwire->points + 1; + llen = wirelength(tpoint, npoint); + npoint = lwire->points + 3; + lwid = wirelength(tpoint, npoint); + if ((npoint->y - tpoint->y) == 0) { /* swap width and length */ + int tmp = lwid; + lwid = llen; + llen = tmp; + } + wlflag = 3; + } + 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[areawin->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[areawin->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[areawin->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[areawin->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 "attachto" element. */ +/*----------------------------------------------------------------*/ + +void findattach(XPoint *newpos, int *rot, XPoint *userpt) +{ + XPoint *endpt1, *endpt2; + float frac; + double tmpang; + int locrot = 0; + + if (rot) locrot = *rot; + + /* find point of intersection and slope */ + + if (SELECTTYPE(&areawin->attachto) == ARC) { + arcptr aarc = SELTOARC(&areawin->attachto); + 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, &locrot); + if (rot) *rot = locrot; + 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 */ + + if (rot) { + *rot = 90 - (int)(INVRFAC * tmpang); + if (*rot < 0) *rot += 360; + } + } + else if (SELECTTYPE(&areawin->attachto) == SPLINE) { + splineptr aspline = SELTOSPLINE(&areawin->attachto); + frac = findsplinemin(aspline, userpt); + findsplinepos(aspline, frac, newpos, &locrot); + if (rot) *rot = locrot; + } + else if (SELECTTYPE(&areawin->attachto) == OBJINST) { + + objinstptr ainst = SELTOOBJINST(&areawin->attachto); + objectptr aobj = ainst->thisobject; + genericptr *ggen; + long testdist, mindist = 1e8; + XPoint mdpoint; + + /* In case instance has no pin labels, we will attach to */ + /* the instance's own origin. */ + + mdpoint.x = mdpoint.y = 0; + ReferencePosition(ainst, &mdpoint, newpos); + + /* Find the nearest pin label in the object instance and attach to it */ + for (ggen = aobj->plist; ggen < aobj->plist + aobj->parts; ggen++) { + if (ELEMENTTYPE(*ggen) == LABEL) { + labelptr alab = TOLABEL(ggen); + if (alab->pin == LOCAL || alab->pin == GLOBAL) { + ReferencePosition(ainst, &alab->position, &mdpoint); + testdist = sqwirelen(&mdpoint, userpt); + if (testdist < mindist) { + mindist = testdist; + *newpos = mdpoint; + } + } + } + } + } + else if (SELECTTYPE(&areawin->attachto) == LABEL) { + /* Only one choice: Attach to the label position */ + labelptr alabel = SELTOLABEL(&areawin->attachto); + newpos->x = alabel->position.x; + newpos->y = alabel->position.y; + } + else if (SELECTTYPE(&areawin->attachto) == POLYGON) { + polyptr apoly = SELTOPOLY(&areawin->attachto); + XPoint *testpt, *minpt, *nxtpt; + long mindist = 1e8, testdist; + minpt = nxtpt = apoly->points; /* so variables aren't uninitialized */ + 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, &locrot); + if (rot) *rot = locrot; + } +} + +/*--------------------------------------------*/ +/* 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, areawin->window, 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 != CATMOVE_MODE && eventmode != MOVE_MODE + && eventmode != COPY_MODE) + return; + + snap(locx, locy, &userpt); + deltax = userpt.x - areawin->save.x; + deltay = userpt.y - areawin->save.y; + if (deltax == 0 && deltay == 0) return; + + areawin->save.x = userpt.x; + areawin->save.y = userpt.y; + + /* set up the graphics state for moving a selected object */ + + XSetXORFg(SELECTCOLOR, BACKGROUND); + SetFunction(dpy, areawin->gc, GXxor); + + placeselects(deltax, deltay, &userpt); + + /* restore graphics state */ + + SetForeground(dpy, areawin->gc, areawin->gccolor); + SetFunction(dpy, areawin->gc, areawin->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 *position) +{ + XPoint negpt, *newpts = (XPoint *)NULL; + + negpt.x = -position->x; + negpt.y = -position->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, *position, + 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, *position, + 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, + *position, 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, XPoint *position) +{ + short *selectobj; /* , ld; (jdk) */ + Boolean single = False; + Boolean need_refresh = False; + Boolean preselected; + XPoint newpt, negpt; + + preselected = (areawin->selects > 0) ? TRUE : FALSE; + if (!checkselect(ALL_TYPES)) return; + if (areawin->selects == 1) single = True; + + negpt.x = -position->x; + negpt.y = -position->y; + + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + + /* erase the element */ + if (!need_refresh) { + SetFunction(dpy, areawin->gc, GXcopy); + SetForeground(dpy, areawin->gc, BACKGROUND); + easydraw(*selectobj, DOFORALL); + } + + switch(SELECTTYPE(selectobj)) { + + case(OBJINST):{ + objinstptr rotateobj = SELTOOBJINST(selectobj); + + if (is_library(topobject) >= 0 && !is_virtual(rotateobj)) break; + 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, *position, + 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, *position, + 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, *position, + 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, areawin->topinstance, + *genpart); + elemrotate(genpart, direction, position); + }break; + + case PATH:{ + genericptr *genpart; + pathptr rotatepath = SELTOPATH(selectobj); + + register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, + rotatepath); + for (genpart = rotatepath->plist; genpart < rotatepath->plist + + rotatepath->parts; genpart++) + elemrotate(genpart, direction, position); + }break; + } + + /* redisplay the element */ + if (preselected || ((eventmode != NORMAL_MODE) && !need_refresh)) { + SetForeground(dpy, areawin->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. */ + + if (eventmode != COPY_MODE) + register_for_undo(XCF_Rotate, UNDO_MORE, areawin->topinstance, + (eventmode == MOVE_MODE) ? &areawin->origin : position, + (int)direction); + + /* New rule (6/15/07) to be applied generally: If objects were */ + /* selected prior to calling elementrotate() and similar functions, */ + /* leave them selected upon exit. Otherwise, deselect them. */ + + if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) + if (!preselected) + unselect_all(); + + if (eventmode == CATALOG_MODE) { + int libnum; + if ((libnum = is_library(topobject)) >= 0) { + composelib(libnum + LIBRARY); + need_refresh = TRUE; + } + } + else { + pwriteback(areawin->topinstance); + calcbbox(areawin->topinstance); + } + + if (need_refresh) drawarea(NULL, NULL, NULL); +} + +/*----------------------------------------------*/ +/* Rescale the current edit element to the */ +/* dimensions of the rescale box. */ +/*----------------------------------------------*/ + +void elementrescale(float newscale) +{ + short *selectobj; + labelptr sclab; + objinstptr scinst; + graphicptr scgraph; + float oldsize; + + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + switch (SELECTTYPE(selectobj)) { + case LABEL: + sclab = SELTOLABEL(selectobj); + oldsize = sclab->scale; + sclab->scale = newscale; + break; + case OBJINST: + scinst = SELTOOBJINST(selectobj); + oldsize = scinst->scale; + scinst->scale = newscale; + break; + case GRAPHIC: + scgraph = SELTOGRAPHIC(selectobj); + oldsize = scgraph->scale; + scgraph->scale = newscale; + break; + } + register_for_undo(XCF_Rescale, UNDO_MORE, areawin->topinstance, + SELTOGENERIC(selectobj), (double)oldsize); + } + calcbbox(areawin->topinstance); +} + +/*-------------------------------------------------*/ +/* Edit an element in an element-dependent fashion */ +/*-------------------------------------------------*/ + +void edit(int x, int y) +{ + short *selectobj, saveselects; + Boolean preselected = False; + + if (areawin->selects == 0) { + short savemode = eventmode; + eventmode = PENDING_MODE; + selectobj = select_element(ALL_TYPES); + eventmode = savemode; + } + else { + preselected = True; + selectobj = areawin->selectlist; + } + if (areawin->selects == 0) + return; + else if (areawin->selects != 1) { /* Multiple object edit */ + int seltype, selnum; + short *selectlist, selrefno; + + /* Find the closest part to use as a reference */ + selnum = areawin->selects; + selectlist = areawin->selectlist; + areawin->selects = 0; + areawin->selectlist = NULL; + selectobj = select_element(ALL_TYPES); + if (selectobj != NULL) + selrefno = *selectobj; + else + selrefno = -1; + freeselects(); + areawin->selects = selnum; + areawin->selectlist = selectlist; + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + if (*selectobj == selrefno) break; + } + if (selectobj == areawin->selectlist + areawin->selects) { + Wprintf("Put cursor close to the reference element."); + return; + } + + /* Shuffle the reference element to the beginning of the select list */ + *selectobj = *(areawin->selectlist); + *(areawin->selectlist) = selrefno; + selectobj = areawin->selectlist; + } + + 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, areawin->topinstance, + *lastlabel); + + /* fill any NULL instance parameters with the default value */ + copyparams(areawin->topinstance, areawin->topinstance); + + /* place text cursor line at point nearest the cursor */ + /* unless textend is set. . . */ + + if (areawin->textend == 0) { + window_to_user(x, y, &areawin->save); + InvTransformPoints(&areawin->save, &tmppt, 1, (*lastlabel)->position, + (*lastlabel)->scale, (*lastlabel)->rotation); + tmpext = ULength(*lastlabel, areawin->topinstance, 0, NULL); + tmppt.x += ((*lastlabel)->justify & NOTLEFT ? + ((*lastlabel)->justify & RIGHT ? tmpext.maxwidth + : tmpext.maxwidth >> 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); + + // Where tbreak is passed to ULength, the character position, + // not the dimension, is held in tmpext.width field. + tmpext = ULength(*lastlabel, areawin->topinstance, 0, &tmppt); + areawin->textpos = tmpext.width; + } + + /* find current font */ + + curfont = findcurfont(areawin->textpos, (*lastlabel)->string, + areawin->topinstance); + + /* change menu buttons accordingly */ + + setfontmarks(curfont, (*lastlabel)->justify); + + if (eventmode == CATALOG_MODE) { + /* CATTEXT_MODE may show an otherwise hidden library namespace */ + undrawtext(*lastlabel); + eventmode = CATTEXT_MODE; + redrawtext(*lastlabel); + } + else eventmode = ETEXT_MODE; + + UDrawTLine(*lastlabel); + XDefineCursor(dpy, areawin->window, TEXTPTR); + + /* write the text at the bottom */ + + charreport(*lastlabel); + + } break; + + case POLYGON: case ARC: case SPLINE: case PATH: + window_to_user(x, y, &areawin->save); + /* Redraw without drawing selections */ + saveselects = areawin->selects; + selectobj = areawin->selectlist; + areawin->selects = 0; + areawin->selectlist = NULL; + drawarea(NULL, NULL, NULL); + areawin->selectlist = selectobj; + areawin->selects = saveselects; + + pathedit(*(EDITPART)); + break; + + case OBJINST: case GRAPHIC: + if (areawin->selects == 1) + unselect_all(); + return; + } + XDefineCursor (dpy, areawin->window, EDCURSOR); +} + +/*----------------------------------------------------------------------*/ +/* edit() routine for path-type elements (polygons, splines, arcs, and */ +/* paths) */ +/*----------------------------------------------------------------------*/ + +void pathedit(genericptr editpart) +{ + splineptr lastspline = NULL; + pathptr lastpath; + polyptr lastpoly = NULL; + arcptr lastarc; + XPoint *savept; + short *eselect; + int cycle; + Boolean havecycle; + + /* Find and set constrained edit points on all elements. Register */ + /* each element with the undo mechanism. */ + + for (eselect = areawin->selectlist; eselect < areawin->selectlist + + areawin->selects; eselect++) { + switch (SELECTTYPE(eselect)) { + case POLYGON: + findconstrained(SELTOPOLY(eselect)); + /* fall through */ + default: + register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, + SELTOGENERIC(eselect)); + break; + } + } + + switch(ELEMENTTYPE(editpart)) { + case PATH: { + genericptr *ggen, *savegen = NULL; + int mincycle, dist, mindist = 1e6; + + lastpath = (pathptr)editpart; + havecycle = (checkcycle(editpart, 0) >= 0) ? True : False; + + /* determine which point of the path is closest to the cursor */ + for (ggen = lastpath->plist; ggen < lastpath->plist + lastpath->parts; + ggen++) { + switch (ELEMENTTYPE(*ggen)) { + case POLYGON: + lastpoly = TOPOLY(ggen); + cycle = closepoint(lastpoly, &areawin->save); + dist = wirelength(lastpoly->points + cycle, &areawin->save); + break; + case SPLINE: + lastspline = TOSPLINE(ggen); + cycle = (wirelength(&lastspline->ctrl[0], + &areawin->save) < wirelength(&lastspline->ctrl[3], + &areawin->save)) ? 0 : 3; + dist = wirelength(&lastspline->ctrl[cycle], &areawin->save); + break; + } + if (dist < mindist) { + mindist = dist; + mincycle = cycle; + savegen = ggen; + } + } + if (savegen == NULL) return; /* something went terribly wrong */ + switch (ELEMENTTYPE(*savegen)) { + case POLYGON: + lastpoly = TOPOLY(savegen); + addcycle(savegen, mincycle, 0); + savept = lastpoly->points + mincycle; + makerefcycle(lastpoly->cycle, mincycle); + findconstrained(lastpoly); + break; + case SPLINE: + lastspline = TOSPLINE(savegen); + addcycle(savegen, mincycle, 0); + savept = &lastspline->ctrl[mincycle]; + makerefcycle(lastspline->cycle, mincycle); + break; + } + updatepath(lastpath); + if (!havecycle) + register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, + (genericptr)lastpath); + patheditpush(lastpath); + areawin->origin = areawin->save; + checkwarp(savept); + + /* Draw edit lines on all splines in the path */ + for (ggen = lastpath->plist; ggen < lastpath->plist + lastpath->parts; + ggen++) { + if (ELEMENTTYPE(*ggen) == SPLINE) { + lastspline = TOSPLINE(ggen); + UDrawXLine(lastspline->ctrl[0], lastspline->ctrl[1]); + UDrawXLine(lastspline->ctrl[3], lastspline->ctrl[2]); + } + } + XcSetXORFg((lastpoly) ? lastpoly->color : lastspline->color, BACKGROUND); + XcSetFunction(GXxor); + + xcAddEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = EPATH_MODE; + printpos(savept->x, savept->y); + + } break; + case POLYGON: { + + lastpoly = (polyptr)editpart; + + /* Determine which point of polygon is closest to cursor */ + cycle = closepoint(lastpoly, &areawin->save); + havecycle = (lastpoly->cycle == NULL) ? False : True; + addcycle(&editpart, cycle, 0); + savept = lastpoly->points + cycle; + makerefcycle(lastpoly->cycle, cycle); + if (!havecycle) { + findconstrained(lastpoly); + register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, + (genericptr)lastpoly); + } + + /* Push onto the editstack */ + polyeditpush(lastpoly); + + /* remember our postion for pointer restore */ + areawin->origin = areawin->save; + + checkwarp(savept); + + XcSetXORFg(lastpoly->color, BACKGROUND); + XcSetFunction(GXxor); + + xcAddEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = EPOLY_MODE; + printeditbindings(); + printpos(savept->x, savept->y); + } break; + case SPLINE: { + XPoint *curpt; + + lastspline = (splineptr)editpart; + + /* find which point is closest to the cursor */ + + cycle = (wirelength(&lastspline->ctrl[0], + &areawin->save) < wirelength(&lastspline->ctrl[3], + &areawin->save)) ? 0 : 3; + havecycle = (lastspline->cycle == NULL) ? False : True; + addcycle(&editpart, cycle, 0); + makerefcycle(lastspline->cycle, cycle); + curpt = &lastspline->ctrl[cycle]; + if (!havecycle) + register_for_undo(XCF_Edit, UNDO_MORE, areawin->topinstance, + (genericptr)lastspline); + + /* Push onto the editstack */ + splineeditpush(lastspline); + + /* remember our postion for pointer restore */ + areawin->origin = areawin->save; + + checkwarp(curpt); + + UDrawXLine(lastspline->ctrl[0], lastspline->ctrl[1]); + UDrawXLine(lastspline->ctrl[3], lastspline->ctrl[2]); + XcSetXORFg(lastspline->color, BACKGROUND); + XcSetFunction(GXxor); + xcAddEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = ESPLINE_MODE; + } break; + case ARC: { + XPoint curpt; + float tmpratio, tlen; + + lastarc = (arcptr)editpart; + + /* find a part of the arc close to the pointer */ + + tlen = (float)wirelength(&areawin->save, &(lastarc->position)); + tmpratio = (float)(abs(lastarc->radius)) / tlen; + curpt.x = lastarc->position.x + tmpratio * (areawin->save.x + - lastarc->position.x); + tmpratio = (float)lastarc->yaxis / tlen; + curpt.y = lastarc->position.y + tmpratio * (areawin->save.y + - lastarc->position.y); + addcycle(&editpart, 0, 0); + saveratio = (double)(lastarc->yaxis) / (double)(abs(lastarc->radius)); + + /* Push onto the editstack */ + arceditpush(lastarc); + areawin->origin = areawin->save; + + checkwarp(&curpt); + + UDrawXLine(curpt, lastarc->position); + areawin->save.x = curpt.x; /* for redrawing dotted edit line */ + areawin->save.y = curpt.y; + XcSetXORFg(lastarc->color, BACKGROUND); + XcSetFunction(GXxor); + xcAddEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackarc, NULL); + eventmode = EARC_MODE; + printpos(curpt.x, curpt.y); + } break; + } +} + +/*------------------------------------------------------*/ +/* Raise an element to the top of the list */ +/*------------------------------------------------------*/ + +void xc_top(short *selectno, short *orderlist) +{ + short i; + genericptr *raiseobj, *genobj, temp; + + raiseobj = topobject->plist + *selectno; + temp = *raiseobj; + i = *selectno; + for (genobj = topobject->plist + *selectno; genobj < + topobject->plist + topobject->parts - 1; genobj++) { + *genobj = *(genobj + 1); + *(orderlist + i) = *(orderlist + i + 1); + i++; + } + *(topobject->plist + topobject->parts - 1) = temp; + *(orderlist + topobject->parts - 1) = *selectno; + *selectno = topobject->parts - 1; +} + +/*------------------------------------------------------*/ +/* Lower an element to the bottom of the list */ +/*------------------------------------------------------*/ + + +void xc_bottom(short *selectno, short *orderlist) +{ + short i; + genericptr *lowerobj, *genobj, temp; + + lowerobj = topobject->plist + *selectno; + temp = *lowerobj; + i = *selectno; + for (genobj = topobject->plist + *selectno; + genobj > topobject->plist; genobj--) { + *genobj = *(genobj - 1); + *(orderlist + i) = *(orderlist + i - 1); + i--; + } + *genobj = temp; + *orderlist = *selectno; + *selectno = 0; +} + +/*--------------------------------------------------------------*/ +/* Raise all selected elements by one position in the list */ +/*--------------------------------------------------------------*/ + +void xc_raise() +{ + short *sel, topsel, maxsel, *topidx, limit, *orderlist, i; + genericptr *raiseobj, temp; + + orderlist = (short *)malloc(topobject->parts * sizeof(short)); + for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i; + + /* Find topmost element in the select list */ + maxsel = -1; + for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects; + sel++) { + if (*sel > maxsel) { + maxsel = *sel; + topidx = sel; + } + } + if (maxsel == -1) return; /* Error condition */ + + topsel = maxsel; + limit = topobject->parts - 1; + while (1) { + + /* Exchange the topmost element with the one above it */ + if (topsel < limit) { + raiseobj = topobject->plist + topsel; + temp = *raiseobj; + *raiseobj = *(raiseobj + 1); + *(raiseobj + 1) = temp; + (*topidx)++; + i = *(orderlist + topsel); + *(orderlist + topsel) = *(orderlist + topsel + 1); + *(orderlist + topsel + 1) = i; + } + else + limit = topsel - 1; + + /* Find next topmost element */ + topsel = -1; + for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects; + sel++) { + if (*sel < maxsel) { + if (*sel > topsel) { + topsel = *sel; + topidx = sel; + } + } + } + if (topsel == -1) break; /* No more elements to raise */ + maxsel = topsel; + } + register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist, + topobject->parts); +} + +/*--------------------------------------------------------------*/ +/* Lower all selected elements by one position in the list */ +/*--------------------------------------------------------------*/ + +void xc_lower() +{ + short *sel, botsel, minsel, *botidx, limit, *orderlist, i; + genericptr *lowerobj, temp; + + orderlist = (short *)malloc(topobject->parts * sizeof(short)); + for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i; + + /* Find bottommost element in the select list */ + minsel = topobject->parts; + for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects; + sel++) { + if (*sel < minsel) { + minsel = *sel; + botidx = sel; + } + } + if (minsel == topobject->parts) return; /* Error condition */ + + botsel = minsel; + limit = 0; + while (1) { + + /* Exchange the topmost element with the one below it */ + if (botsel > limit) { + lowerobj = topobject->plist + botsel; + temp = *lowerobj; + *lowerobj = *(lowerobj - 1); + *(lowerobj - 1) = temp; + (*botidx)--; + i = *(orderlist + botsel); + *(orderlist + botsel) = *(orderlist + botsel - 1); + *(orderlist + botsel - 1) = i; + } + else + limit = botsel + 1; + + /* Find next topmost element */ + botsel = topobject->parts; + for (sel = areawin->selectlist; sel < areawin->selectlist + areawin->selects; + sel++) { + if (*sel > minsel) { + if (*sel < botsel) { + botsel = *sel; + botidx = sel; + } + } + } + if (botsel == topobject->parts) break; /* No more elements to raise */ + minsel = botsel; + } + register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, orderlist, + topobject->parts); +} + +/*------------------------------------------------------*/ +/* 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 = areawin->selectlist; selectno < areawin->selectlist + + areawin->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, *orderlist, i; + genericptr *exchobj, *exchobj2, temp; + Boolean preselected; + + preselected = (areawin->selects > 0) ? TRUE : FALSE; + if (!checkselect(ALL_TYPES)) { + Wprintf("Select 1 or 2 objects"); + return; + } + + selectno = areawin->selectlist; + orderlist = (short *)malloc(topobject->parts * sizeof(short)); + for (i = 0; i < topobject->parts; i++) *(orderlist + i) = i; + + if (areawin->selects == 1) { /* lower if on top; raise otherwise */ + if (*selectno == topobject->parts - 1) + xc_bottom(selectno, orderlist); + else + xc_top(selectno, orderlist); + } + else { /* exchange the two objects */ + exchobj = topobject->plist + *selectno; + exchobj2 = topobject->plist + *(selectno + 1); + + temp = *exchobj; + *exchobj = *exchobj2; + *exchobj2 = temp; + + i = *(orderlist + *selectno); + *(orderlist + *selectno) = *(orderlist + *(selectno + 1)); + *(orderlist + *(selectno + 1)) = i; + } + register_for_undo(XCF_Reorder, UNDO_MORE, areawin->topinstance, + orderlist, topobject->parts); + + incr_changes(topobject); + if (!preselected) + clearselects(); + drawarea(NULL, NULL, NULL); +} + +/*--------------------------------------------------------*/ +/* Flip an element horizontally (POLYGON, ARC, or SPLINE) */ +/*--------------------------------------------------------*/ + +void elhflip(genericptr *genobj, short x) +{ + switch(ELEMENTTYPE(*genobj)) { + case POLYGON:{ + polyptr flippoly = TOPOLY(genobj); + pointlist ppoint; + for (ppoint = flippoly->points; ppoint < flippoly->points + + flippoly->number; ppoint++) + ppoint->x = (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 = (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 = (x << 1) - flipspline->ctrl[i].x; + calcspline(flipspline); + }break; + } +} + +/*--------------------------------------------------------*/ +/* Flip an element vertically (POLYGON, ARC, or SPLINE) */ +/*--------------------------------------------------------*/ + +void elvflip(genericptr *genobj, short y) +{ + switch(ELEMENTTYPE(*genobj)) { + + case POLYGON:{ + polyptr flippoly = TOPOLY(genobj); + pointlist ppoint; + + for (ppoint = flippoly->points; ppoint < flippoly->points + + flippoly->number; ppoint++) + ppoint->y = (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 = (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 = (y << 1) - flipspline->ctrl[i].y; + calcspline(flipspline); + }break; + } +} + +/*------------------------------------------------------*/ +/* Horizontally flip an element */ +/*------------------------------------------------------*/ + +void elementflip(XPoint *position) +{ + short *selectobj; + Boolean single = False; + Boolean preselected; + + preselected = (areawin->selects > 0) ? TRUE : FALSE; + if (!checkselect(ALL_TYPES)) return; + if (areawin->selects == 1) single = True; + + if (eventmode != COPY_MODE) + register_for_undo(XCF_Flip_X, UNDO_MORE, areawin->topinstance, + (eventmode == MOVE_MODE) ? &areawin->origin : position); + + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + + /* erase the object */ + SetFunction(dpy, areawin->gc, GXcopy); + SetForeground(dpy, areawin->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 = (position->x << 1) - fliplab->position.x; + }break; + case GRAPHIC:{ + graphicptr flipg = SELTOGRAPHIC(selectobj); + flipg->scale = -flipg->scale; + flipg->valid = FALSE; + if (!single) + flipg->position.x = (position->x << 1) - flipg->position.x; + }break; + case OBJINST:{ + objinstptr flipobj = SELTOOBJINST(selectobj); + if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break; + flipobj->scale = -flipobj->scale; + if (!single) + flipobj->position.x = (position->x << 1) - flipobj->position.x; + }break; + case POLYGON: case ARC: case SPLINE: + elhflip(topobject->plist + *selectobj, position->x); + break; + case PATH:{ + genericptr *genpart; + pathptr flippath = SELTOPATH(selectobj); + + for (genpart = flippath->plist; genpart < flippath->plist + + flippath->parts; genpart++) + elhflip(genpart, position->x); + }break; + } + + if (preselected || (eventmode != NORMAL_MODE)) { + SetForeground(dpy, areawin->gc, SELECTCOLOR); + easydraw(*selectobj, DOFORALL); + } + } + select_invalidate_netlist(); + if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) + if (!preselected) + unselect_all(); + + if (eventmode == NORMAL_MODE) + incr_changes(topobject); + if (eventmode == CATALOG_MODE) { + int libnum; + if ((libnum = is_library(topobject)) >= 0) { + composelib(libnum + LIBRARY); + drawarea(NULL, NULL, NULL); + } + } + else { + pwriteback(areawin->topinstance); + calcbbox(areawin->topinstance); + } +} + +/*----------------------------------------------*/ +/* Vertically flip an element */ +/*----------------------------------------------*/ + +void elementvflip(XPoint *position) +{ + short *selectobj; + /*short fsign; (jdk) */ + Boolean preselected; + Boolean single = False; + + preselected = (areawin->selects > 0) ? TRUE : FALSE; + if (!checkselect(ALL_TYPES)) return; + if (areawin->selects == 1) single = True; + + if (eventmode != COPY_MODE) + register_for_undo(XCF_Flip_Y, UNDO_MORE, areawin->topinstance, + (eventmode == MOVE_MODE) ? &areawin->origin : position); + + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + + /* erase the object */ + SetFunction(dpy, areawin->gc, GXcopy); + SetForeground(dpy, areawin->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 = (position->y << 1) - fliplab->position.y; + } break; + case(OBJINST):{ + objinstptr flipobj = SELTOOBJINST(selectobj); + + if (is_library(topobject) >= 0 && !is_virtual(flipobj)) break; + flipobj->scale = -(flipobj->scale); + flipobj->rotation += 180; + while (flipobj->rotation >= 360) flipobj->rotation -= 360; + if (!single) + flipobj->position.y = (position->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 = (position->y << 1) - flipg->position.y; + }break; + case POLYGON: case ARC: case SPLINE: + elvflip(topobject->plist + *selectobj, position->y); + break; + case PATH:{ + genericptr *genpart; + pathptr flippath = SELTOPATH(selectobj); + + for (genpart = flippath->plist; genpart < flippath->plist + + flippath->parts; genpart++) + elvflip(genpart, position->y); + }break; + } + if (preselected || (eventmode != NORMAL_MODE)) { + SetForeground(dpy, areawin->gc, SELECTCOLOR); + easydraw(*selectobj, DOFORALL); + } + } + select_invalidate_netlist(); + if (eventmode == NORMAL_MODE || eventmode == CATALOG_MODE) + if (!preselected) + unselect_all(); + if (eventmode == NORMAL_MODE) { + incr_changes(topobject); + } + if (eventmode == CATALOG_MODE) { + int libnum; + if ((libnum = is_library(topobject)) >= 0) { + composelib(libnum + LIBRARY); + drawarea(NULL, NULL, NULL); + } + } + else { + pwriteback(areawin->topinstance); + calcbbox(areawin->topinstance); + } +} + +/*----------------------------------------*/ +/* ButtonPress handler during delete mode */ +/*----------------------------------------*/ + +void deletebutton(int x, int y) +{ + if (checkselect(ALL_TYPES)) { + standard_element_delete(ERASE); + calcbbox(areawin->topinstance); + } + setoptionmenu(); /* Return GUI check/radio boxes to default */ +} + +/*----------------------------------------------------------------------*/ +/* 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 areawin->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, areawin->gc, GXcopy); + SetForeground(dpy, areawin->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 == areawin->selectlist) + freeselects(); + + calcbbox(thisinstance); + /* freenetlist(thisobject); */ + + if (drawmode) { + SetForeground(dpy, areawin->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, areawin->topinstance, +// areawin->selectlist, areawin->selects); + select_invalidate_netlist(); + delobj = delete_element(areawin->topinstance, areawin->selectlist, + areawin->selects, drawmode); + register_for_undo(XCF_Delete, UNDO_DONE, areawin->topinstance, + delobj, (int)drawmode); + incr_changes(topobject); /* Treat as one change */ +} + +/*----------------------------------------------------------------------*/ +/* Another wrapper for delete_element(), in which we do not save the */ +/* deletion as an undo event. However, the returned object is saved */ +/* on areawin->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(areawin->editstack, DESTROY); + areawin->editstack = delete_element(areawin->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; + + select_invalidate_netlist(); + delobj = delete_element(areawin->topinstance, areawin->selectlist, + areawin->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, i; /* position; (jdk) */ + + thisobject = thisinstance->thisobject; + slist = (short *)malloc(delobj->parts * sizeof(short)); + count = 0; + + if (mode) { + SetFunction(dpy, areawin->gc, GXcopy); + } + + for (regen = delobj->plist; regen < delobj->plist + delobj->parts; regen++) { + PLIST_INCR(thisobject); + if (olist == NULL) { + *(slist + count) = thisobject->parts; + *(topobject->plist + topobject->parts) = *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); + + /* flush the delete buffer but don't delete the elements */ + reset(delobj, SAVE); + + if (delobj != areawin->editstack) free(delobj); + + return slist; +} + +/*----------------------------*/ +/* select save object handler */ +/*----------------------------*/ + +void printname(objectptr curobject) +{ + char editstr[10], pagestr[10]; + short ispage; + +#ifndef TCL_WRAPPER + Arg wargs[1]; + Dimension swidth, swidth2, sarea; + char tmpname[256]; + char *sptr = tmpname; +#endif + + /* print full string to make message widget proper size */ + + strcpy(editstr, ((ispage = is_page(curobject)) >= 0) ? "Editing: " : ""); + strcpy(editstr, (is_library(curobject) >= 0) ? "Library: " : ""); + if (strstr(curobject->name, "Page") == NULL && (ispage >= 0)) + sprintf(pagestr, " (p. %d)", areawin->page + 1); + else + pagestr[0] = '\0'; + W2printf("%s%s%s", editstr, curobject->name, pagestr); + + /* Tcl doesn't update width changes immediately. . . what to do? */ + /* (i.e., Tk_Width(message2) gives the original width) */ +#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 = '.'; + + W2printf("Editing: %s", sptr); + } +#endif +} + +/*--------------------------------------------------------------*/ +/* Make sure that a string does not conflict with postscript */ +/* names (commands and definitions found in xcircps2.pro). */ +/* */ +/* Return value is NULL if no change was made. Otherwise, the */ +/* return value is an allocated string. */ +/*--------------------------------------------------------------*/ + +char *checkvalidname(char *teststring, objectptr newobj) +{ + int i, j; + short dupl; /* flag a duplicate string */ + objectptr *libobj; + char *sptr, *pptr, *cptr; + aliasptr aref; + slistptr sref; + + /* Try not to allocate memory unless necessary */ + + sptr = teststring; + pptr = sptr; + + do { + dupl = 0; + 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)) { + + /* Prepend an underscore to the object name. If the */ + /* object has no technology, create a null technology */ + /* name. Otherwise, the technology remains the same */ + /* but the object name gets the prepended underscore. */ + + if ((cptr = strstr(pptr, "::")) == NULL) { + pptr = (char *)malloc(strlen((*libobj)->name) + 3); + sprintf(pptr, "::_%s", (*libobj)->name); + } + else { + int offset = cptr - pptr + 2; + if (pptr == sptr) + pptr = (char *)malloc(strlen((*libobj)->name) + 2); + else + pptr = (char *)realloc(pptr, strlen((*libobj)->name) + 2); + sprintf(pptr, "%s", (*libobj)->name); + sprintf(pptr + offset, "_%s", (*libobj)->name + offset); + } + dupl = 1; + } + } + } + + /* 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)) { + if (pptr == sptr) + pptr = (char *)malloc(strlen(sref->alias) + 2); + else + pptr = (char *)realloc(pptr, strlen(sref->alias) + 2); + sprintf(pptr, "_%s", sref->alias); + dupl = 1; + } + } + } + } + } + + } while (dupl == 1); + + return (pptr == sptr) ? NULL : pptr; +} + +/*--------------------------------------------------------------*/ +/* Make sure that name for new object does not conflict with */ +/* existing object definitions */ +/* */ +/* Return: True if name required change, False otherwise */ +/*--------------------------------------------------------------*/ + +Boolean checkname(objectptr newobj) +{ + char *pptr; + + /* Check for empty string */ + if (strlen(newobj->name) == 0) { + Wprintf("Blank object name changed to default"); + sprintf(newobj->name, "user_object"); + } + + pptr = checkvalidname(newobj->name, newobj); + + /* Change name if necessary to avoid naming conflicts */ + if (pptr == NULL) { + Wprintf("Created new object %s", newobj->name); + return False; + } + else { + Wprintf("Changed name from %s to %s to avoid conflict with " + "existing object", newobj->name, pptr); + strncpy(newobj->name, pptr, 79); + free(pptr); + } + return True; +} + +/*------------------------------------------------------------*/ +/* Find the object "dot" in the builtin library, if it exists */ +/*------------------------------------------------------------*/ + +objectptr finddot() +{ + objectptr dotobj; + short i, j; + char *name, *pptr; + + for (i = 0; i < xobjs.numlibs; i++) { + for (j = 0; j < xobjs.userlibs[i].number; j++) { + dotobj = *(xobjs.userlibs[i].library + j); + name = dotobj->name; + if ((pptr = strstr(name, "::")) != NULL) name = pptr + 2; + if (!strcmp(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; + case OBJINST: + TOOBJINST(ssgen)->position.x += deltax; + TOOBJINST(ssgen)->position.y += deltay; + break; + case GRAPHIC: + TOGRAPHIC(ssgen)->position.x += deltax; + TOGRAPHIC(ssgen)->position.y += deltay; + break; + case LABEL: + TOLABEL(ssgen)->position.x += deltax; + TOLABEL(ssgen)->position.y += deltay; + break; + } +} + +/*----------------------------------------------------------------------*/ +/* Add value origin to all edited points, according to edit flags */ +/*----------------------------------------------------------------------*/ + +void editpoints(genericptr *ssgen, short deltax, short deltay) +{ + pathptr editpath; + polyptr editpoly; + splineptr editspline; + short cycle, cpoint; + pointselect *cptr; + XPoint *curpt; + genericptr *ggen; + + switch(ELEMENTTYPE(*ssgen)) { + case POLYGON: + editpoly = TOPOLY(ssgen); + if (editpoly->cycle == NULL) + movepoints(ssgen, deltax, deltay); + else { + for (cptr = editpoly->cycle;; cptr++) { + cycle = cptr->number; + curpt = editpoly->points + cycle; + if (cptr->flags & EDITX) curpt->x += deltax; + if (cptr->flags & EDITY) curpt->y += deltay; + if (cptr->flags & LASTENTRY) break; + } + } + exprsub(*ssgen); + break; + + case SPLINE: + editspline = TOSPLINE(ssgen); + if (editspline->cycle == NULL) + movepoints(ssgen, deltax, deltay); + else { + for (cptr = editspline->cycle;; cptr++) { + cycle = cptr->number; + if (cycle == 0 || cycle == 3) { + cpoint = (cycle == 0) ? 1 : 2; + if (cptr->flags & EDITX) editspline->ctrl[cpoint].x += deltax; + if (cptr->flags & EDITY) editspline->ctrl[cpoint].y += deltay; + } + if (cptr->flags & EDITX) editspline->ctrl[cycle].x += deltax; + if (cptr->flags & EDITY) editspline->ctrl[cycle].y += deltay; + if (cptr->flags & ANTIXY) { + editspline->ctrl[cycle].x -= deltax; + editspline->ctrl[cycle].y -= deltay; + } + if (cptr->flags & LASTENTRY) break; + } + } + exprsub(*ssgen); + calcspline(editspline); + break; + + case PATH: + editpath = TOPATH(ssgen); + if (checkcycle(*ssgen, 0) < 0) { + for (ggen = editpath->plist; ggen < editpath->plist + editpath->parts; + ggen++) + movepoints(ggen, deltax, deltay); + } + else { + for (ggen = editpath->plist; ggen < editpath->plist + editpath->parts; + ggen++) { + if (checkcycle(*ggen, 0) >= 0) + editpoints(ggen, deltax, deltay); + } + } + break; + + default: + movepoints(ssgen, deltax, deltay); + exprsub(*ssgen); + break; + } +} + +#ifndef TCL_WRAPPER + +void xlib_makeobject(xcWidget w, caddr_t nulldata) +{ + domakeobject(-1, (char *)_STR2, FALSE); +} + +#endif /* !TCL_WRAPPER */ + +/*--------------------------------------------------------------*/ +/* Set the name for a new user-defined object and make the */ +/* object. If "forceempty" is true, we allow creation of a new */ +/* object with no elements (normally would be used only from a */ +/* script, where an object is being constructed automatically). */ +/*--------------------------------------------------------------*/ + +objinstptr domakeobject(int libnum, char *name, Boolean forceempty) +{ + 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(areawin->topinstance, areawin->selectlist, + areawin->selects, NORMAL); + + if (*newobj == NULL) { + objectptr initobj; + + if (!forceempty) return NULL; + + /* Create a new (empty) object */ + + initobj = (objectptr) malloc(sizeof(object)); + initmem(initobj); + *newobj = initobj; + } + + invalidate_netlist(topobject); + xobjs.userlibs[loclibnum].number++; + + /* Create the instance of this object so we can compute a bounding box */ + + NEW_OBJINST(newinst, topobject); + instancedefaults(*newinst, *newobj, 0, 0); + calcbbox(*newinst); + + /* find closest snap point to bbox center and make this the obj. center */ + + if (areawin->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->flags |= P_INDIRECT; + 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->flags |= P_INDIRECT; + 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, areawin->gc, GXcopy); + XTopSetForeground((*newinst)->color); + UDrawObject(*newinst, SINGLE, (*newinst)->color, + xobjs.pagelist[areawin->page]->wirewidth, NULL); + + + /* Copy name into object and check for conflicts */ + + strcpy((*newobj)->name, name); + checkname(*newobj); + + /* register the technology and mark the technology as not saved */ + AddObjectTechnology(*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 and reset view bounds */ + + composelib(loclibnum + LIBRARY); + centerview(xobjs.libtop[loclibnum + LIBRARY]); + + return *newinst; +} + +#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 (areawin->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, areawin->editstack); + arccopy(*newarc, lastarc); + copycycles(&((*newarc)->cycle), &(lastarc->cycle)); +} + +/*--------------------------------------*/ + +void splineeditpush(splineptr lastspline) +{ + splineptr *newspline; + + NEW_SPLINE(newspline, areawin->editstack); + splinecopy(*newspline, lastspline); +} + +/*--------------------------------------*/ + +void polyeditpush(polyptr lastpoly) +{ + polyptr *newpoly; + + NEW_POLY(newpoly, areawin->editstack); + polycopy(*newpoly, lastpoly); +} + +/*--------------------------------------*/ + +void patheditpush(pathptr lastpath) +{ + pathptr *newpath; + + NEW_PATH(newpath, areawin->editstack); + pathcopy(*newpath, lastpath); +} + +/*-----------------------------*/ +/* 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) +{ + Imagedata *iptr; + int i; + + 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; + + /* Update the refcount of the source image */ + for (i = 0; i < xobjs.images; i++) { + iptr = xobjs.imagelist + i; + if (iptr->image == newg->source) { + iptr->refcount++; + break; + } + } +} + +/*------------------------------------------*/ + +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; + newtext->cycle = 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; + newarc->cycle = 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; + copycycles(&(newpoly->cycle), &(copypoly->cycle)); + 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; + copycycles(&(newspline->cycle), &(copyspline->cycle)); + 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 a path */ +/*----------------------------------------------*/ + +void pathcopy(pathptr newpath, pathptr copypath) +{ + genericptr *ggen; + splineptr *newspline, copyspline; + polyptr *newpoly, copypoly; + + newpath->style = copypath->style; + newpath->color = copypath->color; + newpath->width = copypath->width; + newpath->parts = 0; + newpath->passed = NULL; + copyalleparams((genericptr)newpath, (genericptr)copypath); + newpath->plist = (genericptr *)malloc(copypath->parts * sizeof(genericptr)); + + for (ggen = copypath->plist; ggen < copypath->plist + copypath->parts; ggen++) { + switch (ELEMENTTYPE(*ggen)) { + case POLYGON: + copypoly = TOPOLY(ggen); + NEW_POLY(newpoly, newpath); + polycopy(*newpoly, copypoly); + break; + case SPLINE: + copyspline = TOSPLINE(ggen); + NEW_SPLINE(newspline, newpath); + splinecopy(*newspline, copyspline); + break; + } + } +} + +/*--------------------------------------------------------------*/ +/* 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->style = copyobj->style; + 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(objinstptr thisinst) { + Boolean tagged = True; + objectptr thisobject, delobj; + genericptr *pgen; + short *sobj, stmp; + + thisobject = thisinst->thisobject; + + while (tagged) { + tagged = False; + for (stmp = 0; stmp < thisobject->parts; stmp++) { + pgen = thisobject->plist + stmp; + if ((*pgen)->type & REMOVE_TAG) { + (*pgen)->type &= (~REMOVE_TAG); + tagged = True; + + delobj = delete_element(thisinst, &stmp, 1, 0); + register_for_undo(XCF_Delete, UNDO_MORE, thisinst, delobj, 0); + + /* If we destroy elements in the current window, we need to */ + /* make sure that the selection list is updated appropriately. */ + + if ((thisobject == topobject) && (areawin->selects > 0)) { + for (sobj = areawin->selectlist; sobj < areawin->selectlist + + areawin->selects; sobj++) + if (*sobj > stmp) (*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); + } + } + } + undo_finish_series(); +} + +/*-----------------------------------------------------------------*/ +/* 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 = areawin->selectlist; sobj < areawin->selectlist + + areawin->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 = areawin->selectlist; cobj < areawin->selectlist + + areawin->selects; cobj++) { + if (pgen == topobject->plist + (*cobj)) break; + } + /* Tag it for future deletion and prevent further compares */ + if (cobj == areawin->selectlist + areawin->selects) { + tagged = True; + (*pgen)->type |= REMOVE_TAG; + } + } + } + } + if (tagged) { + /* Delete the tagged elements */ + Wprintf("Duplicate object deleted"); + delete_tagged(areawin->topinstance); + incr_changes(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, *ppt; + int rot; + short closest, cycle; + Boolean doattach; + genericptr *pgen; + polyptr cpoly; + pointselect *cptr; + + doattach = ((userpt == NULL) || (areawin->attachto < 0)) ? FALSE : TRUE; + + /* under attachto condition, keep element attached to */ + /* the attachto element. */ + + if (doattach) findattach(&newpos, &rot, userpt); + + areawin->clipped = -1; /* Prevent clipping */ + for (dragselect = areawin->selectlist; dragselect < areawin->selectlist + + areawin->selects; dragselect++) { + + switch(SELECTTYPE(dragselect)) { + case OBJINST: { + objinstptr draginst = SELTOOBJINST(dragselect); + + UDrawObject(draginst, SINGLE, DOFORALL, + xobjs.pagelist[areawin->page]->wirewidth, NULL); + if (doattach) { + draginst->position.x = newpos.x; + draginst->position.y = newpos.y; + while (rot >= 360) rot -= 360; + while (rot < 0) rot += 360; + draginst->rotation = rot; + } + else { + draginst->position.x += deltax; + draginst->position.y += deltay; + } + UDrawObject(draginst, SINGLE, DOFORALL, + xobjs.pagelist[areawin->page]->wirewidth, 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, areawin->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, areawin->topinstance); + if (draglabel->pin == False) + UDrawX(draglabel); + } break; + case PATH: { + pathptr dragpath = SELTOPATH(dragselect); + genericptr *pathlist; + + UDrawPath(dragpath, xobjs.pagelist[areawin->page]->wirewidth); + 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, xobjs.pagelist[areawin->page]->wirewidth); + } break; + case POLYGON: { + polyptr dragpoly = SELTOPOLY(dragselect); + pointlist dragpoints; + + // if (dragpoly->cycle != NULL) continue; + UDrawPolygon(dragpoly, xobjs.pagelist[areawin->page]->wirewidth); + 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, xobjs.pagelist[areawin->page]->wirewidth); + } break; + case SPLINE: { + splineptr dragspline = SELTOSPLINE(dragselect); + short j; + fpointlist dragpoints; + + // if (dragspline->cycle != NULL) continue; + UDrawSpline(dragspline, xobjs.pagelist[areawin->page]->wirewidth); + 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, xobjs.pagelist[areawin->page]->wirewidth); + } break; + case ARC: { + arcptr dragarc = SELTOARC(dragselect); + fpointlist dragpoints; + + UDrawArc(dragarc, xobjs.pagelist[areawin->page]->wirewidth); + 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, xobjs.pagelist[areawin->page]->wirewidth); + } break; + } + } + + if (areawin->pinattach) { + for (pgen = topobject->plist; pgen < topobject->plist + + topobject->parts; pgen++) { + if (ELEMENTTYPE(*pgen) == POLYGON) { + cpoly = TOPOLY(pgen); + if (cpoly->cycle != NULL) { + ppt = cpoly->points + cpoly->cycle->number; + UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth); + newpos.x = ppt->x + deltax; + newpos.y = ppt->y + deltay; + if (areawin->manhatn) + manhattanize(&newpos, cpoly, cpoly->cycle->number, FALSE); + ppt->x = newpos.x; + ppt->y = newpos.y; + UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth); + } + } + } + } + areawin->clipped = 0; +} + +/*----------------------------------------------------------------------*/ +/* Copy handler. Assumes that the selectlist contains the elements */ +/* to be copied, and that the initial position of the copy is held */ +/* in areawin->save. */ +/*----------------------------------------------------------------------*/ + +void createcopies() +{ + short *selectobj; + + if (!checkselect_draw(ALL_TYPES, True)) return; + u2u_snap(&areawin->save); + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + + /* Cycles will not be used for copy mode: remove them */ + removecycle(topobject->plist + (*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; + NEW_PATH(newpath, topobject); + pathcopy(*newpath, copypath); + } 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 - 1; + } +} + +/*--------------------------------------------------------------*/ +/* Function which initiates interactive placement of copied */ +/* elements. */ +/*--------------------------------------------------------------*/ + +void copydrag() +{ + short *selectobj; + + if (areawin->selects > 0) { + /* Put all selected objects into the "select" color */ + + SetFunction(dpy, areawin->gc, GXxor); + for (selectobj = areawin->selectlist; selectobj < areawin->selectlist + + areawin->selects; selectobj++) { + XSetXORFg(SELTOCOLOR(selectobj), BACKGROUND); + easydraw(*selectobj, DOFORALL); + } + + if (eventmode == NORMAL_MODE) { + XDefineCursor(dpy, areawin->window, COPYCURSOR); + eventmode = COPY_MODE; +#ifdef TCL_WRAPPER + Tk_CreateEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + XtAddEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, False, (XtEventHandler)xlib_drag, + NULL); +#endif + } + select_invalidate_netlist(); + } +} + +/*-----------------------------------------------------------*/ +/* 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, &areawin->save); + createcopies(); /* This function does all the hard work */ + copydrag(); /* Start interactive placement */ + } + else { + eventmode = NORMAL_MODE; + areawin->attachto = -1; + W3printf(""); +#ifdef TCL_WRAPPER + xcRemoveEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, False, (xcEventHandler)xctk_drag, + NULL); +#else + xcRemoveEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, False, (xcEventHandler)xlib_drag, + NULL); +#endif + XDefineCursor(dpy, areawin->window, DEFAULTCURSOR); + u2u_snap(&areawin->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 == areawin->selects) + calcbbox(areawin->topinstance); + else + calcbboxselect(); + checkoverlap(); + register_for_undo(XCF_Copy, UNDO_MORE, areawin->topinstance, + areawin->selectlist, areawin->selects); + unselect_all(); + incr_changes(topobject); + } + else { /* XCF_Continue_Copy */ + SetFunction(dpy, areawin->gc, GXcopy); + SetForeground(dpy, areawin->gc, SELECTCOLOR); + for (csel = areawin->selectlist; csel < areawin->selectlist + + areawin->selects; csel++) + easydraw(*csel, DOFORALL); + if (topobject->parts == areawin->selects) + calcbbox(areawin->topinstance); + else + calcbboxselect(); + checkoverlap(); + register_for_undo(XCF_Copy, UNDO_DONE, areawin->topinstance, + areawin->selectlist, areawin->selects); + createcopies(); + copydrag(); /* Start interactive placement again */ + incr_changes(topobject); + } + } +} + +/*----------------------------------------------*/ +/* 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, &areawin->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): case(EPOLY_MODE): case (ARC_MODE): + case(EARC_MODE): case(SPLINE_MODE): case(ESPLINE_MODE): + path_op(*(EDITPART), op, x, y); + break; + case(EINST_MODE): + inst_op(*(EDITPART), op, x, y); + break; + case(BOX_MODE): + finish_op(XCF_Finish_Element, 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; + int libnum; + XPoint snappt, boxpts[4]; + float fscale; + + if (eventmode != EARC_MODE && eventmode != ARC_MODE) { + window_to_user(x, y, &areawin->save); + } + switch(eventmode) { + case(EPATH_MODE): 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(EINST_MODE): + inst_op(*(EDITPART), 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, areawin->window, 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); + topobject->parts--; + curlabel = NULL; + } + else { + singlebbox(EDITPART); + incr_changes(topobject); + select_invalidate_netlist(); + } + 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_finish_series(); + undo_action(); + if (eventmode == CATTEXT_MODE) eventmode = CATALOG_MODE; + redrawtext(curlabel); + W3printf(""); + setdefaultfontmarks(); + } + else textreturn(); /* Generate "return" key character */ + areawin->textend = 0; + break; + + case(CATMOVE_MODE): + u2u_snap(&areawin->save); +#ifdef TCL_WRAPPER + Tk_DeleteEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcRemoveEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, FALSE, (xcEventHandler)xlib_drag, + NULL); +#endif + if (op == XCF_Cancel) { + /* Just regenerate the library where we started */ + if (areawin->selects >= 1) { + objinstptr selinst = SELTOOBJINST(areawin->selectlist); + libnum = libfindobject(selinst->thisobject, NULL); + if (libnum >= 0) + composelib(libnum + LIBRARY); + } + } + else { + catmove(x, y); + } + clearselects(); + eventmode = CATALOG_MODE; + XDefineCursor(dpy, areawin->window, DEFAULTCURSOR); + break; + + case(MOVE_MODE): + u2u_snap(&areawin->save); +#ifdef TCL_WRAPPER + Tk_DeleteEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcRemoveEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, 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 areawin->editpart. */ + + if ((areawin->selects > 0) && (*areawin->selectlist == topobject->parts)) + delete_noundo(NORMAL); + else + placeselects(areawin->origin.x - areawin->save.x, + areawin->origin.y - areawin->save.y, NULL); + clearselects(); + drawarea(NULL, NULL, NULL); + } + else { + if (areawin->selects > 0) { + register_for_undo(XCF_Move, + // (was_preselected) ? UNDO_DONE : UNDO_MORE, + UNDO_MORE, + areawin->topinstance, + (int)(areawin->save.x - areawin->origin.x), + (int)(areawin->save.y - areawin->origin.y)); + pwriteback(areawin->topinstance); + incr_changes(topobject); + select_invalidate_netlist(); + unselect_all(); // The way it used to be. . . + } + W3printf(""); + /* full calc needed: move may shrink bbox */ + calcbbox(areawin->topinstance); + checkoverlap(); + // if (!was_preselected) clearselects(); + } + areawin->attachto = -1; + break; + + case(RESCALE_MODE): + +#ifdef TCL_WRAPPER + Tk_DeleteEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcRemoveEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, FALSE, (xcEventHandler)xlib_drag, + NULL); +#endif + if (op != XCF_Cancel) { + fscale = UDrawRescaleBox(&areawin->save); + if (fscale != 0.0) elementrescale(fscale); + } + eventmode = NORMAL_MODE; + break; + + case(SELAREA_MODE): + UDrawBox(areawin->origin, areawin->save); + +#ifdef TCL_WRAPPER + Tk_DeleteEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcRemoveEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, FALSE, (xcEventHandler)xlib_drag, + NULL); +#endif + /* Zero-width boxes act like a normal selection. Otherwise, */ + /* proceed with the area select. */ + + if ((areawin->origin.x == areawin->save.x) && + (areawin->origin.y == areawin->save.y)) + select_add_element(ALL_TYPES); + else { + boxpts[0] = areawin->origin; + boxpts[1].x = areawin->save.x; + boxpts[1].y = areawin->origin.y; + boxpts[2] = areawin->save; + boxpts[3].x = areawin->origin.x; + boxpts[3].y = areawin->save.y; + selectarea(topobject, boxpts, 0); + } + break; + + case(PAN_MODE): + u2u_snap(&areawin->save); + +#ifdef TCL_WRAPPER + Tk_DeleteEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); +#else + xcRemoveEventHandler(areawin->area, PointerMotionMask | + ButtonMotionMask, False, (xcEventHandler)xlib_drag, + NULL); +#endif + if (op != XCF_Cancel) + panbutton((u_int) 5, (areawin->width >> 1) - (x - areawin->origin.x), + (areawin->height >> 1) - (y - areawin->origin.y), 0); + break; + } + + /* Remove any selections */ + if ((eventmode == SELAREA_MODE) || (eventmode == PAN_MODE) + || (eventmode == MOVE_MODE)) + eventmode = NORMAL_MODE; + else if (eventmode != MOVE_MODE && eventmode != EPATH_MODE && + eventmode != EPOLY_MODE && eventmode != ARC_MODE && + eventmode != EARC_MODE && eventmode != SPLINE_MODE && + eventmode != ESPLINE_MODE && eventmode != WIRE_MODE && + eventmode != ETEXT_MODE && eventmode != TEXT_MODE) { + unselect_all(); + } + + if (eventmode == NORMAL_MODE) { + + /* Return any highlighted networks to normal */ + highlightnetlist(topobject, areawin->topinstance, 0); + + XDefineCursor(dpy, areawin->window, DEFAULTCURSOR); + +#ifdef DOUBLEBUFFER + drawarea(NULL, NULL, NULL); +#endif + } + + snap(x, y, &snappt); + printpos(snappt.x, snappt.y); +} + +/*--------------------------------------------------------------*/ +/* Edit operations for instances. This is used to allow */ +/* numeric parameters to be adjusted from the hierarchical */ +/* level above, shielding the the unparameterized parts from */ +/* change. */ +/*--------------------------------------------------------------*/ + +void inst_op(genericptr editpart, int op, int x, int y) +{ +} + +/*--------------------------------------------------------------*/ +/* Operations for path components */ +/*--------------------------------------------------------------*/ + +void path_op(genericptr editpart, int op, int x, int y) +{ + polyptr newpoly; + splineptr newspline; + genericptr *ggen; + Boolean donecycles = False; + XPoint *refpt, *cptr; + + /* Don't allow point cycling in a multi-part edit. */ + /* Allowing it is just confusing. Instead, we treat */ + /* button 1 (cycle) like button 2 (finish). */ + if (op == XCF_Continue_Element && areawin->selects > 1) + op = XCF_Finish_Element; + + switch(ELEMENTTYPE(editpart)) { + case (PATH): { + pathptr newpath = (pathptr)editpart; + short dotrack = True; + pathptr editpath; + + areawin->attachto = -1; + + if (op != XCF_Continue_Element) { + dotrack = False; + UDrawPath(newpath, xobjs.pagelist[areawin->page]->wirewidth); + } + if (op == XCF_Continue_Element) { + nextpathcycle(newpath, 1); + patheditpush(newpath); + } + else if (op == XCF_Finish_Element) { + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(newpath->color); + UDrawPath(newpath, xobjs.pagelist[areawin->page]->wirewidth); + incr_changes(topobject); + } + else { /* restore previous path from edit stack */ + free_single((genericptr)newpath); + if (areawin->editstack->parts > 0) { + if (op == XCF_Cancel) { + editpath = TOPATH(areawin->editstack->plist); + pathcopy(newpath, editpath); + reset(areawin->editstack, NORMAL); + } + else { + editpath = TOPATH(areawin->editstack->plist + + areawin->editstack->parts - 1); + pathcopy(newpath, editpath); + free_single((genericptr)editpath); + free(editpath); + areawin->editstack->parts--; + } + if (areawin->editstack->parts > 0) { + dotrack = True; + nextpathcycle(newpath, 1); + } + else { + XPoint warppt; + + XcSetFunction(GXcopy); + XcTopSetForeground(newpath->color); + user_to_window(areawin->origin, &warppt); + warppointer(warppt.x, warppt.y); + } + UDrawPath(newpath, xobjs.pagelist[areawin->page]->wirewidth); + } + else { + topobject->parts--; + free_single((genericptr)newpath); + free(newpath); + } + } + pwriteback(areawin->topinstance); + + if (!dotrack) { + /* Free the editstack */ + reset(areawin->editstack, NORMAL); + xcRemoveEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = NORMAL_MODE; + donecycles = True; + } + } break; + + case (POLYGON): { + if (eventmode == BOX_MODE) { + polyptr newbox; + + newbox = (polyptr)editpart; + UDrawPolygon(newbox, xobjs.pagelist[areawin->page]->wirewidth); + + /* 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, areawin->gc, GXcopy); + XTopSetForeground(newbox->color); + UDrawPolygon(newbox, xobjs.pagelist[areawin->page]->wirewidth); + incr_changes(topobject); + if (!nonnetwork(newbox)) invalidate_netlist(topobject); + register_for_undo(XCF_Box, UNDO_MORE, areawin->topinstance, + newbox); + } + else { + free_single((genericptr)newbox); + free(newbox); + topobject->parts--; + } + } + else { + free_single((genericptr)newbox); + free(newbox); + topobject->parts--; + } + + xcRemoveEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackbox, NULL); + eventmode = NORMAL_MODE; + } + else { /* EPOLY_MODE */ + polyptr editpoly; + short dotrack = True; + + newpoly = (polyptr)editpart; + areawin->attachto = -1; + + if (op != XCF_Continue_Element) { + dotrack = False; + UDrawPolygon(newpoly, xobjs.pagelist[areawin->page]->wirewidth); + } + + if (op == XCF_Continue_Element) { + nextpolycycle(&newpoly, 1); + polyeditpush(newpoly); + } + else if (op == XCF_Finish_Element) { + + /* Check for degenerate polygons (all points the same). */ + int i; + for (i = 1; i < newpoly->number; i++) + if ((newpoly->points[i].x != newpoly->points[i - 1].x) || + (newpoly->points[i].y != newpoly->points[i - 1].y)) + break; + if (i == newpoly->number) { + /* Remove this polygon with the standard delete */ + /* method (saves polygon on undo stack). */ + newpoly->type |= REMOVE_TAG; + delete_tagged(areawin->topinstance); + } + else { + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(newpoly->color); + UDrawPolygon(newpoly, xobjs.pagelist[areawin->page]->wirewidth); + if (!nonnetwork(newpoly)) invalidate_netlist(topobject); + incr_changes(topobject); + } + } + else { + XPoint warppt; + free_single((genericptr)newpoly); + if (areawin->editstack->parts > 0) { + if (op == XCF_Cancel) { + editpoly = TOPOLY(areawin->editstack->plist); + polycopy(newpoly, editpoly); + reset(areawin->editstack, NORMAL); + } + else { + editpoly = TOPOLY(areawin->editstack->plist + + areawin->editstack->parts - 1); + polycopy(newpoly, editpoly); + free_single((genericptr)editpoly); + free(editpoly); + areawin->editstack->parts--; + } + if (areawin->editstack->parts > 0) { + dotrack = True; + nextpolycycle(&newpoly, -1); + } + else { + XcSetFunction(GXcopy); + XcTopSetForeground(newpoly->color); + user_to_window(areawin->origin, &warppt); + warppointer(warppt.x, warppt.y); + } + UDrawPolygon(newpoly, xobjs.pagelist[areawin->page]->wirewidth); + } + else { + topobject->parts--; + free(newpoly); + } + } + pwriteback(areawin->topinstance); + + if (!dotrack) { + /* Free the editstack */ + reset(areawin->editstack, NORMAL); + + xcRemoveEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = NORMAL_MODE; + donecycles = True; + } + } + } break; + + case (ARC): { + arcptr newarc, editarc; + short dotrack = True; + + newarc = (arcptr)editpart; + + if (op != XCF_Continue_Element) { + dotrack = False; + UDrawArc(newarc, xobjs.pagelist[areawin->page]->wirewidth); + UDrawXLine(areawin->save, newarc->position); + } + + if (op == XCF_Continue_Element) { + nextarccycle(&newarc, 1); + arceditpush(newarc); + } + + else if (op == XCF_Finish_Element) { + dotrack = False; + + if (newarc->radius != 0 && newarc->yaxis != 0 && + (newarc->angle1 != newarc->angle2)) { + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(newarc->color); + incr_changes(topobject); + if (eventmode == ARC_MODE) { + register_for_undo(XCF_Arc, UNDO_MORE, areawin->topinstance, + newarc); + } + UDrawArc(newarc, xobjs.pagelist[areawin->page]->wirewidth); + + } + else { + + /* Remove the record if the radius is zero. If we were */ + /* creating the arc, just delete it; it's as if it */ + /* never existed. If we were editing an arc, use the */ + /* standard delete method (saves arc on undo stack). */ + + if (eventmode == ARC_MODE) { + free_single((genericptr)newarc); + free(newarc); + topobject->parts--; + } + else { + newarc->type |= REMOVE_TAG; + delete_tagged(areawin->topinstance); + } + } + } + else { /* Cancel: restore previous arc from edit stack */ + free_single((genericptr)newarc); + if (areawin->editstack->parts > 0) { + if (op == XCF_Cancel) { + editarc = TOARC(areawin->editstack->plist); + arccopy(newarc, editarc); + copycycles(&(newarc->cycle), &(editarc->cycle)); + reset(areawin->editstack, NORMAL); + } + else { + editarc = TOARC(areawin->editstack->plist + + areawin->editstack->parts - 1); + arccopy(newarc, editarc); + copycycles(&(newarc->cycle), &(editarc->cycle)); + free_single((genericptr)editarc); + free(editarc); + areawin->editstack->parts--; + } + if (areawin->editstack->parts > 0) { + dotrack = True; + nextarccycle(&newarc, -1); + UDrawXLine(areawin->save, newarc->position); + } + else { + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(newarc->color); + if (eventmode != ARC_MODE) { + XPoint warppt; + user_to_window(areawin->origin, &warppt); + warppointer(warppt.x, warppt.y); + } + } + UDrawArc(newarc, xobjs.pagelist[areawin->page]->wirewidth); + } + else + topobject->parts--; + } + pwriteback(areawin->topinstance); + + if (!dotrack) { + /* Free the editstack */ + reset(areawin->editstack, NORMAL); + + xcRemoveEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackarc, NULL); + eventmode = NORMAL_MODE; + } + } break; + + case (SPLINE): { + splineptr editspline; + short dotrack = True; + + newspline = (splineptr)editpart; + + if (op != XCF_Continue_Element) { + UDrawSpline(newspline, xobjs.pagelist[areawin->page]->wirewidth); + dotrack = False; + } + + if (op == XCF_Continue_Element) { + /* Note: we work backwards through spline control points. */ + /* The reason is that when creating a spline, the sudden */ + /* move from the endpoint to the startpoint (forward */ + /* direction) is more disorienting than moving from the */ + /* endpoint to the endpoint's control point. */ + + 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, areawin->gc, GXcopy); + XTopSetForeground(newspline->color); + incr_changes(topobject); + if (eventmode == SPLINE_MODE) { + register_for_undo(XCF_Spline, UNDO_MORE, areawin->topinstance, + newspline); + } + UDrawSpline(newspline, xobjs.pagelist[areawin->page]->wirewidth); + } + else { /* restore previous spline from edit stack */ + free_single((genericptr)newspline); + if (areawin->editstack->parts > 0) { + if (op == XCF_Cancel) { + editspline = TOSPLINE(areawin->editstack->plist); + splinecopy(newspline, editspline); + reset(areawin->editstack, NORMAL); + } + else { + editspline = TOSPLINE(areawin->editstack->plist + + areawin->editstack->parts - 1); + splinecopy(newspline, editspline); + free_single((genericptr)editspline); + free(editspline); + areawin->editstack->parts--; + } + if (areawin->editstack->parts > 0) { + dotrack = True; + nextsplinecycle(&newspline, 1); + UDrawSpline(newspline, xobjs.pagelist[areawin->page]->wirewidth); + } + else { + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(newspline->color); + if (eventmode != SPLINE_MODE) { + XPoint warppt; + user_to_window(areawin->origin, &warppt); + warppointer(warppt.x, warppt.y); + } + UDrawSpline(newspline, xobjs.pagelist[areawin->page]->wirewidth); + } + } + else + topobject->parts--; + } + } + else { + free_single((genericptr)newspline); + free(newspline); + topobject->parts--; + } + pwriteback(areawin->topinstance); + + if (!dotrack) { + /* Free the editstack */ + reset(areawin->editstack, NORMAL); + + xcRemoveEventHandler(areawin->area, PointerMotionMask, False, + (xcEventHandler)trackelement, NULL); + eventmode = NORMAL_MODE; + donecycles = True; + } + } break; + } + calcbbox(areawin->topinstance); + + /* Multiple-element edit: Some items may have been moved as */ + /* opposed to edited, and should be registered here. To do */ + /* this correctly, we must first unselect the edited items, */ + /* then register the move for the remaining items. */ + + if (donecycles) { + short *eselect, cycle, *sellist, *newsel, selects; + Boolean fullmove = False; + + for (eselect = areawin->selectlist; eselect < areawin->selectlist + + areawin->selects; eselect++) { + cycle = checkcycle(SELTOGENERIC(eselect), 0); + if (cycle < 0) { + fullmove = True; /* At least 1 object moved */ + break; + } + } + + /* Remove all (remaining) cycles */ + for (eselect = areawin->selectlist; eselect < areawin->selectlist + + areawin->selects; eselect++) + removecycle(SELTOGENERICPTR(eselect)); + + /* Remove edits from the undo stack when canceling */ + if (op == XCF_Cancel || op == XCF_Cancel_Last) { + if (xobjs.undostack && (xobjs.undostack->type == XCF_Edit)) { + undo_finish_series(); + undo_action(); + } + } + } +} + +/*-------------------------------------------------------*/ +/* 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 = areawin->width, saveheight = areawin->height; + XCWindowData *thiswin; + XGCValues values; + + if ((dpy != NULL) && xcIsRealized(areawin->area)) { + +#ifdef TCL_WRAPPER + areawin->width = Tk_Width(w); + areawin->height = Tk_Height(w); +#else + XtSetArg(wargs[0], XtNwidth, &areawin->width); + XtSetArg(wargs[1], XtNheight, &areawin->height); + XtGetValues(areawin->area, wargs, 2); +#endif + + if (areawin->width != savewidth || areawin->height != saveheight) { + + int maxwidth = 0, maxheight = 0; + for (thiswin = xobjs.windowlist; thiswin != NULL; thiswin = thiswin->next) { + if (thiswin->width > maxwidth) maxwidth = thiswin->width; + if (thiswin->height > maxheight) maxheight = thiswin->height; + } +#ifdef DOUBLEBUFFER + if (dbuf != (Pixmap)NULL) XFreePixmap(dpy, dbuf); + dbuf = XCreatePixmap(dpy, areawin->window, maxwidth, maxheight, + DefaultDepthOfScreen(xcScreen(w))); +#endif + if (areawin->clipmask != (Pixmap)NULL) XFreePixmap(dpy, areawin->clipmask); + areawin->clipmask = XCreatePixmap(dpy, areawin->window, + maxwidth, maxheight, 1); + + if (areawin->pbuf != (Pixmap)NULL) { + XFreePixmap(dpy, areawin->pbuf); + areawin->pbuf = XCreatePixmap(dpy, areawin->window, + maxwidth, maxheight, 1); + } + + if (areawin->cmgc != (GC)NULL) XFreeGC(dpy, areawin->cmgc); + values.foreground = 0; + values.background = 0; + areawin->cmgc = XCreateGC(dpy, areawin->clipmask, + GCForeground | GCBackground, &values); + + reset_gs(); + + /* Re-compose the directores to match the new dimensions */ + composelib(LIBLIB); + composelib(PAGELIB); + + /* Re-center image in resized window */ + zoomview(NULL, NULL, NULL); + } + + /* Flush all expose events from the buffer */ + + while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard) == True); + } +} + +/*--------------------------------------*/ +/* Draw the primary graphics window */ +/*--------------------------------------*/ + +void drawwindow(xcWidget w, caddr_t clientdata, caddr_t calldata) +{ + Window win, tmpwin; + float x, y, spc, spc2, i, j, fpart; + /*short *selectobj; (jdk) */ + XPoint originpt; + XEvent discard; +#ifdef OPENGL + GLsizei width, height; +#endif + + if (!xcIsRealized(areawin->area)) return; + + newmatrix(); + +#ifdef OPENGL + glXMakeCurrent(dpy, (GLXDrawable)areawin->window, grXcontext); + glDrawBuffer(GL_BACK); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + width = (GLsizei)Tk_Width(areawin->area); + height = (GLsizei)Tk_Height(areawin->area); + + glViewport((GLsizei)0, (GLsizei)0, width, height); + + /* Need to twiddle with these? */ + glScalef(1.0 / (float)(width >> 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); + glEnable(GL_POLYGON_SMOOTH); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + XcSetFunction(GXcopy); + +#else + + XcSetFunction(GXcopy); + +#ifdef DOUBLEBUFFER + tmpwin = areawin->window; + win = (Window)dbuf; + areawin->window = win; + if (xobjs.pagelist[areawin->page]->background.name == (char *)NULL + || (copybackground() < 0)) { + XSetForeground(dpy, areawin->gc, BACKGROUND); + XFillRectangle(dpy, win, areawin->gc, 0, 0, areawin->width, + areawin->height); + } +#else + + if (xobjs.pagelist[areawin->page]->background.name != (char *)NULL) + copybackground(); + else + XClearWindow(dpy, areawin->window); +#endif + +#endif /* OPENGL */ + + win = areawin->window; + + SetThinLineAttributes(dpy, areawin->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 + && eventmode != CATMOVE_MODE && eventmode != CATTEXT_MODE) { + + float major_snapspace, spc3; + + spc = xobjs.pagelist[areawin->page]->gridspace * areawin->vscale; + if (areawin->gridon && spc > 8) { + fpart = (float)(-areawin->pcorner.x) + / xobjs.pagelist[areawin->page]->gridspace; + x = xobjs.pagelist[areawin->page]->gridspace * + (fpart - (float)((int)fpart)) * areawin->vscale; + fpart = (float)(-areawin->pcorner.y) + / xobjs.pagelist[areawin->page]->gridspace; + y = xobjs.pagelist[areawin->page]->gridspace * + (fpart - (float)((int)fpart)) * areawin->vscale; + + SetForeground(dpy, areawin->gc, GRIDCOLOR); + for (i = x; i < (float)areawin->width; i += spc) + DrawLine (dpy, win, areawin->gc, (int)(i + 0.5), + 0, (int)(i + 0.5), areawin->height); + for (j = (float)areawin->height - y; j > 0; j -= spc) + DrawLine (dpy, win, areawin->gc, 0, (int)(j - 0.5), + areawin->width, (int)(j - 0.5)); + }; + if (areawin->axeson) { + XPoint zeropt; + zeropt.x = zeropt.y = 0; + SetForeground(dpy, areawin->gc, AXESCOLOR); + user_to_window(zeropt, &originpt); + DrawLine (dpy, win, areawin->gc, originpt.x, 0, + originpt.x, areawin->height); + DrawLine (dpy, win, areawin->gc, 0, originpt.y, + areawin->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[areawin->page]->snapspace * areawin->vscale; + if (areawin->snapto && spc2 > 8) { + float x2, y2; + + fpart = (float)(-areawin->pcorner.x) + / xobjs.pagelist[areawin->page]->snapspace; + x2 = xobjs.pagelist[areawin->page]->snapspace * + (fpart - (float)((int)fpart)) * areawin->vscale; + fpart = (float)(-areawin->pcorner.y) + / xobjs.pagelist[areawin->page]->snapspace; + y2 = xobjs.pagelist[areawin->page]->snapspace * + (fpart - (float)((int)fpart)) * areawin->vscale; + +#if defined(TCL_WRAPPER) && defined(XC_WIN32) && defined(DOUBLEBUFFER) + { + HDC hdc = CreateCompatibleDC(NULL); + SelectObject(hdc, Tk_GetHWND(win)); +#endif + SetForeground(dpy, areawin->gc, SNAPCOLOR); + for (i = x2; i < areawin->width; i += spc2) + for (j = areawin->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), areawin->gc->foreground); +#endif + DrawPoint (dpy, win, areawin->gc, (int)(i + 0.5), + (int)(j - 0.5)); +#if defined(TCL_WRAPPER) && defined(XC_WIN32) && defined(DOUBLEBUFFER) + DeleteDC(hdc); + } +#endif + }; + + /* Draw major snap points (code contributed by John Barry) */ + + major_snapspace = xobjs.pagelist[areawin->page]->gridspace * 20; + spc3 = major_snapspace * areawin->vscale; + if (spc > 4) { + fpart = (float)(-areawin->pcorner.x) / major_snapspace; + x = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale; + fpart = (float)(-areawin->pcorner.y) / major_snapspace; + y = major_snapspace * (fpart - (float)((int)fpart)) * areawin->vscale; + + SetForeground(dpy, areawin->gc, GRIDCOLOR); + for (i = x; i < (float)areawin->width; i += spc3) { + for (j = (float)areawin->height - y; j > 0; j -= spc3) { + XDrawArc(dpy, win, areawin->gc, (int)(i + 0.5) - 1, + (int)(j - 0.5) - 1, 2, 2, 0, 360*64); + } + } + } + + SetBackground(dpy, areawin->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 (areawin->editinplace == True) { + if (areawin->stack != NULL) { + pushlistptr lastlist = NULL, 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(&areawin->stack, areawin->topinstance, NULL); + + thislist = areawin->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, areawin->gc, OFFBUTTONCOLOR); + UDrawObject(lastlist->thisinst, SINGLE, DOFORALL, + xobjs.pagelist[areawin->page]->wirewidth, &stack); + /* This shouldn't happen, but just in case. . . */ + if (stack) free_stack(&stack); + } + + pop_stack(&areawin->stack); /* restore the original stack state */ + UPopCTM(); /* restore the original matrix state */ + } + } + } + + /* draw all of the elements on the screen */ + + SetForeground(dpy, areawin->gc, FOREGROUND); + + /* Initialize hierstack */ + if (areawin->hierstack) free_stack(&areawin->hierstack); + UDrawObject(areawin->topinstance, TOPLEVEL, FOREGROUND, + xobjs.pagelist[areawin->page]->wirewidth, &areawin->hierstack); + if (areawin->hierstack) free_stack(&areawin->hierstack); + + /* draw the highlighted netlist, if any */ + if (checkvalid(topobject) != -1) + if (topobject->highlight.netlist != NULL) + highlightnetlist(topobject, areawin->topinstance, 1); + + /* Sanity check---specifically to track down an error */ + if ((areawin->selects == 1) && *(areawin->selectlist) >= topobject->parts) { + Wprintf("Internal error!"); + areawin->selects = 0; + unselect_all(); + } + + /* draw selected elements in the SELECTION color */ + /* special case---single label partial text selected */ + + if ((areawin->selects == 1) && SELECTTYPE(areawin->selectlist) == LABEL + && areawin->textend > 0 && areawin->textpos > areawin->textend) { + labelptr drawlabel = SELTOLABEL(areawin->selectlist); + UDrawString(drawlabel, DOSUBSTRING, areawin->topinstance); + } + else { + if (eventmode == NORMAL_MODE || eventmode == MOVE_MODE || eventmode == COPY_MODE + || eventmode == SELAREA_MODE || eventmode == CATALOG_MODE) + draw_all_selected(); + } + + /* fast copy of buffer */ + +#ifdef OPENGL + glXSwapBuffers(dpy, (GLXDrawable)areawin->window); + + /* Draw interactive elements into the front buffer */ + glDrawBuffer(GL_FRONT); + +#else +#ifdef DOUBLEBUFFER + areawin->window = tmpwin; + SetFunction(dpy, areawin->gc, GXcopy); + XCopyArea(dpy, dbuf, areawin->window, areawin->gc, 0, 0, areawin->width, + areawin->height, 0, 0); +#endif +#endif /* OPENGL */ + + /* draw pending elements, if any */ + + if (eventmode != NORMAL_MODE) { + SetFunction(dpy, areawin->gc, GXcopy); + if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE || + eventmode == CATTEXT_MODE) { + labelptr newlabel = TOLABEL(EDITPART); + UDrawTLine(newlabel); + } + else if (eventmode == SELAREA_MODE) { + UDrawBox(areawin->origin, areawin->save); + } + else if (eventmode == RESCALE_MODE) { + UDrawRescaleBox(&areawin->save); + } + else if (eventmode == BOX_MODE || eventmode == WIRE_MODE) { + SetForeground(dpy, areawin->gc, BACKGROUND); + UDrawPolygon(TOPOLY(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + XcSetXORFg(areawin->color, BACKGROUND); + XcSetFunction(GXxor); + UDrawPolygon(TOPOLY(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + } + else if (eventmode == ARC_MODE) { + SetForeground(dpy, areawin->gc, BACKGROUND); + UDrawArc(TOARC(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + XcSetXORFg(areawin->color, BACKGROUND); + XcSetFunction(GXxor); + UDrawArc(TOARC(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + } + else if (eventmode == SPLINE_MODE) { + SetForeground(dpy, areawin->gc, BACKGROUND); + UDrawSpline(TOSPLINE(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + XcSetXORFg(areawin->color, BACKGROUND); + XcSetFunction(GXxor); + UDrawSpline(TOSPLINE(ENDPART), xobjs.pagelist[areawin->page]->wirewidth); + } + else if (eventmode == EPOLY_MODE || eventmode == EARC_MODE || + eventmode == ESPLINE_MODE || eventmode == EPATH_MODE) { + XcSetXORFg(areawin->color, BACKGROUND); + XcSetFunction(GXxor); + } + } + + /* flush out multiple expose/resize events from the event queue */ + + while (XCheckWindowEvent(dpy, areawin->window, ExposureMask, &discard) == True); + + /* end by restoring graphics state */ + + SetForeground(dpy, areawin->gc, areawin->gccolor); + SetFunction(dpy, areawin->gc, areawin->gctype); +} + +/*----------------------------------------------------------------------*/ +/* Draw the current window (areawin). Check if other windows contain */ +/* the same object or one of its ancestors. If so, redraw them, too. */ +/*----------------------------------------------------------------------*/ + +void drawarea(xcWidget w, caddr_t clientdata, caddr_t calldata) +{ + XCWindowDataPtr thiswin, focuswin; + + if (xobjs.suspend >= 0) { + if (xobjs.suspend == 0) + xobjs.suspend = 1; /* Mark that a refresh is pending */ + return; + } + + focuswin = areawin; + + for (thiswin = xobjs.windowlist; thiswin != NULL; thiswin = thiswin->next) { + if (thiswin == focuswin) continue; + + /* Note: need to check ancestry here, not just blindly redraw */ + /* all the windows all the time. */ + areawin = thiswin; + drawwindow(NULL, NULL, NULL); + } + areawin = focuswin; + drawwindow(w, clientdata, calldata); +} + +/*-------------------------------------------------------------------------*/ |