/*
 * Electric(tm) VLSI Design System
 *
 * File: usrtrack.c
 * User interface aid: cursor tracking routines
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

/*
 * the code in this module makes me quiver with trepidation whenever it
 * breaks ... smr
 */
#include "global.h"
#include "egraphics.h"
#include "usr.h"
#include "usrtrack.h"
#include "efunction.h"
#include "tecart.h"
#include "tecgen.h"
#include "sim.h"

#define	MAXTRACE 400
#define MINSLIDEDELAY 60			/* ticks between shifts of window when dragging to edge */
static INTSML us_tracelist;
static INTBIG us_tracedata[MAXTRACE];
static GRAPHICS us_highl = {LAYERH, 0, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};

static INTBIG us_dragx, us_dragy, us_dragox, us_dragoy, us_dragoffx,
	us_dragoffy, us_lastcurx, us_lastcury, us_dragpoint, us_dragjoinfactor,
	us_dragnodetotal, us_dragdescript, us_cantdrag, us_draglowval, us_draghighval;
static INTSML us_dragshown, us_dragangle, us_dragstate, us_dragfport, us_dragextra,
	us_dragnobox, us_dragstayonhigh, us_dragstill, us_dragcorner, us_dragspecial;
static INTBIG      us_arrowamount;				/* for slider tracking */
static INTBIG      us_arrowlx, us_arrowhx, us_arrowly, us_arrowhy;
static WINDOWPART *us_dragwindow;
static POLYGON    *us_dragpoly = NOPOLYGON;
static NODEPROTO  *us_dragnodeproto;
static NODEINST   *us_dragnodeinst, **us_dragnodelist;
static ARCINST    *us_dragarcinst;
static GEOM       *us_dragobject, **us_dragobjectlist;
static INTBIG      us_dragaddr, us_dragtype;
static HIGHLIGHT   us_draghigh;
static char        us_dragmessage[100];
static UINTBIG     us_beforepantime = 0;
static INTBIG      us_initthumbcoord;
static WINDOWPART  us_trackww;

/* prototypes for local routines */
void us_finddoibegin(INTSML);
void us_multidragdraw(INTBIG, INTBIG, INTBIG);
void us_invertstretch(void);
void us_wanttoinvert(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, WINDOWPART *w);
INTSML us_geominrect(GEOM *geom, INTBIG slx, INTBIG shx, INTBIG sly, INTBIG shy);
void us_drawdistance(void);

/************************* NODE TRACE *************************/

/* initialization routine when reading a cursor trace */
void us_tracebegin(void)
{
	us_tracelist = 0;
	us_highl.col = HIGHLIT;
}

/* cursor advance routine when reading a cursor trace */
INTSML us_tracedown(INTBIG x, INTBIG y)
{
	if (us_tracelist >= MAXTRACE) return(1);
	if (us_tracelist != 0)
		screendrawline(el_curwindowpart, (INTSML)us_tracedata[us_tracelist-2], (INTSML)us_tracedata[us_tracelist-1],
			(INTSML)x, (INTSML)y, &us_highl, 0);
	us_tracedata[us_tracelist++] = x;
	us_tracedata[us_tracelist++] = y;
	return(0);
}

/* termination routine when reading a cursor trace */
void us_traceup(void)
{
	REGISTER INTSML i;

	if (us_tracelist == 0)
	{
		(void)setval((INTBIG)us_aid, VAID, us_commandvarname('T'), (INTBIG)"nothing",
			VSTRING|VDONTSAVE);
		return;
	}
	(void)setval((INTBIG)us_aid, VAID, us_commandvarname('T'), (INTBIG)us_tracedata,
		(INTBIG)(VINTEGER|VISARRAY|(us_tracelist<<VLENGTHSH)|VDONTSAVE));
	us_highl.col = 0;
	for(i=2; i<us_tracelist; i += 2)
		screendrawline(el_curwindowpart, (INTSML)us_tracedata[i-2], (INTSML)us_tracedata[i-1],
			(INTSML)us_tracedata[i], (INTSML)us_tracedata[i+1], &us_highl, 0);
}

/* preinitialization routine when creating or moving a point on a node */
void us_pointinit(NODEINST *node, INTSML point)
{
	us_dragnodeinst = node;
	us_dragpoint = point;
}

/* initialization routine when creating or moving a point on a node */
void us_pointbegin(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	(void)getxy(&us_dragx, &us_dragy);
	us_dragwindow = el_curwindowpart;
}

/* initialization routine when finding and moving a point on a node */
void us_findpointbegin(void)
{
	REGISTER VARIABLE *highvar;
	HIGHLIGHT newhigh;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	if (getxy(&us_dragx, &us_dragy)) return;
	gridalign(&us_dragx, &us_dragy, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_dragx, us_dragy);
	us_dragwindow = el_curwindowpart;

	/* select the current point on the node */
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar == NOVARIABLE) return;
	(void)us_makehighlight(((char **)highvar->addr)[0], &newhigh);
	newhigh.fromport = NOPORTPROTO;
	newhigh.facet = getcurfacet();
	us_setfind(&newhigh, 1, 0, 0, 0);

	/* get the point to be moved */
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar == NOVARIABLE) return;
	(void)us_makehighlight(((char **)highvar->addr)[0], &newhigh);
	us_dragpoint = newhigh.frompoint;
}

