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 /selection.c |
Import Upstream version 3.8.78.dfsg
Diffstat (limited to 'selection.c')
-rw-r--r-- | selection.c | 1761 |
1 files changed, 1761 insertions, 0 deletions
diff --git a/selection.c b/selection.c new file mode 100644 index 0000000..e500f7b --- /dev/null +++ b/selection.c @@ -0,0 +1,1761 @@ +/*-------------------------------------------------------------------------*/ +/* selection.c --- xcircuit routines handling element selection etc. */ +/* 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> + +#ifndef XC_WIN32 +#include <X11/Intrinsic.h> +#include <X11/StringDefs.h> +#endif + +#ifdef TCL_WRAPPER +#include <tk.h> +#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 Display *dpy; +extern int *appcolors; +extern Cursor appcursors[NUM_CURSORS]; +extern XCWindowData *areawin; +extern Globaldata xobjs; +extern char _STR[150]; + +#ifdef TCL_WRAPPER +extern Tcl_Interp *xcinterp; +#endif + +/*----------------------------------------------------------------------*/ +/* Prevent a list of elements from being selected. */ +/*----------------------------------------------------------------------*/ + +void disable_selects(objectptr thisobject, short *selectlist, int selects) +{ + genericptr genptr; + short *i; + + for (i = selectlist; i < selectlist + selects; i++) { + genptr = *(thisobject->plist + *i); + genptr->type |= SELECT_HIDE; + } +} + +/*----------------------------------------------------------------------*/ +/* Allow a list of elements to be selected, if they were disabled using */ +/* the disable_selects() routine. */ +/*----------------------------------------------------------------------*/ + +void enable_selects(objectptr thisobject, short *selectlist, int selects) +{ + genericptr genptr; + short *i; + + for (i = selectlist; i < selectlist + selects; i++) { + genptr = *(thisobject->plist + *i); + genptr->type &= ~(SELECT_HIDE); + } +} + +/*----------------------------------------------------------------------*/ +/* Change filter to determine what types can be selected */ +/*----------------------------------------------------------------------*/ + +void selectfilter(xcWidget w, pointertype value, caddr_t calldata) +{ + short bitwise = (short)value; + Boolean bval = (areawin->filter & bitwise) ? True : False; + + if (bval) + areawin->filter &= ~bitwise; + else + areawin->filter |= bitwise; + +#ifndef TCL_WRAPPER + toggle(w, (pointertype)&bval, calldata); +#endif +} + +/*----------------------------------------------------------------------*/ +/* Look at select stack to see if there are any selects; call select, */ +/* if not. If draw_selected is True, then the selected items are drawn */ +/* in the select color. Otherwise, they are not redrawn. */ +/*----------------------------------------------------------------------*/ + +Boolean checkselect_draw(short value, Boolean draw_selected) +{ + short *check, savemode; + + value &= areawin->filter; /* apply the selection filter */ + + if (areawin->selects == 0) { + savemode = eventmode; + if (!draw_selected) eventmode = PENDING_MODE; + select_element(value); + eventmode = savemode; + } + if (areawin->selects == 0) return False; + for (check = areawin->selectlist; check < areawin->selectlist + + areawin->selects; check++) + if (SELECTTYPE(check) & value) break; + if (check == areawin->selectlist + areawin->selects) return False; + else return True; +} + +/*----------------------------------------------------------------------------*/ +/* Look at select stack to see if there are any selects; call select, if not. */ +/*----------------------------------------------------------------------------*/ + +Boolean checkselect(short value) +{ + return checkselect_draw(value, False); +} + +/*--------------------------------------------------------------*/ +/* 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); + + // Note: Setting areawin->clipped to -1 prevents the clipmask from being + // applied to elements, + areawin->clipped = -1; + + switch (ELEMENTTYPE(*(curobj->plist + instance))) { + case ARC: + UDrawArc((arcptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth); + break; + case POLYGON: + UDrawPolygon((polyptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth); + break; + case SPLINE: + UDrawSpline((splineptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth); + break; + case PATH: + UDrawPath((pathptr)elementptr, xobjs.pagelist[areawin->page]->wirewidth); + break; + case LABEL: + UDrawString((labelptr)elementptr, mode, curinst); + break; + case OBJINST: + UDrawObject((objinstptr)elementptr, SINGLE, mode, + xobjs.pagelist[areawin->page]->wirewidth, NULL); + break; + case GRAPHIC: + UDrawGraphic((graphicptr)elementptr); + break; + } + areawin->clipped = 0; +} + +/*-------------------------------------------------*/ +/* Draw a selected item, including selection color */ +/*-------------------------------------------------*/ + +void gendrawselected(short *newselect, objectptr curobj, objinstptr curinst) +{ + /* Don't draw selection color when selecting for edit */ + if (eventmode == PENDING_MODE) return; + + if (*newselect >= curobj->parts) return; // Safety check + + XcSetFunction(GXcopy); + XcSetForeground(BACKGROUND); + geneasydraw(*newselect, DOFORALL, curobj, curinst); + indicateparams(*(curobj->plist + *newselect)); + + XcSetFunction(GXxor); + XcSetForeground(SELECTCOLOR ^ BACKGROUND); + geneasydraw(*newselect, DOFORALL, curobj, curinst); + + SetForeground(dpy, areawin->gc, AUXCOLOR ^ BACKGROUND); + indicateparams(*(curobj->plist + *newselect)); + + SetForeground(dpy, areawin->gc, areawin->gccolor); + SetFunction(dpy, areawin->gc, areawin->gctype); +} + +/*---------------------------------------------------*/ +/* Allocate or reallocate memory for a new selection */ +/*---------------------------------------------------*/ + +short *allocselect() +{ + short *newselect; + + if (areawin->selects == 0) + areawin->selectlist = (short *) malloc(sizeof(short)); + else + areawin->selectlist = (short *) realloc(areawin->selectlist, + (areawin->selects + 1) * sizeof(short)); + + newselect = areawin->selectlist + areawin->selects; + areawin->selects++; + + return newselect; +} + +/*-------------------------------------------------*/ +/* Set Options menu according to 1st selection */ +/*-------------------------------------------------*/ + +void setoptionmenu() +{ + short *mselect; + labelptr mlabel; + + if (areawin->selects == 0) { + setallstylemarks(areawin->style); + setcolormark(areawin->color); + setdefaultfontmarks(); + setparammarks(NULL); + return; + } + + for (mselect = areawin->selectlist; mselect < areawin->selectlist + + areawin->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 / (areawin->vscale + 0.05); + long sqrwirelim = (int)(wirelim * wirelim); + + long newdist; + Boolean selected = False; + + class &= areawin->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], &areawin->save); + if (newdist <= sqrwirelim) break; + } + if ((!(TOARC(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) + newdist = finddist(&nearpt[0], &nearpt[2], &areawin->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]), + &areawin->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], &areawin->save); + if (newdist <= sqrwirelim) break; + } + if (newdist > sqrwirelim) { + newdist = finddist(&nearpt[0], &(TOSPLINE(curgen)->ctrl[3]), + &areawin->save); + if ((!(TOSPLINE(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) + newdist = finddist(&(TOSPLINE(curgen)->ctrl[0]), + &(TOSPLINE(curgen)->ctrl[3]), &areawin->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, &areawin->save); + if (newdist <= sqrwirelim) break; + } + if ((!(TOPOLY(curgen)->style & UNCLOSED)) && (newdist > sqrwirelim)) + newdist = finddist(currentpt, TOPOLY(curgen)->points, + &areawin->save); + + if (newdist <= sqrwirelim) selected = True; + } + return selected; +} + +/*------------------------------------------------------*/ +/* Check to see if any selection has registered cycles */ +/*------------------------------------------------------*/ + +Boolean checkforcycles(short *selectlist, int selects) +{ + genericptr pgen; + pointselect *cycptr; + short *ssel; + + for (ssel = selectlist; ssel < selectlist + selects; ssel++) { + pgen = SELTOGENERIC(ssel); + switch(pgen->type) { + case POLYGON: + cycptr = ((polyptr)pgen)->cycle; + break; + case ARC: + cycptr = ((arcptr)pgen)->cycle; + break; + case SPLINE: + cycptr = ((splineptr)pgen)->cycle; + break; + case LABEL: + cycptr = ((labelptr)pgen)->cycle; + break; + } + if (cycptr != NULL) + if (cycptr->number != -1) + return True; + } + return False; +} + +/*--------------------------------------------------------------*/ +/* Copy a cycle selection list from one element to another */ +/*--------------------------------------------------------------*/ + +void copycycles(pointselect **new, pointselect **old) +{ + pointselect *pptr; + short cycles = 0; + + if (*old == NULL) { + *new = NULL; + return; + } + + for (pptr = *old; !(pptr->flags & LASTENTRY); pptr++, cycles++); + cycles += 2; + *new = (pointselect *)malloc(cycles * sizeof(pointselect)); + memcpy(*new, *old, cycles * sizeof(pointselect)); +} + +/*--------------------------------------------------------------*/ +/* Create a selection record of selected points in an element. */ +/* If a record already exists, and "cycle" is not already in */ +/* the list, add it. */ +/* "flags" may be set to EDITX or EDITY. If "flags" is zero, */ +/* then flags = EDITX | EDITY is assumed. */ +/*--------------------------------------------------------------*/ + +pointselect *addcycle(genericptr *pgen, short cycle, u_char flags) +{ + polyptr ppoly; + arcptr parc; + splineptr pspline; + labelptr plabel; + pointselect *pptr, **cycptr; + short cycles = 0; + + switch((*pgen)->type) { + case POLYGON: + ppoly = TOPOLY(pgen); + cycptr = &ppoly->cycle; + break; + case ARC: + parc = TOARC(pgen); + cycptr = &parc->cycle; + break; + case SPLINE: + pspline = TOSPLINE(pgen); + cycptr = &pspline->cycle; + break; + case LABEL: + plabel = TOLABEL(pgen); + cycptr = &plabel->cycle; + break; + } + + switch((*pgen)->type) { + case POLYGON: + case ARC: + case SPLINE: + case LABEL: // To-do: Handle labels separately + + if (*cycptr == NULL) { + *cycptr = (pointselect *)malloc(sizeof(pointselect)); + pptr = *cycptr; + pptr->number = cycle; + pptr->flags = (flags == 0) ? EDITX | EDITY : flags; + pptr->flags |= LASTENTRY; + } + else { + for (pptr = *cycptr; !(pptr->flags & LASTENTRY); pptr++, cycles++) { + if (pptr->number == cycle) + break; + pptr->flags &= ~LASTENTRY; + } + if (pptr->number != cycle) { + pptr->flags &= ~LASTENTRY; + *cycptr = (pointselect *)realloc(*cycptr, + (cycles + 2) * sizeof(pointselect)); + pptr = *cycptr + cycles + 1; + pptr->number = cycle; + pptr->flags = (flags == 0) ? EDITX | EDITY : flags; + pptr->flags |= LASTENTRY; + } + else { + pptr->flags |= (flags == 0) ? EDITX | EDITY : flags; + } + } + break; + } + return pptr; +} + +/*--------------------------------------------------------------*/ +/* If we edit the position of a control point, and the global */ +/* pathedit mode is set to TANGENTS, then we track the angle of */ +/* the adjoining curve, if there is one, by settings its cycle */ +/* flags to ANTIXY. */ +/*--------------------------------------------------------------*/ + +void addanticycle(pathptr thispath, splineptr thisspline, short cycle) +{ + genericptr *ggen, *rgen; + splineptr otherspline; + + if (areawin->pathedit == TANGENTS) { + for (ggen = thispath->plist; ggen < thispath->plist + thispath->parts; + ggen++) + if (*ggen == (genericptr)thisspline) break; + + if (*ggen != (genericptr)thisspline) return; /* Something went wrong */ + + if (cycle == 1) { + if (ggen > thispath->plist) { + if (ELEMENTTYPE(*(ggen - 1)) == SPLINE) { + addcycle(ggen - 1, 2, ANTIXY); + } + } + else if (!(thispath->style & UNCLOSED)) { + rgen = thispath->plist + thispath->parts - 1; + if (ELEMENTTYPE(*rgen) == SPLINE) { + otherspline = TOSPLINE(rgen); + if (thisspline->ctrl[0].x == otherspline->ctrl[3].x && + thisspline->ctrl[0].y == otherspline->ctrl[3].y) + addcycle(rgen, 2, ANTIXY); + } + } + } + else if (cycle == 2) { /* cycle should be only 1 or 2 */ + if (ggen < thispath->plist + thispath->parts - 1) { + if (ELEMENTTYPE(*(ggen + 1)) == SPLINE) { + addcycle(ggen + 1, 1, ANTIXY); + } + } + else if (!(thispath->style & UNCLOSED)) { + rgen = thispath->plist; + if (ELEMENTTYPE(*rgen) == SPLINE) { + otherspline = TOSPLINE(rgen); + if (thisspline->ctrl[3].x == otherspline->ctrl[0].x && + thisspline->ctrl[3].y == otherspline->ctrl[0].y) + addcycle(rgen, 1, ANTIXY); + } + } + } + } +} + +/*--------------------------------------------------------------*/ +/* Find the cycle numbered "cycle", and mark it as the */ +/* reference point. */ +/*--------------------------------------------------------------*/ + +void makerefcycle(pointselect *cycptr, short cycle) +{ + pointselect *pptr, *sptr; + + for (pptr = cycptr;; pptr++) { + if (pptr->flags & REFERENCE) { + pptr->flags &= ~REFERENCE; + break; + } + if (pptr->flags & LASTENTRY) break; + } + + for (sptr = cycptr;; sptr++) { + if (sptr->number == cycle) { + sptr->flags |= REFERENCE; + break; + } + if (sptr->flags & LASTENTRY) break; + } + + /* If something went wrong, revert to the original reference */ + + if (!(sptr->flags & REFERENCE)) { + pptr->flags |= REFERENCE; + } +} + +/* Original routine, used 1st entry as reference (deprecated) */ + +void makefirstcycle(pointselect *cycptr, short cycle) +{ + pointselect *pptr, tmpp; + + for (pptr = cycptr;; pptr++) { + if (pptr->number == cycle) { + /* swap with first entry */ + tmpp = *cycptr; + *cycptr = *pptr; + *pptr = tmpp; + if (cycptr->flags & LASTENTRY) { + cycptr->flags &= ~LASTENTRY; + pptr->flags |= LASTENTRY; + } + return; + } + if (pptr->flags & LASTENTRY) break; + } +} + +/*------------------------------------------------------------------------------*/ +/* Advance a cycle (point) selection from value "cycle" to value "newvalue" */ +/* If "newvalue" is < 0 then remove the cycle. */ +/* */ +/* If there is only one cycle point on the element, then advance its point */ +/* number. If there are multiple points on the element, then change the */ +/* reference point by moving the last item in the list to the front. */ +/*------------------------------------------------------------------------------*/ + +void advancecycle(genericptr *pgen, short newvalue) +{ + polyptr ppoly; + arcptr parc; + splineptr pspline; + labelptr plabel; + pointselect *pptr, *endptr, *fcycle, **cycptr, tmpcyc; + short cycles = 0; + + if (newvalue < 0) { + removecycle(pgen); + return; + } + + switch((*pgen)->type) { + case POLYGON: + ppoly = TOPOLY(pgen); + cycptr = &ppoly->cycle; + break; + case ARC: + parc = TOARC(pgen); + cycptr = &parc->cycle; + break; + case SPLINE: + pspline = TOSPLINE(pgen); + cycptr = &pspline->cycle; + break; + case LABEL: + plabel = TOLABEL(pgen); + cycptr = &plabel->cycle; + break; + } + if (*cycptr == NULL) return; + + /* Remove any cycles that have only X or Y flags set. */ + /* "Remove" them by shuffling them to the end of the list, */ + /* and marking the one in front as the last entry. */ + + for (endptr = *cycptr; !(endptr->flags & LASTENTRY); endptr++); + pptr = *cycptr; + while (pptr < endptr) { + if ((pptr->flags & (EDITX | EDITY)) != (EDITX | EDITY)) { + tmpcyc = *endptr; + *endptr = *pptr; + *pptr = tmpcyc; + pptr->flags &= ~LASTENTRY; + pptr->number = -1; + endptr--; + endptr->flags |= LASTENTRY; + } + else + pptr++; + } + + if (pptr->flags & LASTENTRY) { + if ((pptr->flags & (EDITX | EDITY)) != (EDITX | EDITY)) { + pptr->flags &= ~LASTENTRY; + pptr->number = -1; + endptr--; + endptr->flags |= LASTENTRY; + } + } + + /* Now advance the cycle */ + + pptr = *cycptr; + if (pptr->flags & LASTENTRY) { + pptr->number = newvalue; + } + else { + fcycle = *cycptr; + for (pptr = fcycle + 1;; pptr++) { + if (pptr->flags & (EDITX | EDITY)) + fcycle = pptr; + if (pptr->flags & LASTENTRY) break; + } + makerefcycle(*cycptr, fcycle->number); + } +} + +/*--------------------------------------*/ +/* Remove a cycle (point) selection */ +/*--------------------------------------*/ + +void removecycle(genericptr *pgen) +{ + polyptr ppoly; + pathptr ppath; + arcptr parc; + splineptr pspline; + labelptr plabel; + pointselect *pptr, **cycptr = NULL; + genericptr *pathgen; + + switch((*pgen)->type) { + case POLYGON: + ppoly = TOPOLY(pgen); + cycptr = &ppoly->cycle; + break; + case ARC: + parc = TOARC(pgen); + cycptr = &parc->cycle; + break; + case SPLINE: + pspline = TOSPLINE(pgen); + cycptr = &pspline->cycle; + break; + case LABEL: + plabel = TOLABEL(pgen); + cycptr = &plabel->cycle; + break; + case PATH: + ppath = TOPATH(pgen); + for (pathgen = ppath->plist; pathgen < ppath->plist + ppath->parts; + pathgen++) + removecycle(pathgen); + break; + } + if (cycptr == NULL) return; + if (*cycptr == NULL) return; + free(*cycptr); + *cycptr = NULL; +} + +/*--------------------------------------*/ +/* Remove cycles from all parts of a */ +/* path other than the one passed */ +/*--------------------------------------*/ + +void removeothercycles(pathptr ppath, genericptr pathpart) +{ + genericptr *pathgen; + for (pathgen = ppath->plist; pathgen < ppath->plist + ppath->parts; + pathgen++) + if (*pathgen != pathpart) + removecycle(pathgen); +} + +/*--------------------------------------*/ +/* Select one of the elements on-screen */ +/*--------------------------------------*/ + +selection *genselectelement(short class, u_char mode, objectptr selobj, + objinstptr selinst) +{ + selection *rselect = NULL; + /* short *newselect; (jdk) */ + 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++) { + + 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(areawin->save.x, areawin->save.y, + newboxpts); + if (selected) areawin->textpos = areawin->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(areawin->save.x, areawin->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 */ + + for (pathp = TOPATH(curgen)->plist; pathp < TOPATH(curgen)->plist + + TOPATH(curgen)->parts; pathp++) + if (pathselect(pathp, SPLINE|ARC|POLYGON, range)) { + selected = True; + break; + } + } + + else if ((*curgen)->type == (class & OBJINST)) { + + objinstbbox(TOOBJINST(curgen), newboxpts, range); + + /* 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(areawin->save.x, areawin->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, XPoint *boxpts, Boolean is_path, short level) +{ + Boolean selected; + pointlist currentpt; + short cycle; + + switch(ELEMENTTYPE(*curgen)) { + + case(ARC): + /* check center of arcs */ + + selected = test_insideness(TOARC(curgen)->position.x, + TOARC(curgen)->position.y, boxpts); + break; + + case(POLYGON): + /* check each point of the polygons */ + + selected = False; + cycle = 0; + for (currentpt = TOPOLY(curgen)->points; currentpt < + TOPOLY(curgen)->points + TOPOLY(curgen)->number; + currentpt++, cycle++) { + if (test_insideness(currentpt->x, currentpt->y, boxpts)) { + selected = True; + if (level == 0) addcycle(curgen, cycle, 0); + } + } + break; + + case(SPLINE): + /* check each control point of the spline */ + + selected = False; + if (test_insideness(TOSPLINE(curgen)->ctrl[0].x, + TOSPLINE(curgen)->ctrl[0].y, boxpts)) { + selected = True; + if (level == 0) addcycle(curgen, 0, 0); + } + + if (test_insideness(TOSPLINE(curgen)->ctrl[3].x, + TOSPLINE(curgen)->ctrl[3].y, boxpts)) { + selected = True; + if (level == 0) addcycle(curgen, 3, 0); + } + break; + } + return selected; +} + +/*--------------------------------------------*/ +/* select all objects from a defined box area */ +/*--------------------------------------------*/ + +Boolean selectarea(objectptr selobj, XPoint *boxpts, short level) +{ + short *newselect; + genericptr *curgen, *pathgen; + Boolean selected; + stringpart *strptr; + int locpos, cx, cy, hwidth, hheight; + objinstptr curinst; + XPoint newboxpts[4]; + + if (selobj == topobject) { + areawin->textpos = areawin->textend = 0; + } + + for (curgen = selobj->plist; curgen < selobj->plist + selobj->parts; curgen++) { + + /* apply the selection filter */ + if (!((*curgen)->type & areawin->filter)) continue; + + switch(ELEMENTTYPE(*curgen)) { + case(OBJINST): + curinst = TOOBJINST(curgen); + + /* An object instance is selected if any part of it is */ + /* selected on a recursive area search. */ + + InvTransformPoints(boxpts, newboxpts, 4, curinst->position, + curinst->scale, curinst->rotation); + selected = selectarea(curinst->thisobject, newboxpts, level + 1); + break; + + case(GRAPHIC): + /* check for graphic image center point inside area box */ + selected = test_insideness(TOGRAPHIC(curgen)->position.x, + TOGRAPHIC(curgen)->position.y, boxpts); + break; + + case(LABEL): { + XPoint 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; + + /* Ignore info and pin labels that are not on the top level */ + if ((selobj != topobject) && (slab->pin != False)) break; + + /* translate select box into the coordinate system of the label */ + InvTransformPoints(boxpts, newboxpts, 4, slab->position, + slab->scale, slab->rotation); + + if (slab->pin) { + for (j = 0; j < 4; j++) + pinadjust(slab->justify, &(newboxpts[j].x), + &(newboxpts[j].y), -1); + } + + tmpext = ULength(slab, areawin->topinstance, 0, NULL); + adj.x = (slab->justify & NOTLEFT ? (slab->justify & RIGHT ? + tmpext.maxwidth : tmpext.maxwidth >> 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, areawin->topinstance); j++) { + strptr = findstringpart(j, &locpos, slab->string, areawin->topinstance); + if (locpos < 0) continue; /* only look at printable characters */ + if (strptr->type == RETURN) tmpl2 = 0; + tmpl1 = tmpl2; + tmpext = ULength(slab, areawin->topinstance, j + 1, NULL); + tmpl2 = tmpext.maxwidth; + 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; + areawin->textend = j; + if ((areawin->textend > 1) && strptr->type != TEXT_STRING) + areawin->textend--; + } + } + else { + if (!isect) { + areawin->textpos = j; + state = 2; + break; + } + } + } + if (state == 1) areawin->textpos = j; /* selection goes to end of string */ + + /* If a label happens to be empty (can happen in the case of */ + /* a label with parameters that are all empty strings), then */ + /* check if the bounding box surrounds the label position. */ + + else if (tmpext.width == 0) { + isect = test_insideness(0, 0, newboxpts); + if (isect) { + selected = True; + areawin->textend = 1; + } + } + + } 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, boxpts, True, level)) selected = True; + } + break; + + default: + selected = areaelement(curgen, boxpts, False, level); + break; + } + + /* on recursive searches, return as soon as we find something */ + + if ((selobj != topobject) && selected) return TRUE; + + /* check if this part has already been selected */ + + if (selected) + for (newselect = areawin->selectlist; newselect < + areawin->selectlist + areawin->selects; newselect++) + if (*newselect == (short)(curgen - topobject->plist)) + selected = False; + + /* add to list of selections */ + + if (selected) { + newselect = allocselect(); + *newselect = (short)(curgen - topobject->plist); + } + } + if (selobj != topobject) return FALSE; + setoptionmenu(); + + /* if none or > 1 label has been selected, cancel any textpos placement */ + + if (!checkselect(LABEL) || areawin->selects != 1 || + (areawin->selects == 1 && SELECTTYPE(areawin->selectlist) != LABEL)) { + areawin->textpos = areawin->textend = 0; + } + + /* Register the selection as an undo event */ + register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance, + areawin->selectlist, areawin->selects); + + /* Drawing of selected objects will take place when drawarea() is */ + /* executed after the button release. */ + +#ifdef TCL_WRAPPER + if (xobjs.suspend < 0) + XcInternalTagCall(xcinterp, 2, "select", "here"); +#endif + + return selected; +} + +/*------------------------*/ +/* start deselection mode */ +/*------------------------*/ + +void startdesel(xcWidget w, caddr_t clientdata, caddr_t calldata) +{ + if (eventmode == NORMAL_MODE) { + if (areawin->selects == 0) + Wprintf("Nothing to deselect!"); + else if (areawin->selects == 1) + unselect_all(); + } +} + +/*------------------------------------------------------*/ +/* Redraw all the selected objects in the select color. */ +/*------------------------------------------------------*/ + +void draw_all_selected() +{ + int j; + + if (areawin->hierstack != NULL) return; + + for (j = 0; j < areawin->selects; j++) + gendrawselected(areawin->selectlist + j, topobject, areawin->topinstance); +} + +/*---------------------------------------------------------*/ +/* Redraw all the selected objects in their normal colors. */ +/*---------------------------------------------------------*/ + +void draw_normal_selected(objectptr thisobj, objinstptr thisinst) +{ + short saveselects; + + if (areawin->selects == 0) return; + else if (areawin->hierstack != NULL) return; + + saveselects = areawin->selects; + + /* reset the graphics state */ + SetFunction(dpy, areawin->gc, GXcopy); + + areawin->selects = 0; + drawarea(NULL, NULL, NULL); + areawin->selects = saveselects; +} + +/*----------------------------------------------------------------------*/ +/* 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 (areawin->selects > 0) + free(areawin->selectlist); + areawin->selects = 0; + free_stack(&areawin->hierstack); +} + +/*--------------------------------------------------------------*/ +/* Free memory from the selection list and set menu/toolbar */ +/* items back to default values. */ +/*--------------------------------------------------------------*/ + +void clearselects_noundo() +{ + if (areawin->selects > 0) { + reset_cycles(); + freeselects(); + if (xobjs.suspend < 0) { + setallstylemarks(areawin->style); + setcolormark(areawin->color); + setdefaultfontmarks(); + setparammarks(NULL); + } + +#ifdef TCL_WRAPPER + if (xobjs.suspend < 0) + XcInternalTagCall(xcinterp, 2, "unselect", "all"); +#endif + } +} + +/*--------------------------------------------------------------*/ +/* Same as above, but registers an undo event. */ +/*--------------------------------------------------------------*/ + +void clearselects() +{ + if (areawin->selects > 0) { + register_for_undo(XCF_Select, UNDO_DONE, areawin->topinstance, + NULL, 0); + clearselects_noundo(); + } +} + +/*--------------------------------------------------------------*/ +/* Unselect all the selected elements and free memory from the */ +/* selection list. */ +/*--------------------------------------------------------------*/ + +void unselect_all() +{ + if (xobjs.suspend < 0) + draw_normal_selected(topobject, areawin->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; + 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 &= areawin->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 areawin->save into object's own coordinate system */ + savesave.x = areawin->save.x; + savesave.y = areawin->save.y; + InvTransformPoints(&areawin->save, &tmppt, 1, selinst->position, + selinst->scale, selinst->rotation); + areawin->save.x = tmppt.x; + areawin->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, + areawin->save.x, areawin->save.y); */ + + rcheck = recurselect(ALL_TYPES, recmode, &selnew); + areawin->save.x = savesave.x; + areawin->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; + areawin->origin.x = areawin->save.x; + areawin->origin.y = areawin->save.y; + UDrawBox(areawin->origin, areawin->save); + +#ifdef TCL_WRAPPER + Tk_CreateEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, (Tk_EventProc *)xctk_drag, + NULL); +#else + xcAddEventHandler(areawin->area, ButtonMotionMask | + PointerMotionMask, False, (xcEventHandler)xlib_drag, + NULL); +#endif + +} + +/*-------------------------*/ +/* Track a select area box */ +/*-------------------------*/ + +void trackselarea() +{ + XPoint newpos; + /* u_int nullui; (jdk) */ + + newpos = UGetCursorPos(); + if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return; + + UDrawBox(areawin->origin, areawin->save); + UDrawBox(areawin->origin, newpos); + + areawin->save.x = newpos.x; + areawin->save.y = newpos.y; +} + +/*----------------------*/ +/* Track a rescale box */ +/*----------------------*/ + +void trackrescale() +{ + XPoint newpos; + + newpos = UGetCursorPos(); + if (newpos.x == areawin->save.x && newpos.y == areawin->save.y) return; + + UDrawRescaleBox(&areawin->save); + UDrawRescaleBox(&newpos); + + areawin->save.x = newpos.x; + areawin->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 = areawin->save.x; + cpt.y = areawin->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; +} + +/*----------------------------------------------------------------------*/ +/* Add pin cycles connected to selected labels */ +/*----------------------------------------------------------------------*/ + +void label_connect_cycles(labelptr thislab) +{ + genericptr *pgen; + Boolean is_selected; + XPoint *testpt; + polyptr cpoly; + short *stest, cycle; + + if (thislab->pin == LOCAL || thislab->pin == GLOBAL) { + for (pgen = topobject->plist; pgen < topobject->plist + + topobject->parts; pgen++) { + /* Ignore any wires that are already selected */ + is_selected = FALSE; + for (stest = areawin->selectlist; stest < areawin->selectlist + + areawin->selects; stest++) { + if (SELTOGENERIC(stest) == *pgen) { + is_selected = TRUE; + break; + } + } + if (ELEMENTTYPE(*pgen) == POLYGON) { + cpoly = TOPOLY(pgen); + if (!is_selected) { + cycle = 0; + for (testpt = cpoly->points; testpt < cpoly->points + + cpoly->number; testpt++) { + if (testpt->x == thislab->position.x + && testpt->y == thislab->position.y) { + addcycle(pgen, cycle, 0); + break; + } + else + cycle++; + } + } + else { + /* Make sure that this polygon's cycle is not set! */ + removecycle(pgen); + } + } + } + } +} + +/*----------------------------------------------------------------------*/ +/* Add pin cycles connected to selected instances */ +/*----------------------------------------------------------------------*/ + +void inst_connect_cycles(objinstptr thisinst) +{ + genericptr *ggen, *pgen; + Boolean is_selected; + XPoint refpoint, *testpt; + labelptr clab; + polyptr cpoly; + short *stest, cycle; + objectptr thisobj = thisinst->thisobject; + + for (ggen = thisobj->plist; ggen < thisobj->plist + thisobj->parts; ggen++) { + if (ELEMENTTYPE(*ggen) == LABEL) { + clab = TOLABEL(ggen); + if (clab->pin == LOCAL || clab->pin == GLOBAL) { + ReferencePosition(thisinst, &clab->position, &refpoint); + for (pgen = topobject->plist; pgen < topobject->plist + + topobject->parts; pgen++) { + /* Ignore any wires that are already selected */ + is_selected = FALSE; + for (stest = areawin->selectlist; stest < areawin->selectlist + + areawin->selects; stest++) { + if (SELTOGENERIC(stest) == *pgen) { + is_selected = TRUE; + break; + } + } + if (ELEMENTTYPE(*pgen) == POLYGON) { + cpoly = TOPOLY(pgen); + if (!is_selected) { + cycle = 0; + for (testpt = cpoly->points; testpt < cpoly->points + + cpoly->number; testpt++) { + if (testpt->x == refpoint.x && testpt->y == refpoint.y) { + addcycle(pgen, cycle, 0); + break; + } + else + cycle++; + } + } + else { + /* Make sure that this polygon's cycle is not set! */ + removecycle(pgen); + } + } + } + } + } + } +} + +/*----------------------------------------------------------------------*/ +/* Select connected pins on all selected object instances and labels */ +/*----------------------------------------------------------------------*/ + +void select_connected_pins() +{ + short *selptr; + objinstptr selinst; + labelptr sellab; + + if (!areawin->pinattach) return; + + for (selptr = areawin->selectlist; selptr < areawin->selectlist + + areawin->selects; selptr++) { + switch (SELECTTYPE(selptr)) { + case LABEL: + sellab = SELTOLABEL(selptr); + label_connect_cycles(sellab); + break; + case OBJINST: + selinst = SELTOOBJINST(selptr); + inst_connect_cycles(selinst); + break; + } + } +} + +/*----------------------------------------------------------------------*/ +/* Reset all polygon cycles flagged during a move (polygon wires */ +/* connected to pins of an object instance). */ +/*----------------------------------------------------------------------*/ + +void reset_cycles() +{ + polyptr cpoly; + genericptr *pgen; + + for (pgen = topobject->plist; pgen < topobject->plist + + topobject->parts; pgen++) + removecycle(pgen); +} + +/*----------------------------------------------------------------------*/ +/* Recursive selection mechanism */ +/*----------------------------------------------------------------------*/ + +short *recurse_select_element(short class, u_char mode) { + pushlistptr seltop, nextptr; + selection *rselect; + short *newselect, localpick; /* *desel, (jdk) */ + static short pick = 0; + static selection *saveselect = NULL; + int i, j, k, ilast, jlast; + Boolean unselect = False; + + seltop = (pushlistptr)malloc(sizeof(pushlist)); + seltop->thisinst = areawin->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 < areawin->selects; j++) { + if (*(areawin->selectlist + j) == *(rselect->selectlist + i)) { + jlast = j; + ilast = i; + if (++k == localpick) + break; + } + } + if (j < areawin->selects) break; + } + if (ilast >= 0) { + newselect = rselect->selectlist + ilast; + SetFunction(dpy, areawin->gc, GXcopy); + XTopSetForeground(SELTOCOLOR(newselect)); + geneasydraw(*newselect, DEFAULTCOLOR, topobject, areawin->topinstance); + areawin->selects--; + for (k = jlast; k < areawin->selects; k++) + *(areawin->selectlist + k) = *(areawin->selectlist + k + 1); + + if (areawin->selects == 0) freeselects(); + + /* Register the selection as an undo event */ + register_for_undo(XCF_Select, mode, areawin->topinstance, + areawin->selectlist, areawin->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 < areawin->selects; j++) { + if (*(areawin->selectlist + j) == *(rselect->selectlist + i)) + break; + } + if (j == areawin->selects) { + ilast = i; + if (++k == localpick) + break; + } + } + + if (ilast >= 0) { + newselect = allocselect(); + *newselect = *(rselect->selectlist + ilast); + gendrawselected(newselect, topobject, areawin->topinstance); + setoptionmenu(); + u2u_snap(&areawin->save); + + /* Register the selection as an undo event */ + /* (only if selection changed) */ + + register_for_undo(XCF_Select, mode, areawin->topinstance, + areawin->selectlist, areawin->selects); + } + } + + /* Cleanup */ + + while (seltop != NULL) { + nextptr = seltop->next; + free(seltop); + seltop = nextptr; + } + + freeselection(saveselect); + saveselect = rselect; + +#ifdef TCL_WRAPPER + if (xobjs.suspend < 0) + XcInternalTagCall(xcinterp, 2, "select", "here"); +#endif + + return areawin->selectlist; +} |