/*-------------------------------------------------------------------------*/ /* selection.c --- xcircuit routines handling element selection etc. */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /* written by Tim Edwards, 8/13/93 */ /*-------------------------------------------------------------------------*/ #include #include #include #include #ifndef XC_WIN32 #include #include #endif #ifdef TCL_WRAPPER #include #else #ifndef XC_WIN32 #include "Xw/Xw.h" #endif #endif /*-------------------------------------------------------------------------*/ /* Local includes */ /*-------------------------------------------------------------------------*/ #include "xcircuit.h" #include "colordefs.h" /*----------------------------------------------------------------------*/ /* Function prototype declarations */ /*----------------------------------------------------------------------*/ #include "prototypes.h" /*----------------------------------------------------------------------*/ /* Exported Variable definitions */ /*----------------------------------------------------------------------*/ extern short eventmode; /* keep track of the mode the screen is in */ extern Display *dpy; extern int *appcolors; extern Cursor appcursors[NUM_CURSORS]; extern Clientdata areastruct; extern char _STR[150]; extern short attachto; extern short textpos, textend; /*----------------------------------------------------------------------*/ /* Change filter to determine what types can be selected */ /*----------------------------------------------------------------------*/ void selectfilter(xcWidget w, pointertype value, caddr_t calldata) { short bitwise = (short)value; Boolean bval = (areastruct.filter & bitwise) ? True : False; if (bval) areastruct.filter &= ~bitwise; else areastruct.filter |= bitwise; #ifndef TCL_WRAPPER toggle(w, &bval, calldata); #endif } /*----------------------------------------------------------------------------*/ /* Look at select stack to see if there are any selects; call select, if not. */ /*----------------------------------------------------------------------------*/ Boolean checkselect(short value) { short *check; value &= areastruct.filter; /* apply the selection filter */ if (areastruct.selects == 0) select_element(value); if (areastruct.selects == 0) return False; for (check = areastruct.selectlist; check < areastruct.selectlist + areastruct.selects; check++) if (SELECTTYPE(check) & value) break; if (check == areastruct.selectlist + areastruct.selects) return False; else return True; } /*--------------------------------------------------------------*/ /* Select list numbering revision when an element is deleted */ /* from an object. */ /*--------------------------------------------------------------*/ void reviseselect(short *slist, int selects, short *removed) { short *chkselect; for (chkselect = slist; chkselect < slist + selects; chkselect++) if (*chkselect > *removed) (*chkselect)--; } /*----------------------*/ /* Draw a selected item */ /*----------------------*/ void geneasydraw(short instance, int mode, objectptr curobj, objinstptr curinst) { genericptr elementptr = *(curobj->plist + instance); switch (ELEMENTTYPE(*(curobj->plist + instance))) { case ARC: UDrawArc((arcptr)elementptr); break; case POLYGON: UDrawPolygon((polyptr)elementptr); break; case SPLINE: UDrawSpline((splineptr)elementptr); break; case PATH: UDrawPath((pathptr)elementptr); break; case LABEL: UDrawString((labelptr)elementptr, mode, curinst); break; case OBJINST: UDrawObject((objinstptr)elementptr, SINGLE, mode, NULL); break; case GRAPHIC: UDrawGraphic((graphicptr)elementptr); break; } } /*-------------------------------------------------*/ /* Draw a selected item, including selection color */ /*-------------------------------------------------*/ void gendrawselected(short *newselect, objectptr curobj, objinstptr curinst) { SetFunction(dpy, areastruct.gc, GXcopy); SetForeground(dpy, areastruct.gc, BACKGROUND); geneasydraw(*newselect, DOFORALL, curobj, curinst); indicateparams(*(curobj->plist + *newselect)); SetFunction(dpy, areastruct.gc, GXxor); SetForeground(dpy, areastruct.gc, SELECTCOLOR ^ BACKGROUND); geneasydraw(*newselect, DOFORALL, curobj, curinst); SetForeground(dpy, areastruct.gc, AUXCOLOR ^ BACKGROUND); indicateparams(*(curobj->plist + *newselect)); } /*---------------------------------------------------*/ /* Allocate or reallocate memory for a new selection */ /*---------------------------------------------------*/ short *allocselect() { short *newselect; if (areastruct.selects == 0) areastruct.selectlist = (short *) malloc(sizeof(short)); else areastruct.selectlist = (short *) realloc(areastruct.selectlist, (areastruct.selects + 1) * sizeof(short)); newselect = areastruct.selectlist + areastruct.selects; areastruct.selects++; return newselect; } /*-------------------------------------------------*/ /* Set Options menu according to 1st selection */ /*-------------------------------------------------*/ void setoptionmenu() { short *mselect; labelptr mlabel; if (areastruct.selects == 0) { setallstylemarks(areastruct.style); setcolormark(areastruct.color); setdefaultfontmarks(); setparammarks(NULL); return; } for (mselect = areastruct.selectlist; mselect < areastruct.selectlist + areastruct.selects; mselect++) { setcolormark(SELTOCOLOR(mselect)); setparammarks(SELTOGENERIC(mselect)); switch(SELECTTYPE(mselect)) { case ARC: setallstylemarks(SELTOARC(mselect)->style); return; case POLYGON: setallstylemarks(SELTOPOLY(mselect)->style); return; case SPLINE: setallstylemarks(SELTOSPLINE(mselect)->style); return; case PATH: setallstylemarks(SELTOPATH(mselect)->style); return; case LABEL: mlabel = SELTOLABEL(mselect); setfontmarks(mlabel->string->data.font, mlabel->justify); return; } } } /*-------------------------------------*/ /* Test of point being inside of a box */ /*-------------------------------------*/ int test_insideness(int tx, int ty, XPoint *boxpoints) { int i, stval = 0; XPoint *pt1, *pt2; int stdir; for (i = 0; i < 4; i++) { pt1 = boxpoints + i; pt2 = boxpoints + ((i + 1) % 4); stdir = (pt2->x - pt1->x) * (ty - pt1->y) - (pt2->y - pt1->y) * (tx - pt1->x); stval += sign(stdir); } return (abs(stval) == 4) ? 1 : 0; } /*--------------------------------------------*/ /* Search for selection among path components */ /*--------------------------------------------*/ #define RANGE_NARROW 11.5 #define RANGE_WIDE 50 Boolean pathselect(genericptr *curgen, short class, float range) { /*----------------------------------------------------------------------*/ /* wirelim is the distance, in user-space units, at which an element is */ /* considered for selection. */ /* */ /* wirelim = A + B / (scale + C) */ /* */ /* where A = minimum possible distance (expands range at close scale) */ /* C = minimum possible scale (contract range at far scale) */ /* B makes wirelim approx. 25 at default scale of 0.5, which */ /* is an empirical result. */ /*----------------------------------------------------------------------*/ float wirelim = 2 + range / (*areastruct.vscale + 0.05); long sqrwirelim = (int)(wirelim * wirelim); long newdist; Boolean selected = False; class &= areastruct.filter; /* apply the selection filter */ if ((*curgen)->type == (class & ARC)) { /* look among the arcs */ fpointlist currentpt; XPoint nearpt[3]; nearpt[2].x = nearpt[0].x = (short)(TOARC(curgen)->points[0].x); nearpt[2].y = nearpt[0].y = (short)(TOARC(curgen)->points[0].y); for (currentpt = TOARC(curgen)->points + 1; currentpt < TOARC(curgen)->points + TOARC(curgen)->number; currentpt++) { nearpt[1].x = nearpt[0].x; nearpt[1].y = nearpt[0].y; nearpt[0].x = (short)(currentpt->x); nearpt[0].y = (short)(currentpt->y); newdist = finddist(&nearpt[0], &nearpt[1], &areastruct.save); if (newdist <= sqrwirelim) break; } if ((!(TOARC(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) newdist = finddist(&nearpt[0], &nearpt[2], &areastruct.save); if (newdist <= sqrwirelim) selected = True; } else if ((*curgen)->type == (class & SPLINE)) { /* look among the splines --- look at polygon representation */ fpointlist currentpt; XPoint nearpt[2]; nearpt[0].x = (short)(TOSPLINE(curgen)->points[0].x); nearpt[0].y = (short)(TOSPLINE(curgen)->points[0].y); newdist = finddist(&(TOSPLINE(curgen)->ctrl[0]), &(nearpt[0]), &areastruct.save); if (newdist > sqrwirelim) { for (currentpt = TOSPLINE(curgen)->points; currentpt < TOSPLINE(curgen)->points + INTSEGS; currentpt++) { nearpt[1].x = nearpt[0].x; nearpt[1].y = nearpt[0].y; nearpt[0].x = (short)(currentpt->x); nearpt[0].y = (short)(currentpt->y); newdist = finddist(&nearpt[0], &nearpt[1], &areastruct.save); if (newdist <= sqrwirelim) break; } if (newdist > sqrwirelim) { newdist = finddist(&nearpt[0], &(TOSPLINE(curgen)->ctrl[3]), &areastruct.save); if ((!(TOSPLINE(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) newdist = finddist(&(TOSPLINE(curgen)->ctrl[0]), &(TOSPLINE(curgen)->ctrl[3]), &areastruct.save); } } if (newdist <= sqrwirelim) selected = True; } else if ((*curgen)->type == (class & POLYGON)) { /* finally, look among the polygons */ pointlist currentpt; for (currentpt = TOPOLY(curgen)->points; currentpt < TOPOLY(curgen) ->points + TOPOLY(curgen)->number - 1; currentpt++) { newdist = finddist(currentpt, currentpt + 1, &areastruct.save); if (newdist <= sqrwirelim) break; } if ((!(TOPOLY(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) newdist = finddist(currentpt, TOPOLY(curgen)->points, &areastruct.save); if (newdist <= sqrwirelim) selected = True; } return selected; } /*--------------------------------------*/ /* Select one of the elements on-screen */ /*--------------------------------------*/ selection *genselectelement(short class, u_char mode, objectptr selobj, objinstptr selinst) { selection *rselect = NULL; short *newselect; genericptr *curgen; XPoint newboxpts[4]; Boolean selected; float range = RANGE_NARROW; if (mode == MODE_RECURSE_WIDE) range = RANGE_WIDE; /* Loop through all elements found underneath the cursor */ for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) { /* when selection element to attach to, don't select the element */ /* to be attached. . . */ if (attachto == 1 && eventmode != COPY_MODE && eventmode != MOVE_MODE && curgen == EDITPART) continue; selected = False; /* Check among polygons, arcs, and curves */ if (((*curgen)->type == (class & POLYGON)) || ((*curgen)->type == (class & ARC)) || ((*curgen)->type == (class & SPLINE))) { selected = pathselect(curgen, class, range); } else if ((*curgen)->type == (class & LABEL)) { /* Look among the labels */ labelptr curlab = TOLABEL(curgen); /* Don't select temporary labels from schematic capture system */ if (curlab->string->type != FONT_NAME) continue; labelbbox(curlab, newboxpts, selinst); /* Need to check for zero-size boxes or test_insideness() */ /* fails. Zero-size boxes happen when labels are parameters */ /* set to a null string. */ if ((newboxpts[0].x != newboxpts[1].x) || (newboxpts[0].y != newboxpts[1].y)) { /* check for point inside bounding box, as for objects */ selected = test_insideness(areastruct.save.x, areastruct.save.y, newboxpts); if (selected) textpos = textend = 0; } } else if ((*curgen)->type == (class & GRAPHIC)) { /* Look among the graphic images */ graphicptr curg = TOGRAPHIC(curgen); graphicbbox(curg, newboxpts); /* check for point inside bounding box, as for objects */ selected = test_insideness(areastruct.save.x, areastruct.save.y, newboxpts); } else if ((*curgen)->type == (class & PATH)) { /* Check among the paths */ genericptr *pathp; /* Accept path if any subcomponent of the path is accepted */ /* Record which part was selected in the "subeditpart" variable */ for (pathp = TOPATH(curgen)->plist; pathp < TOPATH(curgen)->plist + TOPATH(curgen)->parts; pathp++) if (pathselect(pathp, SPLINE|ARC|POLYGON, range)) { selected = True; areastruct.editsubpart = (short)(pathp - TOPATH(curgen)->plist); break; } } else if ((*curgen)->type == (class & OBJINST)) { objinstbbox(TOOBJINST(curgen), newboxpts, True); /* Look for an intersect of the boundingbox and pointer position. */ /* This is a simple matter of rotating the pointer position with */ /* respect to the origin of the bounding box segment, as if the */ /* segment were rotated to 0 degrees. The sign of the resulting */ /* point's y-position is the same for all bbox segments if the */ /* pointer position is inside the bounding box. */ selected = test_insideness(areastruct.save.x, areastruct.save.y, newboxpts); } /* Add this object to the list of things found under the cursor */ if (selected) { if (rselect == NULL) { rselect = (selection *)malloc(sizeof(selection)); rselect->selectlist = (short *)malloc(sizeof(short)); rselect->selects = 0; rselect->thisinst = selinst; rselect->next = NULL; } else { rselect->selectlist = (short *)realloc(rselect->selectlist, (rselect->selects + 1) * sizeof(short)); } *(rselect->selectlist + rselect->selects) = (short)(curgen - selobj->plist); rselect->selects++; } } return rselect; } /*----------------------------------------------------------------*/ /* select arc, curve, and polygon objects from a defined box area */ /*----------------------------------------------------------------*/ Boolean areaelement(genericptr *curgen) { Boolean selected; pointlist currentpt; switch(ELEMENTTYPE(*curgen)) { case(ARC): /* check center of arcs */ selected = (TOARC(curgen)->position.x < areastruct.save.x) && (TOARC(curgen)->position.x > areastruct.origin.x) && (TOARC(curgen)->position.y < areastruct.save.y) && (TOARC(curgen)->position.y > areastruct.origin.y); break; case(POLYGON): /* check each point of the polygons */ selected = False; for (currentpt = TOPOLY(curgen)->points; currentpt < TOPOLY(curgen)->points + TOPOLY(curgen)->number; currentpt++) { if ((currentpt->x < areastruct.save.x) && (currentpt->x > areastruct.origin.x) && (currentpt->y < areastruct.save.y) && (currentpt->y > areastruct.origin.y)) { selected = True; break; } } break; case(SPLINE): /* check each control point of the spline */ selected = ((TOSPLINE(curgen)->ctrl[0].x < areastruct.save.x) && (TOSPLINE(curgen)->ctrl[0].x > areastruct.origin.x) && (TOSPLINE(curgen)->ctrl[0].y < areastruct.save.y) && (TOSPLINE(curgen)->ctrl[0].y > areastruct.origin.y)) || ((TOSPLINE(curgen)->ctrl[3].x < areastruct.save.x) && (TOSPLINE(curgen)->ctrl[3].x > areastruct.origin.x) && (TOSPLINE(curgen)->ctrl[3].y < areastruct.save.y) && (TOSPLINE(curgen)->ctrl[3].y > areastruct.origin.y)); break; } return selected; } /*--------------------------------------------*/ /* select all objects from a defined box area */ /*--------------------------------------------*/ void selectarea() { short *newselect; genericptr *curgen, *pathgen; Boolean selected; stringpart *strptr; int locpos; /* put points of area bounding box into proper order */ if (areastruct.origin.y > areastruct.save.y) { short tmp; tmp = areastruct.origin.y; areastruct.origin.y = areastruct.save.y; areastruct.save.y = tmp; } if (areastruct.origin.x > areastruct.save.x) { short tmp; tmp = areastruct.origin.x; areastruct.origin.x = areastruct.save.x; areastruct.save.x = tmp; } textpos = textend = 0; for (curgen = topobject->plist; curgen < topobject->plist + topobject->parts; curgen++) { /* apply the selection filter */ if (!((*curgen)->type & areastruct.filter)) continue; switch(ELEMENTTYPE(*curgen)) { case(OBJINST): /* check for instance of object center point inside area box */ selected = (TOOBJINST(curgen)->position.x < areastruct.save.x) && (TOOBJINST(curgen)->position.x > areastruct.origin.x) && (TOOBJINST(curgen)->position.y < areastruct.save.y) && (TOOBJINST(curgen)->position.y > areastruct.origin.y); break; case(GRAPHIC): /* check for graphic image center point inside area box */ selected = (TOGRAPHIC(curgen)->position.x < areastruct.save.x) && (TOGRAPHIC(curgen)->position.x > areastruct.origin.x) && (TOGRAPHIC(curgen)->position.y < areastruct.save.y) && (TOGRAPHIC(curgen)->position.y > areastruct.origin.y); break; case(LABEL): { XPoint bboxpts[4], newboxpts[4], adj; labelptr slab = TOLABEL(curgen); short j, state, isect, tmpl1, tmpl2; TextExtents tmpext; selected = False; /* Ignore temporary labels created by the netlist generator */ if (slab->string->type != FONT_NAME) break; /* create a 4-point polygon out of the select box information */ bboxpts[0].x = bboxpts[3].x = areastruct.origin.x; bboxpts[0].y = bboxpts[1].y = areastruct.origin.y; bboxpts[1].x = bboxpts[2].x = areastruct.save.x; bboxpts[2].y = bboxpts[3].y = areastruct.save.y; /* translate select box into the coordinate system of the label */ InvTransformPoints(bboxpts, newboxpts, 4, slab->position, slab->scale, slab->rotation); if (slab->pin) { if (!areastruct.schemon) continue; for (j = 0; j < 4; j++) pinadjust(slab->justify, &(newboxpts[j].x), &(newboxpts[j].y), -1); } tmpext = ULength(slab->string, areastruct.topinstance, 0.0, 0, NULL); adj.x = (slab->justify & NOTLEFT ? (slab->justify & RIGHT ? tmpext.width : tmpext.width >> 1) : 0); adj.y = (slab->justify & NOTBOTTOM ? (slab->justify & TOP ? tmpext.ascent : (tmpext.ascent + tmpext.base) >> 1) : tmpext.base); /* Label selection: For each character in the label string, */ /* do an insideness test with the select box. */ state = tmpl2 = 0; for (j = 0; j < stringlength(slab->string, True, areastruct.topinstance); j++) { strptr = findstringpart(j, &locpos, slab->string, areastruct.topinstance); if (locpos < 0) continue; /* only look at printable characters */ if (strptr->type == RETURN) tmpl2 = 0; tmpl1 = tmpl2; tmpext = ULength(slab->string, areastruct.topinstance, 0.0, j + 1, NULL); tmpl2 = tmpext.width; isect = test_insideness(((tmpl1 + tmpl2) >> 1) - adj.x, (tmpext.base + (BASELINE >> 1)) - adj.y, newboxpts); /* tiny state machine */ if (state == 0) { if (isect) { state = 1; selected = True; textend = j; if ((textend > 1) && strptr->type != TEXT_STRING) textend--; } } else { if (!isect) { textpos = j; state = 2; break; } } } if (state == 1) textpos = j; /* selection goes to end of string */ } break; case(PATH): /* check position point of each subpart of the path */ selected = False; for (pathgen = TOPATH(curgen)->plist; pathgen < TOPATH(curgen)->plist + TOPATH(curgen)->parts; pathgen++) { if (areaelement(pathgen)) selected = True; } break; default: selected = areaelement(curgen); break; } /* check if this part has already been selected */ if (selected) for (newselect = areastruct.selectlist; newselect < areastruct.selectlist + areastruct.selects; newselect++) if (*newselect == (short)(curgen - topobject->plist)) selected = False; /* add to list of selections */ if (selected) { newselect = allocselect(); *newselect = (short)(curgen - topobject->plist); } } setoptionmenu(); /* if none or > 1 label has been selected, cancel any textpos placement */ if (!checkselect(LABEL) || areastruct.selects != 1 || (areastruct.selects == 1 && SELECTTYPE(areastruct.selectlist) != LABEL)) { textpos = textend = 0; } /* Register the selection as an undo event */ register_for_undo(XCF_Select, UNDO_DONE, areastruct.topinstance, areastruct.selectlist, areastruct.selects); /* Drawing of selected objects will take place when drawarea() is */ /* executed after the button release. */ } /*------------------------*/ /* start deselection mode */ /*------------------------*/ void startdesel(xcWidget w, caddr_t clientdata, caddr_t calldata) { if (eventmode == NORMAL_MODE) { if (areastruct.selects == 0) Wprintf("Nothing to deselect!"); else if (areastruct.selects == 1) unselect_all(); } } /*------------------------------------------------------*/ /* Redraw all the selected objects in the select color. */ /*------------------------------------------------------*/ void draw_all_selected() { int j; if (areastruct.hierstack != NULL) return; for (j = 0; j < areastruct.selects; j++) gendrawselected(areastruct.selectlist + j, topobject, areastruct.topinstance); } /*---------------------------------------------------------*/ /* Redraw all the selected objects in their normal colors. */ /*---------------------------------------------------------*/ void draw_normal_selected(objectptr thisobj, objinstptr thisinst) { short *lastselect; if (areastruct.selects == 0) return; else if (areastruct.hierstack != NULL) return; /* reset the graphics state */ SetFunction(dpy, areastruct.gc, GXcopy); for (lastselect = areastruct.selectlist; lastselect < areastruct.selectlist + areastruct.selects; lastselect++) { XTopSetForeground(SELTOCOLOR(lastselect)); geneasydraw(*lastselect, SELTOCOLOR(lastselect), thisobj, thisinst); } } /*----------------------------------------------------------------------*/ /* Free a selection linked-list structure */ /* (don't confuse with freeselects) */ /*----------------------------------------------------------------------*/ static void freeselection(selection *rselect) { selection *nextselect; while (rselect != NULL) { nextselect = rselect->next; free(rselect->selectlist); free(rselect); rselect = nextselect; } } /*--------------------------------------------------------------*/ /* Free memory from the previous selection list, copy the */ /* current selection list to the previous selection list, and */ /* zero out the current selection list. */ /* Normally one would use clearselects(); use freeselects() */ /* only if the menu/toolbars are going to be updated later in */ /* the call. */ /*--------------------------------------------------------------*/ void freeselects() { if (areastruct.selects > 0) free(areastruct.selectlist); areastruct.selects = 0; free_stack(&areastruct.hierstack); } /*--------------------------------------------------------------*/ /* Free memory from the selection list and set menu/toolbar */ /* items back to default values. */ /*--------------------------------------------------------------*/ void clearselects_noundo() { if (areastruct.selects > 0) { freeselects(); setallstylemarks(areastruct.style); setcolormark(areastruct.color); setdefaultfontmarks(); } } /*--------------------------------------------------------------*/ /* Same as above, but registers an undo event. */ /*--------------------------------------------------------------*/ void clearselects() { if (areastruct.selects > 0) { register_for_undo(XCF_Select, UNDO_DONE, areastruct.topinstance, NULL, 0); clearselects_noundo(); } } /*--------------------------------------------------------------*/ /* Unselect all the selected elements and free memory from the */ /* selection list. */ /*--------------------------------------------------------------*/ void unselect_all() { draw_normal_selected(topobject, areastruct.topinstance); clearselects(); } /*----------------------------------------------------------------------*/ /* Select the nearest element, searching the hierarchy if necessary. */ /* Return an pushlist pointer to a linked list containing the */ /* hierarchy of objects, with the topmost pushlist also containing a */ /* pointer to the polygon found. */ /* Allocates memory for the returned linked list which must be freed by */ /* the calling routine. */ /*----------------------------------------------------------------------*/ selection *recurselect(short class, u_char mode, pushlistptr *seltop) { selection *rselect, *rcheck, *lastselect; genericptr rgen; short i, *selectobj, numselect, origselect; short rclass; objectptr selobj; objinstptr selinst; XPoint savesave, tmppt; pushlistptr selnew; short j, unselects; u_char locmode = (mode == MODE_CONNECT) ? UNDO_DONE : mode; u_char recmode = (mode != MODE_CONNECT) ? MODE_RECURSE_WIDE : MODE_RECURSE_NARROW; if (*seltop == NULL) { Fprintf(stderr, "Error: recurselect called with NULL pushlist pointer\n"); return NULL; } selinst = (*seltop)->thisinst; selobj = selinst->thisobject; class &= areastruct.filter; /* apply the selection filter */ unselects = 0; rselect = genselectelement(class, locmode, selobj, selinst); if (rselect == NULL) return NULL; for (i = 0; i < rselect->selects; i++) { rgen = *(selobj->plist + (*(rselect->selectlist + i))); if (rgen->type == OBJINST) { selinst = TOOBJINST(selobj->plist + (*(rselect->selectlist + i))); /* Link hierarchy information to the pushlist linked list */ selnew = (pushlistptr)malloc(sizeof(pushlist)); selnew->thisinst = selinst; selnew->next = NULL; (*seltop)->next = selnew; /* Translate areastruct.save into object's own coordinate system */ savesave.x = areastruct.save.x; savesave.y = areastruct.save.y; InvTransformPoints(&areastruct.save, &tmppt, 1, selinst->position, selinst->scale, selinst->rotation); areastruct.save.x = tmppt.x; areastruct.save.y = tmppt.y; /* Fprintf(stdout, "objinst %s found in object %s; searching recursively\n", selinst->thisobject->name, selobj->name); */ /* Fprintf(stdout, "cursor position originally (%d, %d); " "in new object is (%d, %d)\n", savesave.x, savesave.y, areastruct.save.x, areastruct.save.y); */ UPushCTM(); UPreMultCTM(DCTM, selinst->position, selinst->scale, selinst->rotation); rclass = class; if ((class & (~OBJINST)) == 0) class = ALL_TYPES; rcheck = recurselect(class, recmode, &selnew); UPopCTM(); areastruct.save.x = savesave.x; areastruct.save.y = savesave.y; /* If rgen is NULL, remove selected object from the list, and */ /* remove the last entry from the pushlist stack. */ if (rcheck == NULL) { *(rselect->selectlist + i) = -1; unselects++; (*seltop)->next = NULL; if (selnew->next != NULL) Fprintf(stderr, "Error: pushstack was freed, but was not empty!\n"); free(selnew); } else { for (lastselect = rselect; lastselect->next != NULL; lastselect = lastselect->next); lastselect->next = rcheck; } } } /* Modify the selection list */ for (i = 0, j = 0; i < rselect->selects; i++) { if (*(rselect->selectlist + i) >= 0) { if (i != j) *(rselect->selectlist + j) = *(rselect->selectlist + i); j++; } } rselect->selects -= unselects; if (rselect->selects == 0) { freeselection(rselect); rselect = NULL; } return rselect; } /*----------------------------------*/ /* Start drawing a select area box. */ /*----------------------------------*/ void startselect() { eventmode = SELAREA_MODE; areastruct.origin.x = areastruct.save.x; areastruct.origin.y = areastruct.save.y; UDrawBox(areastruct.origin, areastruct.save); #ifdef TCL_WRAPPER Tk_CreateEventHandler(areastruct.area, ButtonMotionMask, (Tk_EventProc *)xctk_drag, NULL); #else xcAddEventHandler(areastruct.area, ButtonMotionMask, False, (xcEventHandler)xlib_drag, NULL); #endif } /*-------------------------*/ /* Track a select area box */ /*-------------------------*/ void trackselarea() { XPoint newpos; u_int nullui; newpos = UGetCursorPos(); if (newpos.x == areastruct.save.x && newpos.y == areastruct.save.y) return; UDrawBox(areastruct.origin, areastruct.save); UDrawBox(areastruct.origin, newpos); areastruct.save.x = newpos.x; areastruct.save.y = newpos.y; } /*----------------------*/ /* Track a rescale box */ /*----------------------*/ void trackrescale() { XPoint newpos; u_int nullui; newpos = UGetCursorPos(); if (newpos.x == areastruct.save.x && newpos.y == areastruct.save.y) return; UDrawRescaleBox(&areastruct.save); UDrawRescaleBox(&newpos); areastruct.save.x = newpos.x; areastruct.save.y = newpos.y; } /*----------------------------------------------------------------------*/ /* Polygon distance comparison function for qsort */ /*----------------------------------------------------------------------*/ int dcompare(const void *a, const void *b) { XPoint cpt; genericptr agen, bgen; short j, k, adist, bdist; cpt.x = areastruct.save.x; cpt.y = areastruct.save.y; j = *((short *)a); k = *((short *)b); agen = *(topobject->plist + j); bgen = *(topobject->plist + k); if (agen->type != POLYGON || bgen->type != POLYGON) return 0; adist = closedistance((polyptr)agen, &cpt); bdist = closedistance((polyptr)bgen, &cpt); if (adist == bdist) return 0; return (adist < bdist) ? 1 : -1; } /*----------------------------------------------------------------------*/ /* Compare two selection linked lists */ /*----------------------------------------------------------------------*/ Boolean compareselection(selection *sa, selection *sb) { int i, j, match; short n1, n2; if ((sa == NULL) || (sb == NULL)) return False; if (sa->selects != sb->selects) return False; match = 0; for (i = 0; i < sa->selects; i++) { n1 = *(sa->selectlist + i); for (j = 0; j < sb->selects; j++) { n2 = *(sb->selectlist + j); if (n1 == n2) { match++; break; } } } return (match == sa->selects) ? True : False; } /*----------------------------------------------------------------------*/ /* Recursive selection mechanism */ /*----------------------------------------------------------------------*/ short *recurse_select_element(short class, u_char mode) { pushlistptr seltop, nextptr; selection *rselect; short *newselect, *desel, localpick; static short pick = 0; static selection *saveselect = NULL; int i, j, k, ilast, jlast; Boolean unselect = False; seltop = (pushlistptr)malloc(sizeof(pushlist)); seltop->thisinst = areastruct.topinstance; seltop->next = NULL; /* Definition for unselecting an element */ if (class < 0) { unselect = True; class = -class; } rselect = recurselect(class, mode, &seltop); if (rselect) { /* Order polygons according to nearest point distance. */ qsort((void *)rselect->selectlist, (size_t)rselect->selects, sizeof(short), dcompare); if (compareselection(rselect, saveselect)) pick++; else pick = 0; localpick = pick % rselect->selects; } /* Mechanism for unselecting elements under the cursor */ /* (Unselect all picked objects) */ if (rselect && unselect) { ilast = -1; k = 0; for (i = 0; i < rselect->selects; i++) { for (j = 0; j < areastruct.selects; j++) { if (*(areastruct.selectlist + j) == *(rselect->selectlist + i)) { jlast = j; ilast = i; if (++k == localpick) break; } } if (j < areastruct.selects) break; } if (ilast >= 0) { newselect = rselect->selectlist + ilast; SetFunction(dpy, areastruct.gc, GXcopy); XTopSetForeground(SELTOCOLOR(newselect)); geneasydraw(*newselect, DEFAULTCOLOR, topobject, areastruct.topinstance); areastruct.selects--; for (k = jlast; k < areastruct.selects; k++) *(areastruct.selectlist + k) = *(areastruct.selectlist + k + 1); if (areastruct.selects == 0) freeselects(); /* Register the selection as an undo event */ register_for_undo(XCF_Select, mode, areastruct.topinstance, areastruct.selectlist, areastruct.selects); } } else if (rselect) { /* Mechanism for selecting objects: */ /* Count all elements from rselect that are part of */ /* the current selection. Pick the "pick"th item (modulo */ /* total number of items). */ ilast = -1; k = 0; for (i = 0; i < rselect->selects; i++) { for (j = 0; j < areastruct.selects; j++) { if (*(areastruct.selectlist + j) == *(rselect->selectlist + i)) break; } if (j == areastruct.selects) { ilast = i; if (++k == localpick) break; } } if (ilast >= 0) { newselect = allocselect(); *newselect = *(rselect->selectlist + ilast); gendrawselected(newselect, topobject, areastruct.topinstance); setoptionmenu(); u2u_snap(&areastruct.save); /* Register the selection as an undo event */ /* (only if selection changed) */ register_for_undo(XCF_Select, mode, areastruct.topinstance, areastruct.selectlist, areastruct.selects); } } /* Cleanup */ while (seltop != NULL) { nextptr = seltop->next; free(seltop); seltop = nextptr; } freeselection(saveselect); saveselect = rselect; return areastruct.selectlist; }