/* cursor advance routine when creating a point on a node */
INTSML us_addpdown(INTBIG x, INTBIG y)
{
	REGISTER INTSML p, size;
	REGISTER INTBIG cx, cy;
	XARRAY trans;
	REGISTER VARIABLE *var;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the lines are already being shown, erase them */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	makerot(us_dragnodeinst, trans);
	var = gettrace(us_dragnodeinst);
	if (var == NOVARIABLE)
	{
		us_dragpoly->xv[0] = us_lastcurx;
		us_dragpoly->yv[0] = us_lastcury;
		us_dragpoly->count = 1;
		us_dragpoly->style = CROSS;
	} else
	{
		size = getlength(var) / 2;
		cx = (us_dragnodeinst->lowx + us_dragnodeinst->highx) / 2;
		cy = (us_dragnodeinst->lowy + us_dragnodeinst->highy) / 2;
		p = 0;
		if (us_dragpoint <= size)
		{
			xform(((INTBIG *)var->addr)[(us_dragpoint-1)*2] + cx,
				((INTBIG *)var->addr)[(us_dragpoint-1)*2+1] + cy,
					&us_dragpoly->xv[p], &us_dragpoly->yv[p], trans);
			p++;
		}
		us_dragpoly->xv[p] = us_lastcurx;
		us_dragpoly->yv[p] = us_lastcury;
		p++;
		if (us_dragpoint < size)
		{
			xform(((INTBIG *)var->addr)[us_dragpoint*2] + cx,
				((INTBIG *)var->addr)[us_dragpoint*2+1] + cy,
					&us_dragpoly->xv[p], &us_dragpoly->yv[p], trans);
			p++;
		}
		us_dragpoly->count = p;
		us_dragpoly->style = OPENED;
	}

	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/* cursor advance routine when moving a point on a node */
INTSML us_movepdown(INTBIG x, INTBIG y)
{
	REGISTER INTSML p, size;
	REGISTER INTBIG cx, cy;
	XARRAY trans;
	REGISTER VARIABLE *var;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the lines are already being shown, erase them */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	makerot(us_dragnodeinst, trans);
	var = gettrace(us_dragnodeinst);
	if (var == NOVARIABLE) return(0);

	size = getlength(var) / 2;
	cx = (us_dragnodeinst->lowx + us_dragnodeinst->highx) / 2;
	cy = (us_dragnodeinst->lowy + us_dragnodeinst->highy) / 2;
	p = 0;
	if (us_dragpoint > 1)
	{
		xform(((INTBIG *)var->addr)[(us_dragpoint-2)*2] + cx,
			((INTBIG *)var->addr)[(us_dragpoint-2)*2+1] + cy,
				&us_dragpoly->xv[p], &us_dragpoly->yv[p], trans);
		p++;
	}
	us_dragpoly->xv[p] = us_lastcurx;
	us_dragpoly->yv[p] = us_lastcury;
	p++;
	if (us_dragpoint < size)
	{
		xform(((INTBIG *)var->addr)[us_dragpoint*2] + cx,
			((INTBIG *)var->addr)[us_dragpoint*2+1] + cy,
				&us_dragpoly->xv[p], &us_dragpoly->yv[p], trans);
		p++;
	}
	us_dragpoly->count = p;
	us_dragpoly->style = OPENED;

	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/************************* TEXT GRAB-POINT *************************/

/* preinitialization routine when positioning text's grab point */
void us_textgrabinit(INTBIG olddescript, INTBIG xw, INTBIG yw, INTBIG xc, INTBIG yc,
	GEOM *geom)
{
	/* save text extent information */
	us_dragdescript = olddescript;
	us_dragx = xc;          us_dragy = yc;
	us_dragox = xw;         us_dragoy = yw;
	us_dragobject = geom;
}

/* initialization routine when positioning text's grab-point */
void us_textgrabbegin(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
}

/* cursor advance routine when positioning text's grab-point */
INTSML us_textgrabdown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG descript;
	REGISTER INTSML style;
	XARRAY trans;

	/* get the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* convert the cursor position into a text descriptor */
	descript = us_figuregrabpoint(us_dragdescript, us_lastcurx, us_lastcury,
		us_dragx, us_dragy, us_dragox, us_dragoy);
	descript = us_rotatedescriptI(us_dragobject, descript);

	/* convert the text descriptor into a GRAPHICS style */
	switch (descript&VTPOSITION)
	{
		case VTPOSCENT:      style = TEXTCENT;      break;
		case VTPOSBOXED:     style = TEXTBOX;       break;
		case VTPOSUP:        style = TEXTBOT;       break;
		case VTPOSDOWN:      style = TEXTTOP;       break;
		case VTPOSLEFT:      style = TEXTRIGHT;     break;
		case VTPOSRIGHT:     style = TEXTLEFT;      break;
		case VTPOSUPLEFT:    style = TEXTBOTRIGHT;  break;
		case VTPOSUPRIGHT:   style = TEXTBOTLEFT;   break;
		case VTPOSDOWNLEFT:  style = TEXTTOPRIGHT;  break;
		case VTPOSDOWNRIGHT: style = TEXTTOPLEFT;   break;
	}
	if (us_dragobject->entrytype == OBJNODEINST)
	{
		makeangle(us_dragobject->entryaddr.ni->rotation, us_dragobject->entryaddr.ni->transpose,
			trans);
		style = rotatelabel(style, trans);
	}

	/* convert the text descriptor into a POLYGON */
	us_buildtexthighpoly(us_dragobject->lowx, us_dragobject->highx,
		us_dragobject->lowy, us_dragobject->highy, us_dragx, us_dragy, us_dragox, us_dragoy,
			style, us_dragpoly);

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/************************* TEXT MOTION *************************/

/* preinitialization routine when positioning text */
void us_textmoveinit(INTBIG xc, INTBIG yc, INTBIG xw, INTBIG yw, INTBIG descript, GEOM *geom)
{
	/* save text extent information */
	us_dragx = xc;      us_dragy = yc;
	us_dragoffx = xc;   us_dragoffy = yc;
	us_dragox = xw;     us_dragoy = yw;
	us_dragdescript = descript;
	us_dragaddr = (INTBIG)geom->entryaddr.blind;
	if (geom->entrytype == OBJNODEINST) us_dragtype = VNODEINST; else
		us_dragtype = VARCINST;
}

/* initialization routine when positioning text */
void us_textmovebegin(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
}

/* cursor advance routine when positioning text */
INTSML us_textmovedown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG xcur, ycur, lambda;
	INTBIG lx, hx, ly, hy;
	REGISTER TECHNOLOGY *tech;

	/* get the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* convert the text descriptor into a GRAPHICS style */
	us_dragpoly->count = 1;
	us_dragpoly->xv[0] = us_dragpoly->yv[0] = 0;

	tech = us_getobjectinfo(us_dragaddr, us_dragtype, &lx, &hx, &ly, &hy);
	adjustdisoffset(us_dragaddr, us_dragtype, tech, us_dragpoly, us_dragdescript);

	/* clip range of text descriptor */
	lambda = tech->deflambda;
	xcur = (us_lastcurx - us_dragx) * 4 / lambda;
	us_lastcurx = us_lastcurx - us_dragx + us_dragoffx;
	if (xcur > 1023)
		us_lastcurx = 1023 * lambda / 4 + us_dragoffx; else
			if (xcur < -1023)
				us_lastcurx = -1023 * lambda / 4 + us_dragoffx;
	ycur = (us_lastcury - us_dragy) * 4 / lambda;
	us_lastcury = us_lastcury - us_dragy + us_dragoffy;
	if (ycur > 1023)
		us_lastcury = 1023 * lambda / 4 + us_dragoffy; else
			if (ycur < -1023)
				us_lastcury = -1023 * lambda / 4 + us_dragoffy;

	/* draw the descriptor */
	us_buildtexthighpoly(lx, hx, ly, hy, us_lastcurx, us_lastcury,
		us_dragox, us_dragoy, us_dragpoly->style, us_dragpoly);

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/************************* ROTATE *************************/

/* preinitialization routine when rotating a node */
void us_rotateinit(NODEINST *node)
{
	us_dragnodeinst = node;
}

/* initialization routine when rotating a node */
void us_rotatebegin(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	(void)getxy(&us_dragx, &us_dragy);
	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
	us_dragangle = figureangle((us_dragnodeinst->lowx+us_dragnodeinst->highx)/2,
		(us_dragnodeinst->lowy+us_dragnodeinst->highy)/2, us_dragx, us_dragy);
}

/* cursor advance routine when rotating the current node */
INTSML us_rotatedown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG lx, hx, ly, hy;
	REGISTER INTSML newangle, saver, savet;
	XARRAY trans;
	INTBIG xl, yl, xh, yh;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* compute new angle from node center to cursor */
	newangle = figureangle((us_dragnodeinst->lowx+us_dragnodeinst->highx)/2,
		(us_dragnodeinst->lowy+us_dragnodeinst->highy)/2, us_lastcurx, us_lastcury);

	/* compute highlighted box about node */
	nodesizeoffset(us_dragnodeinst, &xl, &yl, &xh, &yh);
	lx = us_dragnodeinst->lowx+xl;   hx = us_dragnodeinst->highx-xh;
	ly = us_dragnodeinst->lowy+yl;   hy = us_dragnodeinst->highy-yh;
	makerot(us_dragnodeinst, trans);
	maketruerectpoly(lx, hx, ly, hy, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	xformpoly(us_dragpoly, trans);

	/* rotate this box according to the cursor */
	saver = us_dragnodeinst->rotation;
	savet = us_dragnodeinst->transpose;
	us_dragnodeinst->transpose = 0;
	us_dragnodeinst->rotation = newangle-us_dragangle;
	while (us_dragnodeinst->rotation < 0) us_dragnodeinst->rotation += 3600;
	while (us_dragnodeinst->rotation > 3600) us_dragnodeinst->rotation -= 3600;
	makerot(us_dragnodeinst, trans);
	xformpoly(us_dragpoly, trans);
	us_dragnodeinst->rotation = saver;
	us_dragnodeinst->transpose = savet;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/************************* SIZE *************************/

/* preinitialization routine when changing the size of a node */
void us_sizeinit(NODEINST *node)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;
	us_dragnodeinst = node;
	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
	us_dragcorner = -2;
}

/* initialization routine when changing the size of a node */
void us_sizebegin(void)
{
	us_dragcorner = -1;
}

INTSML us_sizeterm(void)
{
	return(us_dragcorner);
}

/* preinitialization routine when changing the size of an arc */
void us_sizeainit(ARCINST *arc)
{
	us_dragarcinst = arc;
}

/* initialization routine when changing the size of an arc */
void us_sizeabegin(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
}

/* cursor advance routine when stretching the current arc */
INTSML us_sizeadown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG wid;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment/2);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* compute new size of the arcinst */
	if (us_dragarcinst->end[0].xpos == us_dragarcinst->end[1].xpos)
		wid = abs(us_lastcurx - us_dragarcinst->end[0].xpos) * 2; else
			wid = abs(us_lastcury - us_dragarcinst->end[0].ypos) * 2;
	makearcpoly(us_dragarcinst->length, wid, us_dragarcinst, us_dragpoly,CLOSED);
	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/* cursor advance routine when stretching the current node about far corner */
INTSML us_sizedown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG lx, hx, ly, hy, otherx, othery, dist, bestdist;
	INTBIG xl, xh, yl, yh, rx, ry;
	REGISTER INTSML corner;
	XARRAY trans;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	nodesizeoffset(us_dragnodeinst, &xl, &yl, &xh, &yh);
	lx = us_dragnodeinst->lowx+xl;   hx = us_dragnodeinst->highx-xh;
	ly = us_dragnodeinst->lowy+yl;   hy = us_dragnodeinst->highy-yh;
	makerot(us_dragnodeinst, trans);

	/* determine which corner is fixed by finding farthest from cursor */
	corner = us_dragcorner;
	if (corner < 0)
	{
		xform(lx, ly, &rx, &ry, trans);
		bestdist = abs(rx - us_lastcurx) + abs(ry - us_lastcury);
		corner = 1;	/* lower-left */
		xform(hx, ly, &rx, &ry, trans);
		dist = abs(rx - us_lastcurx) + abs(ry - us_lastcury);
		if (dist < bestdist)
		{
			bestdist = dist;   corner = 2;	/* lower-right */
		}
		xform(lx, hy, &rx, &ry, trans);
		dist = abs(rx - us_lastcurx) + abs(ry - us_lastcury);
		if (dist < bestdist)
		{
			bestdist = dist;   corner = 3;	/* upper-left */
		}
		xform(hx, hy, &rx, &ry, trans);
		dist = abs(rx - us_lastcurx) + abs(ry - us_lastcury);
		if (dist < bestdist) corner = 4;	/* upper-right */
		if (us_dragcorner == -1) us_dragcorner = corner;
	}

	switch (corner)
	{
		case 1:		/* lower-left */
			otherx = hx;   othery = hy;   break;
		case 2:		/* lower-right */
			otherx = lx;   othery = hy;   break;
		case 3:		/* upper-left */
			otherx = hx;   othery = ly;   break;
		case 4:		/* upper-right */
			otherx = lx;   othery = ly;   break;
	}
	xform(otherx, othery, &rx, &ry, trans);
	maketruerectpoly(mini(rx, us_lastcurx), maxi(rx, us_lastcurx),
		mini(ry, us_lastcury), maxi(ry, us_lastcury), us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/* cursor advance routine when stretching the current node about center */
INTSML us_sizecdown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG dx, dy, cx, cy;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment/2);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* compute new size of the nodeinst */
	cx = (us_dragnodeinst->lowx+us_dragnodeinst->highx) / 2;
	cy = (us_dragnodeinst->lowy+us_dragnodeinst->highy) / 2;
	dx = abs(cx - us_lastcurx);
	dy = abs(cy - us_lastcury);

	maketruerectpoly(cx-dx, cx+dx, cy-dy, cy+dy, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/************************* FIND AREA *************************/

/* pre-initialization routine when doing "find area-XXXX" */
void us_findinit(INTBIG sizex, INTBIG sizey)
{
	us_dragox = sizex;
	us_dragoy = sizey;
}

/* initialization routine when doing "find area-move" */
void us_findmbegin(void)
{
	INTBIG xcur, ycur;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	(void)getxy(&xcur, &ycur);
	maketruerectpoly(xcur, xcur+us_dragox, ycur, ycur+us_dragoy, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = xcur;   us_dragy = ycur;
	us_dragwindow = el_curwindowpart;
}

/* initialization routine when doing "find area-size" */
void us_findsbegin(void)
{
	INTBIG xcur, ycur;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	(void)getxy(&xcur, &ycur);
	maketruerectpoly(us_dragox, xcur, us_dragoy, ycur, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = xcur;   us_dragy = ycur;
	us_dragwindow = el_curwindowpart;
}

/* initialization routine when doing "find area-define" */
void us_finddbegin(void)
{
	INTBIG xcur, ycur;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	(void)getxy(&xcur, &ycur);
	us_setcursorpos(NOWINDOWFRAME, xcur, ycur);
	us_dragox = xcur;
	us_dragoy = ycur;
	maketruerectpoly(us_dragox, xcur, us_dragoy, ycur, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = xcur;   us_dragy = ycur;
	us_dragwindow = el_curwindowpart;
}

/* posttermination routine when doing "find area-define" */
void us_finddterm(INTBIG *x, INTBIG *y)
{
	*x = us_dragox;
	*y = us_dragoy;
}

/************************* FIND INTERACTIVE *************************/

void us_findiinit(INTSML findport, INTSML extrainfo, INTSML findangle, INTSML stayonhighlighted,
	INTSML findstill, INTSML findnobox, INTSML findspecial)
{
	us_dragfport = findport;
	us_dragextra = (extrainfo != 0 ? 1 : 0);
	us_dragangle = findangle;
	us_dragstill = findstill;
	us_dragnobox = findnobox;
	us_dragspecial = findspecial;
	us_dragpoint = -1;
	us_dragstayonhigh = stayonhighlighted;
}

void us_findcibegin(void)
{
	us_finddoibegin(1);
}

void us_findibegin(void)
{
	us_finddoibegin(0);
}

UINTBIG us_initialtime, us_doubleclicktime;

void us_finddoibegin(INTSML compl)
{
	REGISTER VARIABLE *highvar;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER INTSML len, another;
	REGISTER char *str;
	HIGHLIGHT newhigh, oldhigh;
	REGISTER TECHNOLOGY *tech;
	INTSML i, tsx, tsy, j;
	INTBIG xc, yc, xw, yw, xcur, ycur, dist, bestdist, lx, hx, ly, hy;

	/* remember initial time of this action */
	us_initialtime = ticktime();
	us_doubleclicktime = doubleclicktime() / 2;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocstaticpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;
	us_dragstate = -1;
	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;

	/* get cursor coordinates */
	if (getxy(&xcur, &ycur)) return;
	us_dragx = xcur;   us_dragy = ycur;
	us_dragox = us_dragx;   us_dragoy = us_dragy;
	us_dragnodeproto = getcurfacet();

	/* see what is highlighted */
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar != NOVARIABLE)
	{
		len = (INTSML)getlength(highvar);
		(void)us_makehighlight(((char **)highvar->addr)[0], &newhigh);
	} else
	{
		len = 0;
		newhigh.status = 0;
		newhigh.fromgeom = NOGEOM;
		newhigh.fromport = NOPORTPROTO;
		newhigh.fromvar = NOVARIABLE;
		newhigh.frompoint = 0;
	}

	/* see what is under the cursor */
	if (us_dragstayonhigh != 0 && highvar != NOVARIABLE)
	{
		/* see if the cursor is over something highlighted */
		for(i=0; i<len; i++)
		{
			(void)us_makehighlight(((char **)highvar->addr)[i], &newhigh);
			if (us_cursoroverhigh(&newhigh, xcur, ycur, el_curwindowpart) != 0) break;
		}
		if (i < len)
		{
			/* re-find the closest port when one node is selected */
			if (len == 1 && (newhigh.status&HIGHFROM) != 0 &&
				newhigh.fromgeom->entrytype == OBJNODEINST)
			{
				ni = newhigh.fromgeom->entryaddr.ni;
				newhigh.fromport = NOPORTPROTO;
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					shapeportpoly(ni, pp, us_dragpoly, 0);
					us_dragpoly->desc = &us_highl;

					/* get distance of desired point to polygon */
					dist = polydistance(us_dragpoly, xcur, ycur);
					if (dist < 0)
					{
						newhigh.fromport = pp;
						break;
					}
					if (newhigh.fromport == NOPORTPROTO) bestdist = dist;
					if (dist > bestdist) continue;
					bestdist = dist;   newhigh.fromport = pp;
				}
			}
		} else us_findobject(xcur, ycur, el_curwindowpart, &newhigh, 0, 0, us_dragfport, 1, us_dragspecial);
	} else
	{
		another = 0;
		if (compl != 0)
		{
			for(i=0; i<len; i++)
			{
				(void)us_makehighlight(((char **)highvar->addr)[i], &newhigh);
				if (us_cursoroverhigh(&newhigh, xcur, ycur, el_curwindowpart) != 0) break;
			}
			if (i < len)
			{
				another = 1;
				(void)us_delhighlight(&newhigh);
				highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
				if (highvar == NOVARIABLE) len = 0; else
					len = (INTSML)getlength(highvar);
			}
		}
		us_findobject(xcur, ycur, el_curwindowpart, &newhigh, 0, another, us_dragfport, 1, us_dragspecial);
	}
	if (newhigh.status == 0)
	{
		/* nothing under cursor: select an area */
		if (compl != 0) us_dragstate = 2; else us_dragstate = 1;
		maketruerectpoly(us_dragx, us_dragx, us_dragy, us_dragy, us_dragpoly);
		us_dragpoly->style = CLOSEDRECT;
		return;
	}

	/* grid the cursor coordinates when over something */
	gridalign(&us_dragx, &us_dragy, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_dragx, us_dragy);
	us_dragox = us_dragx;   us_dragoy = us_dragy;

	/* clear port info if not requested or multiple highlights */
	if (us_dragfport == 0) newhigh.fromport = NOPORTPROTO;

	/* see if object under cursor is already highlighted */
	for(i=0; i<len; i++)
	{
		if (us_makehighlight(((char **)highvar->addr)[i], &oldhigh) != 0) continue;
		if (oldhigh.facet != newhigh.facet) continue;
		if ((oldhigh.status&HIGHTYPE) != (newhigh.status&HIGHTYPE)) continue;
		if (oldhigh.fromgeom != newhigh.fromgeom) continue;
		break;
	}

	/* is this a normal command or SHIFTed? */
	if (compl == 0)
	{
		/* normal find/move */
		newhigh.facet = getcurfacet();
		if (i >= len || us_dragpoint >= 0)
		{
			/* object is not highlighted: select only it and move */
			us_setfind(&newhigh, (INTSML)((us_dragpoint < 0 ? 0 : 1)),
				(INTSML)((us_dragextra != 0 ? HIGHEXTRA : 0)), 0, us_dragnobox);
		} else
		{
			/* object is already highlighted: rehighlight and move */
			(void)us_delhighlight(&newhigh);
			(void)us_addhighlight(&newhigh);
		}
	} else
	{
		/* SHIFT find/move */
		if (i >= len)
		{
			/* object not highlighted: add it and move */
			/* newhigh.fromport = NOPORTPROTO; */
			newhigh.facet = geomparent(newhigh.fromgeom);
			us_setfind(&newhigh, 0, (INTSML)((us_dragextra != 0 ? HIGHEXTRA : 0)), 1, us_dragnobox);
		} else
		{
			/* object highlighted: unhighlight it and quit */
			(void)us_delhighlight(&newhigh);
			return;
		}
	}

	/* can only drag if in "Mac" mode */
	if (us_dragstayonhigh == 0) return;

	/* do no motion if stillness requested */
	if (us_dragstill != 0 || (us_dragnodeproto->userbits&NLOCKED) != 0)
	{
		us_dragstate = -1;
		return;
	}

	/* get the objects to be moved */
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar == NOVARIABLE) return;

	/* see if a text object is highlighted */
	if (getlength(highvar) == 1)
	{
		(void)us_makehighlight(((char **)highvar->addr)[0], &us_draghigh);
		if ((us_draghigh.status&HIGHTYPE) == HIGHTEXT)
		{
			/* get descriptor and text string */
			us_gethighaddrtype(&us_draghigh, &us_dragaddr, &us_dragtype);
			us_dragnodeinst = us_gethighnodeinst(&us_draghigh);
			tech = us_hightech(&us_draghigh);
			us_dragdescript = us_gethighdescript(&us_draghigh);
			str = us_gethighstring(&us_draghigh);

			/* determine number of lines of text and text size */
			len = 1;
			if (us_draghigh.fromvar != NOVARIABLE && (us_draghigh.fromvar->type&VISARRAY) != 0)
				len = (INTSML)getlength(us_draghigh.fromvar);
			if (len > 1)
			{
				makedisparrayvarpoly(us_draghigh.fromgeom, el_curwindowpart, us_draghigh.fromvar, us_dragpoly);
				getbbox(us_dragpoly, &lx, &hx, &ly, &hy);
				xw = hx - lx;
				yw = hy - ly;
			} else
			{
				screensettextsize(el_curwindowpart, truefontsize((INTSML)((us_dragdescript&VTSIZE)>>VTSIZESH),
					el_curwindowpart, tech));
				screengettextsize(el_curwindowpart, str, &tsx, &tsy);
				xw = muldiv(tsx, el_curwindowpart->screenhx-el_curwindowpart->screenlx,
					el_curwindowpart->usehx-el_curwindowpart->uselx);
				yw = muldiv(tsy, el_curwindowpart->screenhy-el_curwindowpart->screenly,
					el_curwindowpart->usehy-el_curwindowpart->usely);
			}

			/* get offset */
			us_gethightextcenter(&us_draghigh, &xc, &yc, &j);
			us_dragoffx = xc;  us_dragoffy = yc;
			us_dragox = xw;    us_dragoy = yw;
			us_dragstate = 3;
			return;
		}
	}

	us_dragobjectlist = us_gethighlighted(OBJNODEINST | OBJARCINST);
	if (us_dragobjectlist[0] == NOGEOM) return;
	for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
		if (us_dragnodeproto != geomparent(us_dragobjectlist[i])) return;

	/* mark all nodes (including those touched by highlighted arcs) */
	for(ni = us_dragnodeproto->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		ni->temp1 = 0;
	for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
		if (us_dragobjectlist[i]->entrytype == OBJARCINST)
	{
		ai = us_dragobjectlist[i]->entryaddr.ai;
		ai->end[0].nodeinst->temp1 = ai->end[1].nodeinst->temp1 = 1;
	}
	for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
		if (us_dragobjectlist[i]->entrytype == OBJNODEINST)
			us_dragobjectlist[i]->entryaddr.ni->temp1 = 1;

	/* count the number of nodes, excluding locked ones */
	us_dragnodetotal = 0;
	for(ni = us_dragnodeproto->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->temp1 == 0) continue;
		if (us_canedit(us_dragnodeproto, ni->proto, 0) != 0)
		{
			ni->temp1 = 0;
			continue;
		}
		us_dragnodetotal++;
	}
	if (us_dragnodetotal == 0) us_cantdrag = 1; else
		us_cantdrag = 0;

	/* remove from list all arcs touching locked nodes */
	for(i=j=0; us_dragobjectlist[i] != NOGEOM; i++)
	{
		if (us_dragobjectlist[i]->entrytype == OBJARCINST)
		{
			ai = us_dragobjectlist[i]->entryaddr.ai;
			if (us_canedit(us_dragnodeproto, ai->end[0].nodeinst->proto, 0) != 0 ||
				us_canedit(us_dragnodeproto, ai->end[1].nodeinst->proto, 0) != 0) continue;
		}
		us_dragobjectlist[j++] = us_dragobjectlist[i];
	}
	us_dragobjectlist[j] = NOGEOM;

	/* build a list that includes all nodes touching selected arcs */
	if (us_dragnodetotal != 0)
	{
		us_dragnodelist = (NODEINST **)emalloc((us_dragnodetotal * (sizeof (NODEINST *))),
			el_tempcluster);
		if (us_dragnodelist == 0) return;
		for(i=0, ni = us_dragnodeproto->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->temp1 == 0) continue;
			us_dragnodelist[i++] = ni;
		}
	}

	/* save and turn off all highlighting */
	us_pushhighlight();
	us_clearhighlightcount();

	/* setup for moving */
	us_multidragbegin();
	us_dragstate = 0;
}

INTSML us_findidown(INTBIG x, INTBIG y)
{
	REGISTER UINTBIG now;
	INTSML ret, didpan;

	/* pan screen if drag went over edge */
	didpan = 0;
	if (x >= us_dragwindow->usehx)
	{
		x = us_dragwindow->usehx;
		now = ticktime();
		if (now - us_beforepantime >= MINSLIDEDELAY)
		{
			us_beforepantime = now;
			us_slideleft((us_dragwindow->screenhx - us_dragwindow->screenlx) / 10);
			if (us_dragstate == 1 || us_dragstate == 2) us_dragshown = 0;
			didpan = 1;
		}
	}
	if (x <= us_dragwindow->uselx)
	{
		x = us_dragwindow->uselx;
		now = ticktime();
		if (now - us_beforepantime >= MINSLIDEDELAY)
		{
			us_beforepantime = now;
			us_slideleft(-(us_dragwindow->screenhx - us_dragwindow->screenlx) / 10);
			if (us_dragstate == 1 || us_dragstate == 2) us_dragshown = 0;
			didpan = 1;
		}
	}
	if (y >= us_dragwindow->usehy)
	{
		y = us_dragwindow->usehy;
		now = ticktime();
		if (now - us_beforepantime >= MINSLIDEDELAY)
		{
			us_beforepantime = now;
			us_slideup(-(us_dragwindow->screenhy - us_dragwindow->screenly) / 10);
			if (us_dragstate == 1 || us_dragstate == 2) us_dragshown = 0;
			didpan = 1;
		}
	}
	if (y <= us_dragwindow->usely)
	{
		y = us_dragwindow->usely;
		now = ticktime();
		if (now - us_beforepantime >= MINSLIDEDELAY)
		{
			us_beforepantime = now;
			us_slideup((us_dragwindow->screenhy - us_dragwindow->screenly) / 10);
			if (us_dragstate == 1 || us_dragstate == 2) us_dragshown = 0;
			didpan = 1;
		}
	}

	/* make the drag */
	ret = 1;
	switch (us_dragstate)
	{
		case 0:
			now = ticktime();
			if (now < us_initialtime + us_doubleclicktime) ret = 0; else
				ret = us_multidragdown(x, y);
			break;
		case 1:
		case 2:
			ret = us_stretchdown(x, y);
			break;
		case 3:
			now = ticktime();
			if (now < us_initialtime + us_doubleclicktime) ret = 0; else
				ret = us_textmovedown(x, y);
			break;
	}

	/* if panning happened, finish redraw */
	if (didpan != 0) us_endchanges(NOWINDOWPART);

	return(ret);
}

void us_findiup(void)
{
	INTBIG xcur, ycur, search, lambda, dx, dy;
	XARRAY trans;
	REGISTER INTSML i, j;
	REGISTER INTBIG len, k, slx, shx, sly, shy, addtotal;
	REGISTER GEOM *geom;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER UINTBIG now;
	REGISTER char **highlist;
	HIGHLIGHT newhigh, oldhigh;
	REGISTER VARIABLE *highvar, *var;
	static POLYGON *poly = NOPOLYGON;

	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
	if (us_dragstate == 0)
	{
		us_multidragup();
		now = ticktime();
		if (now >= us_initialtime + us_doubleclicktime &&
			us_cantdrag == 0 && el_pleasestop == 0)
		{
			(void)getxy(&xcur, &ycur);
			gridalign(&xcur, &ycur, us_alignment);
			us_setcursorpos(NOWINDOWFRAME, xcur, ycur);
			us_getslide(us_dragangle, us_dragox, us_dragoy, xcur, ycur, &dx, &dy);
			if (dx != us_dragox || dy != us_dragoy)
				us_manymove(us_dragobjectlist, us_dragnodelist, (INTSML)us_dragnodetotal,
					dx-us_dragox, dy-us_dragoy);
		}
		if (us_dragnodetotal > 0) efree((char *)us_dragnodelist);
		(void)us_pophighlight(1);
	} else if (us_dragstate == 1)
	{
		/* get area of drag */
		us_invertdragup();
		slx = mini(us_dragox, us_dragx);   shx = maxi(us_dragox, us_dragx);
		sly = mini(us_dragoy, us_dragy);   shy = maxi(us_dragoy, us_dragy);
		us_clearhighlightcount();

		/* see how many objects are being highlighted */
		addtotal = 0;
		search = initsearch(slx, shx, sly, shy, us_dragnodeproto);
		while ((geom = nextobject(search)) != NOGEOM)
		{
			if ((addtotal%50) == 49)
				if (stopping("Selection")) { termsearch(search);   return; }

			/* don't find facets if not requested */
			if (us_dragspecial == 0 && (us_useroptions&NOINSTANCESELECT) != 0 &&
				geom->entrytype == OBJNODEINST && geom->entryaddr.ni->proto->primindex == 0) continue;

			/* eliminate objects that aren't really in the selected area */
			if (us_geominrect(geom, slx, shx, sly, shy) == 0) continue;
			addtotal++;
		}
		if (addtotal == 0) return;

		/* build a list of objects to highlight */
		highlist = (char **)emalloc(addtotal * (sizeof (char *)), el_tempcluster);
		if (highlist == 0) return;
		addtotal = 0;
		search = initsearch(slx, shx, sly, shy, us_dragnodeproto);
		while ((geom = nextobject(search)) != NOGEOM)
		{
			if ((addtotal%50) == 49)
				if (stopping("Selection")) { termsearch(search);   break; }

			/* eliminate objects that aren't really in the selected area */
			if (us_geominrect(geom, slx, shx, sly, shy) == 0) continue;

			if (geom->entrytype == OBJNODEINST)
			{
				ni = geom->entryaddr.ni;

				/* don't find facets if not requested */
				if (us_dragspecial == 0 && (us_useroptions&NOINSTANCESELECT) != 0 &&
					ni->proto->primindex == 0) continue;

				/* do not include hard-to-find nodes if not selecting specially */
				if (us_dragspecial == 0 && (ni->userbits&HARDSELECTN) != 0) continue;

				/* do not include "edge select" primitives that are outside the area */
				if (ni->proto->primindex != 0 &&
					(ni->proto->userbits&NEDGESELECT) != 0)
				{
					i = nodepolys(ni);
					makerot(ni, trans);
					for(j=0; j<i; j++)
					{
						shapenodepoly(ni, j, poly);
						if ((poly->desc->colstyle&INVISIBLE) == 0)
						{
							xformpoly(poly, trans);
							if (polyinrect(poly, slx, shx, sly, shy) == 0) break;
						}
					}
					if (j < i) continue;
				}
			} else
			{
				ai = geom->entryaddr.ai;

				/* do not include hard-to-find arcs if not selecting specially */
				if (us_dragspecial == 0 && (ai->userbits&HARDSELECTA) != 0) continue;

				/* do not include "edge select" arcs that are outside the area */
				if ((ai->proto->userbits&AEDGESELECT) != 0)
				{
					ai = geom->entryaddr.ai;
					i = arcpolys(ai);
					if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
					for(j=0; j<i; j++)
					{
						shapearcpoly(ai, j, poly);
						if ((poly->desc->colstyle&INVISIBLE) == 0)
						{
							if (polyinrect(poly, slx, shx, sly, shy) == 0) break;
						}
					}
					if (j < i) continue;
				}
			}

			newhigh.status = HIGHFROM;
			if (us_dragnobox != 0) newhigh.status |= HIGHNOBOX;
			newhigh.facet = us_dragnodeproto;
			newhigh.fromgeom = geom;
			newhigh.fromport = NOPORTPROTO;
			newhigh.fromvar = NOVARIABLE;
			newhigh.frompoint = 0;

			/* add snapping information if requested */
			if ((us_state&SNAPMODE) != SNAPMODENONE)
			{
				(void)getxy(&xcur, &ycur);
				us_selectsnap(&newhigh, xcur, ycur);
			}

			/* select the first port if ports are requested and node is artwork */
			if (us_dragfport != 0 && geom->entrytype == OBJNODEINST &&
				geom->entryaddr.ni->proto->primindex != 0 &&
					geom->entryaddr.ni->proto->tech == art_tech)
						newhigh.fromport = geom->entryaddr.ni->proto->firstportproto;

			/* special case when an invisible pin is selected */
			if (geom->entrytype == OBJNODEINST &&
				geom->entryaddr.ni->proto == gen_invispinprim)
			{
				/* if the pin has a displayable variable, highlight the text */
				for(i=0; i<geom->entryaddr.ni->numvar; i++)
				{
					var = &geom->entryaddr.ni->firstvar[i];
					if ((var->type&VDISPLAY) != 0)
					{
						newhigh.fromvar = var;
						newhigh.status = HIGHTEXT;
						break;
					}
				}
			}
			(void)allocstring(&highlist[addtotal], us_makehighlightstring(&newhigh), el_tempcluster);
			addtotal++;
		}
		if (addtotal > 0)
			(void)setvalkey((INTBIG)us_aid, VAID, us_highlighted, (INTBIG)highlist,
				VSTRING|VISARRAY|(addtotal<<VLENGTHSH)|VDONTSAVE);
		for(k=0; k<addtotal; k++) efree(highlist[k]);
		efree((char *)highlist);
	} else if (us_dragstate == 2)
	{
		/* special case when complementing highlight */
		us_invertdragup();
		slx = mini(us_dragox, us_dragx);   shx = maxi(us_dragox, us_dragx);
		sly = mini(us_dragoy, us_dragy);   shy = maxi(us_dragoy, us_dragy);
		highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
		if (highvar != NOVARIABLE) len = getlength(highvar); else len = 0;

		search = initsearch(slx, shx, sly, shy, us_dragnodeproto);
		while ((geom = nextobject(search)) != NOGEOM)
		{
			if (stopping("Selection")) { termsearch(search);   break; }

			/* don't find facets if not requested */
			if (us_dragspecial == 0 && (us_useroptions&NOINSTANCESELECT) != 0 &&
				geom->entrytype == OBJNODEINST && geom->entryaddr.ni->proto->primindex == 0) continue;

			/* eliminate objects that aren't really in the selected area */
			if (us_geominrect(geom, slx, shx, sly, shy) == 0) continue;

			/* do not include hard-to-find nodes if not selecting specially */
			if (us_dragspecial == 0 && geom->entrytype == OBJNODEINST &&
				(geom->entryaddr.ni->userbits&HARDSELECTN) != 0) continue;

			/* do not include "edge select" primitives that are outside the area */
			if (geom->entrytype == OBJNODEINST && geom->entryaddr.ni->proto->primindex != 0 &&
				(geom->entryaddr.ni->proto->userbits&NEDGESELECT) != 0)
			{
				ni = geom->entryaddr.ni;
				i = nodepolys(ni);
				if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
				makerot(ni, trans);
				for(j=0; j<i; j++)
				{
					shapenodepoly(ni, j, poly);
					if ((poly->desc->colstyle&INVISIBLE) == 0)
					{
						xformpoly(poly, trans);
						if (polyinrect(poly, slx, shx, sly, shy) == 0) break;
					}
				}
				if (j < i) continue;
			} else if (geom->entrytype == OBJARCINST &&
				(geom->entryaddr.ai->proto->userbits&AEDGESELECT) != 0)
			{
				ai = geom->entryaddr.ai;
				i = arcpolys(ai);
				if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
				for(j=0; j<i; j++)
				{
					shapearcpoly(ai, j, poly);
					if ((poly->desc->colstyle&INVISIBLE) == 0)
					{
						if (polyinrect(poly, slx, shx, sly, shy) == 0) break;
					}
				}
				if (j < i) continue;
			}

			newhigh.status = HIGHFROM;
			if (us_dragnobox != 0) newhigh.status |= HIGHNOBOX;
			newhigh.facet = us_dragnodeproto;
			newhigh.fromgeom = geom;
			newhigh.fromport = NOPORTPROTO;
			newhigh.fromvar = NOVARIABLE;
			newhigh.frompoint = 0;

			/* add snapping information if requested */
			if ((us_state&SNAPMODE) != SNAPMODENONE)
			{
				(void)getxy(&xcur, &ycur);
				us_selectsnap(&newhigh, xcur, ycur);
			}

			/* select the first port if ports are requested and node is artwork */
			if (us_dragfport != 0 && geom->entrytype == OBJNODEINST &&
				geom->entryaddr.ni->proto->primindex != 0 &&
					geom->entryaddr.ni->proto->tech == art_tech)
						newhigh.fromport = geom->entryaddr.ni->proto->firstportproto;

			/* special case when an invisible pin is selected */
			if (geom->entrytype == OBJNODEINST && geom->entryaddr.ni->proto == gen_invispinprim)
			{
				/* if the pin has a displayable variable, highlight the text */
				for(i=0; i<geom->entryaddr.ni->numvar; i++)
				{
					var = &geom->entryaddr.ni->firstvar[i];
					if ((var->type&VDISPLAY) != 0)
					{
						newhigh.fromvar = var;
						newhigh.status = HIGHTEXT;
						break;
					}
				}
			}

			/* see if this object is already highlighted */
			for(k=0; k<len; k++)
			{
				if (us_makehighlight(((char **)highvar->addr)[k], &oldhigh) != 0) continue;
				if (oldhigh.facet != newhigh.facet) continue;
				if ((oldhigh.status&HIGHTYPE) != HIGHFROM) continue;
				if (oldhigh.fromgeom != newhigh.fromgeom) continue;
				break;
			}
			if (k < len) (void)us_delhighlight(&newhigh); else
				(void)us_addhighlight(&newhigh);

			/* recache what is highlighted */
			highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
			if (highvar != NOVARIABLE) len = getlength(highvar); else len = 0;
		}
	} else if (us_dragstate == 3)
	{
		us_dragup();
		if (el_pleasestop != 0) return;
		now = ticktime();
		if (now < us_initialtime + us_doubleclicktime) return;
		(void)getxy(&xcur, &ycur);
		gridalign(&xcur, &ycur, us_alignment);
		us_setcursorpos(NOWINDOWFRAME, xcur, ycur);
		dx = xcur-us_dragx;    dy = ycur-us_dragy;
		us_clearhighlightcount();

		/* move entire node if it is an "invisible pin" */
		if (us_dragnodeinst != NONODEINST &&
			us_dragnodeinst->proto == gen_invispinprim &&
			us_dragtype == VNODEINST)
		{
			startobjectchange((INTBIG)us_dragnodeinst, VNODEINST);
			modifynodeinst(us_dragnodeinst, dx, dy, dx, dy, 0, 0);
			endobjectchange((INTBIG)us_dragnodeinst, VNODEINST);
			(void)us_addhighlight(&us_draghigh);
			return;
		}

		/* undraw the text */
		if (us_dragtype == VPORTPROTO)
		{
			startobjectchange((INTBIG)(((PORTPROTO *)us_dragaddr)->subnodeinst),
				VNODEINST);
		} else
		{
			startobjectchange(us_dragaddr, us_dragtype);
		}
		if (us_dragtype == VNODEPROTO && us_draghigh.fromvar != NOVARIABLE)
			us_undrawfacetvariable(us_draghigh.fromvar, (NODEPROTO *)us_dragaddr);

		/* set the new descriptor on the text */
		if (us_dragnodeinst != NONODEINST)
		{
			if (us_dragnodeinst->transpose != 0)
				makeangle(us_dragnodeinst->rotation, us_dragnodeinst->transpose, trans); else
					makeangle((INTSML)((3600-us_dragnodeinst->rotation)%3600), 0, trans);
			xform(dx, dy, &dx, &dy, trans);
		}
		lambda = us_hightech(&us_draghigh)->deflambda;
		dx = dx*4/lambda;   dy = dy*4/lambda;
		if ((us_dragdescript&VTXOFFNEG) != 0) dx -= (us_dragdescript&VTXOFF) >> VTXOFFSH; else
			dx += (us_dragdescript&VTXOFF) >> VTXOFFSH;
		if ((us_dragdescript&VTYOFFNEG) != 0) dy -= (us_dragdescript&VTYOFF) >> VTYOFFSH; else
			dy += (us_dragdescript&VTYOFF) >> VTYOFFSH;
		us_dragdescript = us_setdescriptoffset(us_dragdescript, dx, dy);
		us_modifytextdescript(&us_draghigh, us_dragdescript);

		/* redisplay the text */
		if (us_dragtype == VNODEPROTO && us_draghigh.fromvar != NOVARIABLE)
			us_drawfacetvariable(us_draghigh.fromvar, (NODEPROTO *)us_dragaddr);
		if (us_dragtype == VPORTPROTO)
		{
			endobjectchange((INTBIG)(((PORTPROTO *)us_dragaddr)->subnodeinst),
				VNODEINST);
		} else
		{
			endobjectchange(us_dragaddr, us_dragtype);
		}
		(void)us_addhighlight(&us_draghigh);

		/* modify all higher-level nodes if port moved */
		if (us_draghigh.fromvar == NOVARIABLE && us_draghigh.fromport != NOPORTPROTO)
		{
			for(ni = us_draghigh.fromport->parent->firstinst; ni != NONODEINST; ni = ni->nextinst)
			{
				if ((ni->userbits&NEXPAND) != 0 &&
					(us_draghigh.fromport->userbits&PORTDRAWN) == 0) continue;
				startobjectchange((INTBIG)ni, VNODEINST);
				endobjectchange((INTBIG)ni, VNODEINST);
			}
		}
	}
}

/*
 * Routine to determine whether geometry object "geom" is inside the area
 * (slx <= X <= shx, sly <= Y <= shy).  Returns nonzero if so.
 */
INTSML us_geominrect(GEOM *geom, INTBIG slx, INTBIG shx, INTBIG sly, INTBIG shy)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	INTBIG plx, ply, phx, phy;
	REGISTER INTBIG wid, lx, hx, ly, hy;
	XARRAY trans;
	static POLYGON *poly = NOPOLYGON;

	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
	if (geom->entrytype == OBJNODEINST)
	{
		/* handle nodes */
		ni = geom->entryaddr.ni;
		makerot(ni, trans);

		/* get the true bounds of the node */
		nodesizeoffset(ni, &plx, &ply, &phx, &phy);
		lx = ni->lowx+plx;   hx = ni->highx-phx;
		ly = ni->lowy+ply;   hy = ni->highy-phy;
		maketruerectpoly(lx, hx, ly, hy, poly);
		poly->style = FILLEDRECT;

		/* transform to account for node orientation */
		xformpoly(poly, trans);

		/* see if it is in the selected area */
		if (polyinrect(poly, slx, shx, sly, shy) == 0) return(0);
		return(1);
	}

	/* handle arcs */
	ai = geom->entryaddr.ai;
	wid = ai->width - arcwidthoffset(ai);
	if (wid == 0) wid = ai->proto->tech->deflambda;
	if (curvedarcoutline(ai, poly, FILLED, wid) != 0)
		makearcpoly(ai->length, wid, ai, poly, FILLED);
	if (polyinrect(poly, slx, shx, sly, shy) == 0) return(0);
	return(1);
}

/************************* CREATE *************************/

/* pre-initialization routine when inserting type "np" in arc "ai" */
void us_createinsinit(ARCINST *ai, NODEPROTO *np)
{
	us_dragarcinst = ai;
	us_dragnodeproto = np;
}

/* pre-initialization routine when creating an object */
void us_createinit(INTBIG cornerx, INTBIG cornery, NODEPROTO *np, INTSML angle, INTSML join)
{
	us_dragox = cornerx;
	us_dragoy = cornery;
	us_dragnodeproto = np;
	us_dragangle = angle;
	us_dragjoinfactor = join;
}

/* initialization routine when creating an object */
void us_createbegin(void)
{
	XARRAY trans;
	INTBIG cx, cy, lx, ly, hx, hy, xcur, ycur, pxs, pys;
	REGISTER NODEINST *ni;
	REGISTER WINDOWFRAME *curframe;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocstaticpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	(void)getxy(&xcur, &ycur);
	curframe = getwindowframe(1);
	if ((us_aid->aidstate&MENUON) != 0)
	{
		if (curframe == us_menuframe ||
			(us_menuframe == NOWINDOWFRAME && ycur >= us_menuly && ycur <= us_menuhy &&
				xcur >= us_menulx && xcur <= us_menuhx))
		{
			el_pleasestop = 1;
		}
	}
	us_dragshown = 0;
	ni = dummynode();
	ni->proto = us_dragnodeproto;
	ni->rotation = us_dragangle%3600;
	ni->transpose = us_dragangle/3600;
	defaultnodesize(us_dragnodeproto, &pxs, &pys);
	ni->lowx = xcur;   ni->highx = xcur + pxs;
	ni->lowy = ycur;   ni->highy = ycur + pys;
	makerot(ni, trans);
	nodeprotosizeoffset(us_dragnodeproto, &lx, &ly, &hx, &hy);
	maketruerectpoly(ni->lowx+lx, ni->highx-hx, ni->lowy+ly, ni->highy-hy, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	xformpoly(us_dragpoly, trans);
	corneroffset(NONODEINST, us_dragnodeproto, (INTSML)(us_dragangle%3600),
		(INTSML)(us_dragangle/3600), &cx, &cy,
			(INTSML)((us_useroptions&CENTEREDPRIMITIVES) != 0 ? 1 : 0));
	us_dragx = xcur + cx;   us_dragy = ycur + cy;
	us_dragwindow = el_curwindowpart;
}

/* initialization routine when creating along an angle */
void us_createabegin(void)
{
	XARRAY trans;
	INTBIG cx, cy, lx, ly, hx, hy, xcur, ycur, pxs, pys;
	REGISTER NODEINST *ni;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	(void)getxy(&xcur, &ycur);
	us_dragshown = 0;
	ni = dummynode();
	ni->proto = us_dragnodeproto;
	ni->rotation = 0;
	ni->transpose = 0;
	defaultnodesize(us_dragnodeproto, &pxs, &pys);
	ni->lowx = xcur;   ni->highx = xcur + pxs;
	ni->lowy = ycur;   ni->highy = ycur + pys;
	makerot(ni, trans);
	nodeprotosizeoffset(us_dragnodeproto, &lx, &ly, &hx, &hy);
	maketruerectpoly(ni->lowx+lx, ni->highx-hx, ni->lowy+ly, ni->highy-hy, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	xformpoly(us_dragpoly, trans);
	corneroffset(NONODEINST, us_dragnodeproto, 0, 0, &cx, &cy,
		(INTSML)((us_useroptions&CENTEREDPRIMITIVES) != 0 ? 1 : 0));
	us_dragoffx = cx;
	us_dragoffy = cy;
	us_dragx = xcur;   us_dragy = ycur;
	us_dragwindow = el_curwindowpart;
	us_dragobject = NOGEOM;
}

/* cursor advance routine when creating along an angle */
INTSML us_createadown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG dx, dy;
	REGISTER INTSML i;
	INTBIG rdx, rdy, pxs, pys;
	REGISTER GEOM *foundgeom;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	us_getslide(us_dragangle, us_dragox, us_dragoy, us_lastcurx, us_lastcury, &rdx, &rdy);
	dx = rdx - us_dragoffx;
	dy = rdy - us_dragoffy;

	/* find the object under the cursor */
	if (us_dragjoinfactor == 0) foundgeom = NOGEOM; else
	{
		defaultnodesize(us_dragnodeproto, &pxs, &pys);
		foundgeom = us_getclosest(dx+pxs/2, dy+pys/2, us_dragwindow->curnodeproto);
	}

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == dx && us_dragy == dy) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* if the selected other object is being shown, erase it */
	if (us_dragobject != NOGEOM)
	{
		if (us_dragobject != foundgeom)
			us_highlighteverywhere(us_dragobject, NOPORTPROTO, 0,0, ALLOFF, 0);
	}

	/* advance the box */
	for(i = 0; i < us_dragpoly->count; i++)
	{
		us_dragpoly->xv[i] += dx - us_dragx;
		us_dragpoly->yv[i] += dy - us_dragy;
	}
	us_dragx = dx;
	us_dragy = dy;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	if (foundgeom != NOGEOM && foundgeom != us_dragobject)
		us_highlighteverywhere(foundgeom, NOPORTPROTO, 0, 0, HIGHLIT, 0);
	us_dragobject = foundgeom;
	return(0);
}

/* cursor advance routine when creating inside an arc */
INTSML us_createinsdown(INTBIG x, INTBIG y)
{
	REGISTER INTSML i;
	INTBIG dx, dy, pxs, pys;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* find closest point along arc */
	dx = us_lastcurx;   dy = us_lastcury;
	(void)closestpointtosegment(us_dragarcinst->end[0].xpos, us_dragarcinst->end[0].ypos,
		us_dragarcinst->end[1].xpos, us_dragarcinst->end[1].ypos, &dx, &dy);
	defaultnodesize(us_dragnodeproto, &pxs, &pys);
	dx -= pxs/2;
	dy -= pys/2;

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == dx && us_dragy == dy) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* advance the box */
	for(i = 0; i < us_dragpoly->count; i++)
	{
		us_dragpoly->xv[i] += dx - us_dragx;
		us_dragpoly->yv[i] += dy - us_dragy;
	}
	us_dragx = dx;
	us_dragy = dy;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/* termination routine when creating along an angle */
void us_createaup(void)
{
	if (us_dragshown != 0)
	{
		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}
	if (us_dragobject != NOGEOM)
		us_highlighteverywhere(us_dragobject, NOPORTPROTO, 0, 0, ALLOFF, 0);
}

/************************* DUPLICATE and multiple MOVE *************************/

/* preinitialization routine when duplicating objects */
void us_multidraginit(INTBIG xc, INTBIG yc, GEOM **geomlist, NODEINST **nodelist,
	INTSML total, INTSML angle, INTSML extra)
{
	us_dragox = xc;
	us_dragoy = yc;
	us_dragobjectlist = geomlist;
	us_dragnodelist = nodelist;
	us_dragnodetotal = total;
	us_dragangle = angle;
	us_dragextra = extra;
	us_cantdrag = 0;
}

/* initialization routine when duplicating objects */
void us_multidragbegin(void)
{
	REGISTER INTSML i;
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *ai;

	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	(void)getxy(&us_dragx, &us_dragy);
	gridalign(&us_dragx, &us_dragy, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_dragx, us_dragy);
	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;

	/* preprocess all arcs that stretch to moved nodes if in verbose mode */
	if (us_dragextra != 0)
	{
		/* reset clock (temp2) and clear display nature bits (temp1) on arcs */
		for(i=0; i<us_dragnodetotal; i++)
			for(pi = us_dragnodelist[i]->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			ai->temp1 = ai->temp2 = 0;
		}

		/* mark number of ends that move on each arc */
		for(i=0; i<us_dragnodetotal; i++)
			for(pi = us_dragnodelist[i]->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				pi->conarcinst->temp1++;

		/* do not move arcs that are already in move list */
		for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
		{
			if (us_dragobjectlist[i]->entrytype != OBJARCINST) continue;
			ai = us_dragobjectlist[i]->entryaddr.ai;
			ai->temp1 = 0;
		}
	}
}

/* cursor advance routine when duplicating objects */
INTSML us_multidragdown(INTBIG x, INTBIG y)
{
	INTBIG nx, ny;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	us_getslide(us_dragangle, us_dragox, us_dragoy, us_lastcurx, us_lastcury, &nx, &ny);
	us_lastcurx = nx;   us_lastcury = ny;

	/* warn if moving and can't */
	if (us_cantdrag != 0)
	{
		if (us_dragx != us_lastcurx || us_dragy != us_lastcury)
		{
			if (us_cantdrag == 1)
				us_abortcommand("Sorry, changes are currently disallowed");
			us_cantdrag++;
			return(0);
		}
	}

	/* if the highlighting is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw highlighting */
		us_multidragdraw(0, us_dragx-us_dragox, us_dragy-us_dragoy);
	}

	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;
	us_multidragdraw(HIGHLIT, us_dragx-us_dragox, us_dragy-us_dragoy);
	us_dragshown = 1;
	return(0);
}

void us_multidragup(void)
{
	if (us_dragshown != 0)
		us_multidragdraw(0, us_dragx-us_dragox, us_dragy-us_dragoy);
}

void us_multidragdraw(INTBIG col, INTBIG dx, INTBIG dy)
{
	REGISTER INTSML i, j, thisend, portcount, len, endfixed;
	REGISTER INTBIG wid, lambda, xcur, ycur, xw, yw;
	INTBIG lx, ly, hx, hy, xc, yc;
	XARRAY trans;
	INTSML tsx, tsy;
	REGISTER char *str;
	REGISTER VARIABLE *var;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTPROTO *pp;

	us_highl.col = (INTSML)col;
	if (us_dragextra != 0) us_dragextra++;
	for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
	{
		if (us_dragobjectlist[i]->entrytype != OBJNODEINST) continue;
		ni = us_dragobjectlist[i]->entryaddr.ni;

		/* if node is an invisible pin with text, show the text highlight */
		if (ni->proto == gen_invispinprim)
		{
			var = NOVARIABLE;
			for(j=0; j<ni->numvar; j++)
			{
				var = &ni->firstvar[j];
				if ((var->type&VDISPLAY) != 0) break;
				var = NOVARIABLE;
			}
			if (var != NOVARIABLE)
			{
				/* determine number of lines of text and text size */
				str = describevariable(var, -1, -1);
				len = 1;
				if ((var->type&VISARRAY) != 0) len = (INTSML)getlength(var);
				if (len > 1)
				{
					xw = ni->geom->highx - ni->geom->lowx;
					yw = ni->geom->highy - ni->geom->lowy;
				} else
				{
					screensettextsize(el_curwindowpart, truefontsize((INTSML)((var->textdescript&VTSIZE)>>VTSIZESH),
						el_curwindowpart, ni->proto->tech));
					screengettextsize(el_curwindowpart, str, &tsx, &tsy);
					xw = muldiv(tsx, el_curwindowpart->screenhx-el_curwindowpart->screenlx,
						el_curwindowpart->usehx-el_curwindowpart->uselx);
					yw = muldiv(tsy, el_curwindowpart->screenhy-el_curwindowpart->screenly,
						el_curwindowpart->usehy-el_curwindowpart->usely);
				}

				/* get center of highlight, and also a style */
				us_dragpoly->count = 1;
				us_dragpoly->style = FILLED;
				us_dragpoly->xv[0] = (ni->lowx + ni->highx) / 2;
				us_dragpoly->yv[0] = (ni->lowy + ni->highy) / 2;
				adjustdisoffset((INTBIG)ni, VNODEINST, whattech(ni->proto), us_dragpoly, var->textdescript);
				makerot(ni, trans);
				xformpoly(us_dragpoly, trans);
				getcenter(us_dragpoly, &xc, &yc);

				/* clip range of text descriptor */
				lambda = figurelambda(ni->geom);
				xcur = (xc + dx) * 4 / lambda;
				xc += dx;
				if (xcur > 1023) xc = 1023 * lambda / 4; else
					if (xcur < -1023) xc = -1023 * lambda / 4;
				ycur = (yc + dy) * 4 / lambda;
				yc += dy;
				if (ycur > 1023) yc = 1023 * lambda / 4; else
					if (ycur < -1023) yc = -1023 * lambda / 4;

				/* draw the descriptor */
				us_buildtexthighpoly(ni->geom->lowx, ni->geom->highx,
					ni->geom->lowy, ni->geom->highy, xc, yc, xw, yw,
						us_dragpoly->style, us_dragpoly);

				/* draw the text highlight */
				(void)us_showpoly(us_dragpoly, us_dragwindow);
				continue;
			}
		}

		makerot(ni, trans);
		nodesizeoffset(ni, &lx, &ly, &hx, &hy);
		maketruerectpoly(ni->lowx+lx, ni->highx-hx, ni->lowy+ly, ni->highy-hy, us_dragpoly);
		us_dragpoly->style = CLOSEDRECT;
		xformpoly(us_dragpoly, trans);
		for(j=0; j<us_dragpoly->count; j++)
		{
			us_dragpoly->xv[j] += dx;
			us_dragpoly->yv[j] += dy;
		}
		(void)us_showpoly(us_dragpoly, us_dragwindow);

		/* draw more if in verbose mode */
		if (us_dragextra != 0)
		{
			/* if only 1 node selected, show its ports */
			if (us_dragnodetotal == 1)
			{
				portcount = 0;
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					/* compute the port bounds */
					shapeportpoly(ni, pp, us_dragpoly, 0);
					us_dragpoly->desc = &us_highl;

					/* see if the polygon is a single point */
					for(j=1; j<us_dragpoly->count; j++)
						if (us_dragpoly->xv[j] != us_dragpoly->xv[j-1] ||
							us_dragpoly->yv[j] != us_dragpoly->yv[j-1]) break;
					if (j < us_dragpoly->count)
					{
						/* not a single point, draw its outline */
						if (us_dragpoly->style == FILLEDRECT) us_dragpoly->style = CLOSEDRECT;
						if (us_dragpoly->style == FILLED) us_dragpoly->style = CLOSED;
						if (us_dragpoly->style == DISC) us_dragpoly->style = CIRCLE;
					} else
					{
						/* single point port: make it a cross */
						us_dragpoly->count = 1;
						us_dragpoly->style = CROSS;
					}
					for(j=0; j<us_dragpoly->count; j++)
					{
						us_dragpoly->xv[j] += dx;
						us_dragpoly->yv[j] += dy;
					}

					/* draw the port */
					(void)us_showpoly(us_dragpoly, us_dragwindow);

					/* stop if interrupted */
					portcount++;
					if ((portcount%20) == 0)
					{
						if (stopping("highlight")) break;
					}
				}
			}

			/* rubber-band arcs to this node */
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				ai = pi->conarcinst;

				/* ignore if not drawing this arc or already drawn */
				if (ai->temp1 == 0 || ai->temp2 == us_dragextra) continue;
				ai->temp2 = us_dragextra;

				/* get original endpoint of arc */
				us_dragpoly->xv[0] = ai->end[0].xpos;
				us_dragpoly->yv[0] = ai->end[0].ypos;
				us_dragpoly->xv[1] = ai->end[1].xpos;
				us_dragpoly->yv[1] = ai->end[1].ypos;
				us_dragpoly->count = 2;
				us_dragpoly->style = OPENED;

				/* offset this end by amount node moves */
				if (ai->end[0].portarcinst == pi) thisend = 0; else thisend = 1;
				us_dragpoly->xv[thisend] += dx;
				us_dragpoly->yv[thisend] += dy;

				/* offset other end if both connect to moved nodes or arc rigid */
				endfixed = 0;
				if (ai->temp1 >= 2 || (ai->userbits&FIXED) != 0) endfixed = 1;

				if (endfixed != 0)
				{
					us_dragpoly->xv[1-thisend] += dx;
					us_dragpoly->yv[1-thisend] += dy;
				} else if ((ai->userbits&FIXANG) != 0)
				{
					if (ai->end[0].xpos == ai->end[1].xpos) us_dragpoly->xv[1-thisend] += dx;
					if (ai->end[0].ypos == ai->end[1].ypos) us_dragpoly->yv[1-thisend] += dy;
				}

				/* draw line */
				(void)us_showpoly(us_dragpoly, us_dragwindow);
			}
		}
	}

	for(i=0; us_dragobjectlist[i] != NOGEOM; i++)
	{
		if (us_dragobjectlist[i]->entrytype != OBJARCINST) continue;
		ai = us_dragobjectlist[i]->entryaddr.ai;
		wid = ai->width - arcwidthoffset(ai);
		makearcpoly(ai->length, wid, ai, us_dragpoly, CLOSED);
		for(j=0; j<us_dragpoly->count; j++)
		{
			us_dragpoly->xv[j] += dx;
			us_dragpoly->yv[j] += dy;
		}
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}
}

/************************* DISTANCE TRACKING *************************/

INTSML us_distancewasaborted;

void us_distanceinit(void)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragnodeproto = el_curwindowpart->curnodeproto;
	us_dragshown = 0;
	us_dragwindow = el_curwindowpart;
	us_distancewasaborted = 0;
}

INTSML us_distanceaborted(void)
{
	return(us_distancewasaborted);
}

void us_distancebegin(void)
{
}

INTSML us_distancedown(INTBIG x, INTBIG y)
{
	REGISTER INTBIG dist;

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0) return(0);
	if (el_curwindowpart->curnodeproto != us_dragnodeproto) return(0);
	(void)getxy(&us_dragx, &us_dragy);
	gridalign(&us_dragx, &us_dragy, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_dragx, us_dragy);

	if (us_dragshown == 0)
	{
		us_dragox = us_dragx;   us_dragoy = us_dragy;
	} else
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the distance */
		us_highl.col = 0;
		us_drawdistance();
	}

	/* remember this measured distance */
	us_lastmeasurex = us_dragx - us_dragox;
	us_lastmeasurey = us_dragy - us_dragoy;
	us_validmesaure = 1;

	/* draw crosses at the end */
	us_highl.col = HIGHLIT;
	us_lastcurx = us_dragx;   us_lastcury = us_dragy;
	us_dragpoly->font = TXTMEDIUM;
	us_dragpoly->tech = el_curtech;
	dist = computedistance(us_dragox, us_dragoy, us_dragx, us_dragy);
	(void)strcpy(us_dragmessage, "Distance: ");
	(void)strcat(us_dragmessage, latoa(dist));
	(void)strcat(us_dragmessage, " (dX=");
	(void)strcat(us_dragmessage, latoa(us_dragx - us_dragox));
	(void)strcat(us_dragmessage, " dY=");
	(void)strcat(us_dragmessage, latoa(us_dragy - us_dragoy));
	(void)strcat(us_dragmessage, ")");
	us_dragpoly->string = us_dragmessage;
	us_drawdistance();
	us_dragshown = 1;
	return(0);
}

/*
 * routine to cause termination of tracking when a key is typed
 */
INTSML us_distancechar(INTBIG x, INTBIG y, INTSML chr)
{
	if (chr == 'x' || chr == 'X')
	{
		if (us_dragshown != 0)
		{
			/* undraw the distance */
			us_highl.col = 0;
			us_drawdistance();
		}
		us_dragshown = 0;
		us_distancedown(x, y);
		return(0);
	}
	if (chr == '\n' || chr == '\r')
	{
		us_distancewasaborted = 1;
		if (us_dragshown != 0)
		{
			/* undraw the distance */
			us_highl.col = 0;
			us_drawdistance();
		}
		return(1);
	}
	return(0);
}

void us_drawdistance(void)
{
	REGISTER WINDOWPART *w;

	/* draw the measured distance */
	us_dragpoly->xv[0] = (us_dragox+us_lastcurx) / 2;
	us_dragpoly->yv[0] = (us_dragoy+us_lastcury) / 2;
	us_dragpoly->count = 1;
	us_dragpoly->style = TEXTBOTLEFT;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if (w->curnodeproto == us_dragnodeproto)
			(void)us_showpoly(us_dragpoly, w);

	/* draw the line */
	us_dragpoly->xv[0] = us_dragox;     us_dragpoly->yv[0] = us_dragoy;
	us_dragpoly->xv[1] = us_lastcurx;   us_dragpoly->yv[1] = us_lastcury;
	us_dragpoly->count = 2;
	us_dragpoly->style = OPENED;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if (w->curnodeproto == us_dragnodeproto)
			(void)us_showpoly(us_dragpoly, w);

	/* draw crosses at the end */
	us_dragpoly->xv[0] = us_dragox;     us_dragpoly->yv[0] = us_dragoy;
	us_dragpoly->count = 1;
	us_dragpoly->style = CROSS;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if (w->curnodeproto == us_dragnodeproto)
			(void)us_showpoly(us_dragpoly, w);
	us_dragpoly->xv[0] = us_lastcurx;   us_dragpoly->yv[0] = us_lastcury;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if (w->curnodeproto == us_dragnodeproto)
			(void)us_showpoly(us_dragpoly, w);
}

void us_distanceup(void)
{
	if (us_dragshown != 0 && us_distancewasaborted == 0)
	{
		/* leave the results in the messages window */
		ttyputmsg("%s", us_dragpoly->string);
	}
}

/************************* SLIDER THUMB TRACKING *************************/

/*
 * initialization method for tracking the horizontal thumb.  The cursor X coordinate
 * is "x" and the window is "w".  The horizontal slider's top side is "hy" and it
 * runs horizontally from "lx" to "hx" (including arrows).
 */
void us_hthumbbegin(INTBIG x, WINDOWPART *w, INTBIG hy, INTBIG lx, INTBIG hx)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_dragwindow = w;
	us_initthumbcoord = x;

	us_trackww.screenlx = us_trackww.uselx = (INTSML)lx;
	us_trackww.screenhx = us_trackww.usehx = (INTSML)hx;
	us_trackww.screenly = us_trackww.usely = (INTSML)(hy-DISPLAYSLIDERSIZE);
	us_trackww.screenhy = us_trackww.usehy = (INTSML)hy;
	us_trackww.frame = w->frame;
	us_trackww.state = DISPWINDOW;
	computewindowscale(&us_trackww);
}

/* tracking method for the horizontal thumb */
INTSML us_hthumbdown(INTBIG x, INTBIG y)
{
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (x == us_lastcurx) return(0);

		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragwindow->thumblx + (us_lastcurx-us_initthumbcoord)+1;
		us_dragpoly->yv[0] = us_trackww.usehy-3;
		us_dragpoly->xv[1] = us_dragwindow->thumbhx + (us_lastcurx-us_initthumbcoord)-1;
		us_dragpoly->yv[1] = us_trackww.usehy-DISPLAYSLIDERSIZE+1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);
	}

	/* draw the outline */
	us_highl.col = HIGHLIT;
	if (us_dragwindow->thumblx + (x-us_initthumbcoord) <= us_trackww.uselx+DISPLAYSLIDERSIZE)
		 x = us_trackww.uselx+DISPLAYSLIDERSIZE+1 - us_dragwindow->thumblx + us_initthumbcoord;
	if (us_dragwindow->thumbhx + (x-us_initthumbcoord) >= us_trackww.usehx-DISPLAYSLIDERSIZE)
		 x = us_trackww.usehx-DISPLAYSLIDERSIZE-1 - us_dragwindow->thumbhx + us_initthumbcoord;
	us_dragpoly->xv[0] = us_dragwindow->thumblx + (x-us_initthumbcoord)+1;
	us_dragpoly->yv[0] = us_trackww.usehy-3;
	us_dragpoly->xv[1] = us_dragwindow->thumbhx + (x-us_initthumbcoord)-1;
	us_dragpoly->yv[1] = us_trackww.usehy-DISPLAYSLIDERSIZE+1;
	us_dragpoly->count = 2;
	us_dragpoly->style = FILLEDRECT;
	(void)us_showpoly(us_dragpoly, &us_trackww);

	us_dragshown = 1;
	us_lastcurx = x;
	return(0);
}

/* completion method for tracking the horozintal thumb in a regular edit window */
void us_hthumbdone(void)
{
	REGISTER NODEPROTO *np;
	REGISTER INTBIG facetsizex, screensizex, thumbareax, dx, dsx;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragwindow->thumblx + (us_lastcurx-us_initthumbcoord)+1;
		us_dragpoly->yv[0] = us_trackww.usehy-3;
		us_dragpoly->xv[1] = us_dragwindow->thumbhx + (us_lastcurx-us_initthumbcoord)-1;
		us_dragpoly->yv[1] = us_trackww.usehy-DISPLAYSLIDERSIZE+1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		/* adjust the screen */
		dx = us_lastcurx-us_initthumbcoord;
		np = us_dragwindow->curnodeproto;
		if (np == NONODEPROTO) return;
		facetsizex = np->highx - np->lowx;
		screensizex = us_dragwindow->screenhx - us_dragwindow->screenlx;
		thumbareax = (us_dragwindow->usehx - us_dragwindow->uselx - DISPLAYSLIDERSIZE*2-4) / 2;
		if (facetsizex <= screensizex)
		{
			dsx = dx * (us_dragwindow->screenhx-us_dragwindow->screenlx) / thumbareax;
		} else
		{
			dsx = dx * (np->highx-np->lowx) / thumbareax;
		}
		us_slideleft(dsx);
	}
}

/* completion method for tracking the horizontal thumb in a text-edit window */
void us_ehthumbdone(void)
{
	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragwindow->thumblx + (us_lastcurx-us_initthumbcoord)+1;
		us_dragpoly->yv[0] = us_trackww.usehy-3;
		us_dragpoly->xv[1] = us_dragwindow->thumbhx + (us_lastcurx-us_initthumbcoord)-1;
		us_dragpoly->yv[1] = us_trackww.usehy-DISPLAYSLIDERSIZE+1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		us_dragpoint = (us_dragwindow->thumblx + us_dragwindow->thumbhx) / 2 +
			(us_lastcurx-us_initthumbcoord);
	}
}

/* return method for reporting the new horizontal thumb in a text-edit window */
INTBIG us_ehthumbend(void)
{
	return(us_dragpoint);
}

/*
 * initialization method for tracking the vertical thumb.  The cursor Y coordinate
 * is "y" and the window is "w".  The vertical slider's left side is "hx" and it
 * runs vertically from "ly" to "hy" (including arrows).  The total number of
 * lines of text is "totallines" and the slider is on the left side if "onleft"
 * is nonzero.
 */
void us_vthumbbegin(INTBIG y, WINDOWPART *w, INTBIG hx, INTBIG ly, INTBIG hy,
	INTBIG totallines, INTSML onleft)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_dragwindow = w;
	us_initthumbcoord = y;
	us_dragnodetotal = totallines;
	us_dragpoint = -1;

	if (onleft != 0)
	{
		/* slider is on the left (simulation window) */
		us_trackww.screenlx = us_trackww.uselx = (INTSML)hx;
		us_trackww.screenhx = us_trackww.usehx = w->usehx;
		us_dragoffx = hx-2;
	} else
	{
		/* slider is on the right (edit, text edit, and explorer window) */
		us_trackww.screenlx = us_trackww.uselx = w->uselx;
		us_trackww.screenhx = us_trackww.usehx = hx+DISPLAYSLIDERSIZE;
		us_dragoffx = hx;
	}
	us_trackww.screenly = ly;
	us_trackww.usely = (INTSML)ly;
	us_trackww.screenhy = hy;
	us_trackww.usehy = (INTSML)hy;
	us_trackww.frame = w->frame;
	us_trackww.state = DISPWINDOW;
	computewindowscale(&us_trackww);
}

/* tracking method for the vertical thumb */
INTSML us_vthumbdown(INTBIG x, INTBIG y)
{
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (y == us_lastcury) return(0);

		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragoffx+3;
		us_dragpoly->yv[0] = us_dragwindow->thumbly + (us_lastcury-us_initthumbcoord)+1;
		us_dragpoly->yv[1] = us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord)-1;
		us_dragpoly->xv[1] = us_dragoffx+DISPLAYSLIDERSIZE-1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);
	}

	/* draw the outline */
	us_highl.col = HIGHLIT;
	if (us_dragwindow->thumbly + (y-us_initthumbcoord) <= us_trackww.usely+DISPLAYSLIDERSIZE)
		 y = us_trackww.usely+DISPLAYSLIDERSIZE+1 - us_dragwindow->thumbly + us_initthumbcoord;
	if (us_dragwindow->thumbhy + (y-us_initthumbcoord) >= us_trackww.usehy-DISPLAYSLIDERSIZE)
		 y = us_trackww.usehy-DISPLAYSLIDERSIZE-1 - us_dragwindow->thumbhy + us_initthumbcoord;
	us_dragpoly->xv[0] = us_dragoffx+3;
	us_dragpoly->yv[0] = us_dragwindow->thumbly + (y-us_initthumbcoord)+1;
	us_dragpoly->yv[1] = us_dragwindow->thumbhy + (y-us_initthumbcoord)-1;
	us_dragpoly->xv[1] = us_dragoffx+DISPLAYSLIDERSIZE-1;
	us_dragpoly->count = 2;
	us_dragpoly->style = FILLEDRECT;
	(void)us_showpoly(us_dragpoly, &us_trackww);

	us_dragshown = 1;
	us_lastcury = y;
	return(0);
}

/* completion method for tracking the vertical thumb in regular edit window */
void us_vthumbdone(void)
{
	REGISTER NODEPROTO *np;
	REGISTER INTBIG facetsizey, screensizey, thumbareay, dy, dsy;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragoffx+3;
		us_dragpoly->yv[0] = us_dragwindow->thumbly + (us_lastcury-us_initthumbcoord)+1;
		us_dragpoly->yv[1] = us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord)-1;
		us_dragpoly->xv[1] = us_dragoffx+DISPLAYSLIDERSIZE-1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		/* adjust the screen */
		dy = us_lastcury-us_initthumbcoord;
		np = us_dragwindow->curnodeproto;
		if (np == NONODEPROTO) return;
		facetsizey = np->highy - np->lowy;
		screensizey = us_dragwindow->screenhy - us_dragwindow->screenly;
		thumbareay = (us_dragwindow->usehy - us_dragwindow->usely - DISPLAYSLIDERSIZE*2-4) / 2;
		if (facetsizey <= screensizey)
		{
			dsy = dy * (us_dragwindow->screenhy-us_dragwindow->screenly) / thumbareay;
		} else
		{
			dsy = dy * (np->highy-np->lowy) / thumbareay;
		}
		us_slideup(-dsy);
	}
}

/* initialization method for tracking the arrows on sliders */
void us_arrowclickbegin(WINDOWPART *w, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy, INTBIG amount)
{
	us_dragwindow = w;
	us_arrowlx = lx;
	us_arrowhx = hx;
	us_arrowly = ly;
	us_arrowhy = hy;
	us_arrowamount = amount;
}

INTSML us_varrowdown(INTBIG x, INTBIG y)
{
	if (x < us_arrowlx || x > us_arrowhx) return(0);
	if (y < us_arrowly || y > us_arrowhy) return(0);
	if (y >= us_dragwindow->thumbly && y <= us_dragwindow->thumbhy) return(0);
	us_slideup(us_arrowamount);
	us_endbatch();
	return(0);
}

INTSML us_harrowdown(INTBIG x, INTBIG y)
{
	if (x < us_arrowlx || x > us_arrowhx) return(0);
	if (y < us_arrowly || y > us_arrowhy) return(0);
	if (x >= us_dragwindow->thumblx && x <= us_dragwindow->thumbhx) return(0);
	us_slideleft(us_arrowamount);
	us_endbatch();
	return(0);
}

/* completion method for tracking the vertical thumb in an explorer or text-edit window */
void us_evthumbdone(void)
{
	REGISTER INTBIG thumbarea;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragoffx+3;
		us_dragpoly->yv[0] = us_dragwindow->thumbly + (us_lastcury-us_initthumbcoord)+1;
		us_dragpoly->yv[1] = us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord)-1;
		us_dragpoly->xv[1] = us_dragoffx+DISPLAYSLIDERSIZE-1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		thumbarea = us_trackww.usehy - us_trackww.usely - DISPLAYSLIDERSIZE*2;
		us_dragpoint = (us_trackww.usehy - DISPLAYSLIDERSIZE - 1 -
			(us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord))) *
				us_dragnodetotal / thumbarea;
		if (us_dragpoint < 0) us_dragpoint = 0;
	}
}

/* return method for reporting the vertical thumb in an explorer window */
INTBIG us_evthumbend(void)
{
	return(us_dragpoint);
}

/* completion method for tracking the vertical thumb in a simulation waveform window */
void us_svthumbdone(void)
{
	REGISTER INTBIG thumbarea;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragoffx+3;
		us_dragpoly->yv[0] = us_dragwindow->thumbly + (us_lastcury-us_initthumbcoord)+1;
		us_dragpoly->yv[1] = us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord)-1;
		us_dragpoly->xv[1] = us_dragoffx+DISPLAYSLIDERSIZE-1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		thumbarea = us_dragwindow->usehy - us_dragwindow->usely - DISPLAYSLIDERSIZE*2;
		us_dragpoint = (us_dragwindow->usehy - DISPLAYSLIDERSIZE -
			(us_dragwindow->thumbhy + (us_lastcury-us_initthumbcoord))) *
				us_dragnodetotal / thumbarea;
		if (us_dragpoint < 0) us_dragpoint = 0;
	}
}

/* return method for reporting the vertical thumb in a simulation waveform window */
INTBIG us_svthumbend(void)
{
	return(us_dragpoint);
}

float us_dragmintime;

/* completion method for tracking the horizontal thumb in a simulation waveform window */
void us_shthumbdone(void)
{
	REGISTER INTBIG thumbarea;
	float mintime, maxtime, range;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_dragwindow->thumblx + (us_lastcurx-us_initthumbcoord)+1;
		us_dragpoly->yv[0] = us_trackww.usehy-3;
		us_dragpoly->xv[1] = us_dragwindow->thumbhx + (us_lastcurx-us_initthumbcoord)-1;
		us_dragpoly->yv[1] = us_trackww.usehy-DISPLAYSLIDERSIZE+1;
		us_dragpoly->count = 2;
		us_dragpoly->style = FILLEDRECT;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		thumbarea = us_dragwindow->usehx - us_dragwindow->uselx - DISPLAYSLIDERSIZE*2;
		sim_window_gettimerange(&mintime, &maxtime);
		range = maxtime * 2.0f;
		us_dragmintime = (us_dragwindow->thumblx + (us_lastcurx-us_initthumbcoord) -
			(us_dragwindow->uselx+DISPLAYSLIDERSIZE+2)) * range / thumbarea;
		if (us_dragmintime < 0.0) us_dragmintime = 0.0;
	}
}

/* return method for reporting the horizontal thumb in a simulation waveform window */
float us_shthumbend(void)
{
	return(us_dragmintime);
}

/************************* WINDOW PARTITION DIVIDER TRACKING *************************/

void us_vpartdividerbegin(INTBIG x, INTBIG ly, INTBIG hy, WINDOWFRAME *wf)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_initthumbcoord = x;
	us_draglowval = ly;
	us_draghighval = hy;

	us_trackww.screenlx = us_trackww.uselx = 0;
	us_trackww.screenhx = us_trackww.usehx = wf->swid;
	us_trackww.screenly = us_trackww.usely = 0;
	us_trackww.screenhy = us_trackww.usehy = wf->shei;
	us_trackww.frame = wf;
	us_trackww.state = DISPWINDOW;
	computewindowscale(&us_trackww);
}

INTSML us_vpartdividerdown(INTBIG x, INTBIG y)
{
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (x == us_lastcurx) return(0);

		/* undraw the line */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_lastcurx;
		us_dragpoly->yv[0] = us_draglowval;
		us_dragpoly->xv[1] = us_lastcurx;
		us_dragpoly->yv[1] = us_draghighval;
		us_dragpoly->count = 2;
		us_dragpoly->style = VECTORS;
		(void)us_showpoly(us_dragpoly, &us_trackww);
	}

	us_lastcurx = x;
	if (us_lastcurx >= us_trackww.usehx) us_lastcurx = us_trackww.usehx-1;
	if (us_lastcurx < us_trackww.uselx) us_lastcurx = us_trackww.uselx;

	/* draw the outline */
	us_highl.col = HIGHLIT;
	us_dragpoly->xv[0] = us_lastcurx;
	us_dragpoly->yv[0] = us_draglowval;
	us_dragpoly->xv[1] = us_lastcurx;
	us_dragpoly->yv[1] = us_draghighval;
	us_dragpoly->count = 2;
	us_dragpoly->style = VECTORS;
	(void)us_showpoly(us_dragpoly, &us_trackww);

	us_dragshown = 1;
	return(0);
}

void us_vpartdividerdone(void)
{
	INTBIG lx, hx, ly, hy;
	REGISTER WINDOWPART *w;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_lastcurx;
		us_dragpoly->yv[0] = us_draglowval;
		us_dragpoly->xv[1] = us_lastcurx;
		us_dragpoly->yv[1] = us_draghighval;
		us_dragpoly->count = 2;
		us_dragpoly->style = VECTORS;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		/* adjust the screen */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if (w->frame != us_trackww.frame) continue;
			if (strcmp(w->location, "entire") == 0) continue;

			us_gettruewindowbounds(w, &lx, &hx, &ly, &hy);
			lx--;   hx++;
			ly--;   hy++;
			if (us_initthumbcoord >= lx-2 && us_initthumbcoord <= lx+2)
			{
				w->hratio = (INTSML)(((hx - us_lastcurx) * w->hratio + (hx - lx)/2) / (hx - lx));
				if (w->hratio > 95)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(0);
					return;
				}
				if (w->hratio < 5)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(1);
					return;
				}
			}
			if (us_initthumbcoord >= hx-2 && us_initthumbcoord <= hx+2)
			{
				w->hratio = (INTSML)(((us_lastcurx - lx) * w->hratio + (hx - lx)/2) / (hx - lx));
				if (w->hratio > 95)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(0);
					return;
				}
				if (w->hratio < 5)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(1);
					return;
				}
			}
		}
		us_beginchanges();
		us_drawmenu(1, us_trackww.frame);
		us_endchanges(NOWINDOWPART);
		us_state |= HIGHLIGHTSET;
		us_showallhighlight();
	}
}

void us_hpartdividerbegin(INTBIG y, INTBIG lx, INTBIG hx, WINDOWFRAME *wf)
{
	/* initialize polygon */
	if (us_dragpoly == NOPOLYGON) us_dragpoly = allocpolygon(4, us_aid->cluster);
	us_dragpoly->desc = &us_highl;

	us_dragshown = 0;
	us_initthumbcoord = y;
	us_draglowval = lx;
	us_draghighval = hx;

	us_trackww.screenlx = us_trackww.uselx = 0;
	us_trackww.screenhx = us_trackww.usehx = wf->swid;
	us_trackww.screenly = us_trackww.usely = 0;
	us_trackww.screenhy = us_trackww.usehy = wf->shei;
	us_trackww.frame = wf;
	us_trackww.state = DISPWINDOW;
	computewindowscale(&us_trackww);
}

INTSML us_hpartdividerdown(INTBIG x, INTBIG y)
{
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (y == us_lastcury) return(0);

		/* undraw the line */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_draglowval;
		us_dragpoly->yv[0] = us_lastcury;
		us_dragpoly->xv[1] = us_draghighval;
		us_dragpoly->yv[1] = us_lastcury;
		us_dragpoly->count = 2;
		us_dragpoly->style = VECTORS;
		(void)us_showpoly(us_dragpoly, &us_trackww);
	}

	us_lastcury = y;
	if (us_lastcury >= us_trackww.usehy) us_lastcury = us_trackww.usehy-1;
	if (us_lastcury < us_trackww.usely) us_lastcury = us_trackww.usely;

	/* draw the outline */
	us_highl.col = HIGHLIT;
	us_dragpoly->xv[0] = us_draglowval;
	us_dragpoly->yv[0] = us_lastcury;
	us_dragpoly->xv[1] = us_draghighval;
	us_dragpoly->yv[1] = us_lastcury;
	us_dragpoly->count = 2;
	us_dragpoly->style = VECTORS;
	(void)us_showpoly(us_dragpoly, &us_trackww);

	us_dragshown = 1;
	return(0);
}

void us_hpartdividerdone(void)
{
	INTBIG lx, hx, ly, hy;
	REGISTER WINDOWPART *w;

	if (us_dragshown != 0)
	{
		/* undraw the outline */
		us_highl.col = 0;
		us_dragpoly->xv[0] = us_draglowval;
		us_dragpoly->yv[0] = us_lastcury;
		us_dragpoly->xv[1] = us_draghighval;
		us_dragpoly->yv[1] = us_lastcury;
		us_dragpoly->count = 2;
		us_dragpoly->style = VECTORS;
		(void)us_showpoly(us_dragpoly, &us_trackww);

		/* adjust the screen */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if (w->frame != us_trackww.frame) continue;
			if (strcmp(w->location, "entire") == 0) continue;

			us_gettruewindowbounds(w, &lx, &hx, &ly, &hy);
			lx--;   hx++;
			ly--;   hy++;
			if (us_initthumbcoord >= ly-2 && us_initthumbcoord <= ly+2)
			{
				w->vratio = (INTSML)(((hy - us_lastcury) * w->vratio + (hy - ly)/2) / (hy - ly));
				if (w->vratio > 95)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(0);
					return;
				}
				if (w->vratio < 5)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(1);
					return;
				}
			}
			if (us_initthumbcoord >= hy-2 && us_initthumbcoord <= hy+2)
			{
				w->vratio = (INTSML)(((us_lastcury - ly) * w->vratio + (hy - ly)/2) / (hy - ly));
				if (w->vratio > 95)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(0);
					return;
				}
				if (w->vratio < 5)
				{
					el_curwindowpart = w;
					us_killcurrentwindow(1);
					return;
				}
			}
		}
		us_beginchanges();
		us_drawmenu(1, us_trackww.frame);
		us_endchanges(NOWINDOWPART);
		us_state |= HIGHLIGHTSET;
		us_showallhighlight();
	}
}

/************************* MISCELLANEOUS *************************/

/* cursor advance routine when stretching the highlight object */
INTSML us_stretchdown(INTBIG x, INTBIG y)
{
	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_invertstretch();
	}

	/* advance the box */
	maketruerectpoly(us_dragox, us_lastcurx, us_dragoy, us_lastcury, us_dragpoly);
	us_dragpoly->style = CLOSEDRECT;
	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_invertstretch();
	us_dragshown = 1;
	return(0);
}

void us_invertstretch(void)
{
	us_wanttoinvert(us_dragox, us_dragoy, us_dragox, us_dragy,  us_dragwindow);
	us_wanttoinvert(us_dragox, us_dragy,  us_dragx,  us_dragy,  us_dragwindow);
	us_wanttoinvert(us_dragx,  us_dragy,  us_dragx,  us_dragoy, us_dragwindow);
	us_wanttoinvert(us_dragx,  us_dragoy, us_dragox, us_dragoy, us_dragwindow);
}

void us_wanttoinvert(INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, WINDOWPART *w)
{
	fx = applyxscale(w, fx-w->screenlx) + w->uselx;
	fy = applyyscale(w, fy-w->screenly) + w->usely;
	tx = applyxscale(w, tx-w->screenlx) + w->uselx;
	ty = applyyscale(w, ty-w->screenly) + w->usely;
	if (clipline(&fx, &fy, &tx, &ty, w->uselx, w->usehx, w->usely, w->usehy) != 0) return;
	screeninvertline(w, (INTSML)fx, (INTSML)fy, (INTSML)tx, (INTSML)ty);
}

/* cursor advance routine when creating or moving an object */
INTSML us_dragdown(INTBIG x, INTBIG y)
{
	REGISTER INTSML i;

	if (el_pleasestop != 0) return(1);

	/* grid align the cursor value */
	if (us_setxy((INTSML)x, (INTSML)y) != 0 || us_dragwindow != el_curwindowpart)
	{
		(void)us_setxy((INTSML)us_lastcurx, (INTSML)us_lastcury);
		return(0);
	}
	(void)getxy(&us_lastcurx, &us_lastcury);
	gridalign(&us_lastcurx, &us_lastcury, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, us_lastcurx, us_lastcury);

	/* if the box is already being shown, erase it */
	if (us_dragshown != 0)
	{
		/* if it didn't actually move, go no further */
		if (us_dragx == us_lastcurx && us_dragy == us_lastcury) return(0);

		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}

	/* advance the box */
	for(i = 0; i < us_dragpoly->count; i++)
	{
		us_dragpoly->xv[i] += us_lastcurx - us_dragx;
		us_dragpoly->yv[i] += us_lastcury - us_dragy;
	}
	us_dragx = us_lastcurx;
	us_dragy = us_lastcury;

	/* draw the new box */
	us_highl.col = HIGHLIT;
	(void)us_showpoly(us_dragpoly, us_dragwindow);
	us_dragshown = 1;
	return(0);
}

/* termination routine when stretching an area */
void us_invertdragup(void)
{
	if (us_dragshown != 0) us_invertstretch();
}

/* termination routine when creating or moving an object */
void us_dragup(void)
{
	if (us_dragshown != 0)
	{
		/* undraw the box */
		us_highl.col = 0;
		(void)us_showpoly(us_dragpoly, us_dragwindow);
	}
}

INTSML us_nullup(INTBIG x, INTBIG y)  { return(0); }

void us_nullvoid(void) {}

INTSML us_nullchar(INTBIG x, INTBIG y, INTSML ch) { return(0); }

/*
 * routine to ignore all cursor motion while the button is up
 */
INTSML us_ignoreup(INTBIG x, INTBIG y)
{
	gridalign(&x, &y, us_alignment);
	us_setcursorpos(NOWINDOWFRAME, x, y);
	return(0);
}

/*
 * routine to cause termination of tracking when a key is typed
 */
INTSML us_stoponchar(INTBIG x, INTBIG y, INTSML chr)
{
	if (chr == 'a' || chr == 'A')
	{
		el_pleasestop = 1;
		stopping("Tracking");
	}
	return(1);
}

/*
 * routine to cause termination of tracking and pop stacked highlighting
 * when a key is typed
 */
INTSML us_stopandpoponchar(INTBIG x, INTBIG y, INTSML chr)
{
	if (chr == 'a' || chr == 'A')
	{
		el_pleasestop = 1;
		stopping("Tracking");
		(void)us_pophighlight(0);
	}
	return(1);
}
