/*
 * Electric(tm) VLSI Design System
 *
 * File: usrcomtz.c
 * User interface aid: command handler for W through Z
 * 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
 */

#include "global.h"
#include "egraphics.h"
#include "usr.h"
#include "usrtrack.h"
#include "efunction.h"

struct
{
	char         *keyword;
	short         unique;
	STATUSFIELD **fieldaddr;
} us_statusfields[] =
{
	{"align",      2,  &us_statusalign},
	{"angle",      2,  &us_statusangle},
	{"arc",        2,  &us_statusarc},
	{"facet",      1,  &us_statusfacet},
	{"grid",       1,  &us_statusgridsize},
	{"lambda",     1,  &us_statuslambda},
	{"network",    2,  &us_statusnetwork},
	{"node",       2,  &us_statusnode},
	{"package",    3,  &us_statuspackage},
	{"part",       3,  &us_statuspart},
	{"project",    2,  &us_statusproject},
	{"root",       1,  &us_statusroot},
	{"selection",  2,  &us_statusselection},
	{"size",       2,  &us_statusfacetsize},
	{"technology", 1,  &us_statustechnology},
	{"x",          1,  &us_statusxpos},
	{"y",          1,  &us_statusypos},
	{0,0,0}
};

void us_window(INTSML count, char *par[])
{
	REGISTER WINDOWPART *w, *oldw, *nextw, *neww;
	REGISTER INTBIG i, dist, curwx, curwy, size, x, y, diffx, diffy;
	REGISTER INTSML l, nogood, splitkey, lineno, startper, endper;
	INTBIG lx, hx, ly, hy, xcur, ycur, schx, schy, windowView[8];
	static POLYGON *poly = NOPOLYGON;
	REGISTER STATUSFIELD *sf, **whichstatus;
	char *newpar[4], *fieldname;
	WINDOWFRAME *wf;
#if SIMAID
	extern AIDENTRY *sim_aid;
#endif
	REGISTER char *pp, *win;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np;
	extern GRAPHICS us_arbit;
	extern COMCOMP us_windowp, us_windowup, us_windowmp;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);

	if (count == 0)
	{
		count = ttygetparam("Window configuration: ", &us_windowp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}
	l = (INTSML)strlen(pp = par[0]);

	if (namesamen(pp, "all-displayed", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "zoom";
			newpar[2] = "all-displayed";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* fill a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dfillview(el_curwindowpart);
			return;
		}

		/* use direct methods on nonstandard windows */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			if (el_curwindowpart->redisphandler != 0)
				(*el_curwindowpart->redisphandler)(el_curwindowpart);
			return;
		}

		np = us_needfacet();
		if (np == NONODEPROTO) return;

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* make the facet fill the window */
		us_fullview(np, &lx, &hx, &ly, &hy);
		us_squarescreen(el_curwindowpart, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "center-highlight", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;

		np = us_getareabounds(&lx, &hx, &ly, &hy);
		if (np == NONODEPROTO)
		{
			us_abortcommand("Outline an area");
			return;
		}

		if (el_curwindowpart->curnodeproto == np) w = el_curwindowpart; else
		{
			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
				if (w->curnodeproto == np && (w->state&WINDOWTYPE) == DISPWINDOW)
					break;
			if (w == NOWINDOWPART)
			{
				us_abortcommand("Cannot find an editing window with highlighted objects");
				return;
			}
		}

		/* cannot manipulate a nonstandard window */
		if ((w->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		/* pre-compute current window size */
		curwx = w->screenhx - w->screenlx;
		curwy = w->screenhy - w->screenly;

		/* center about this area without re-scaling */
		x = (hx + lx) / 2;     y = (hy + ly) / 2;
		lx = x - curwx/2;      ly = y - curwy/2;
		hx = lx + curwx;       hy = ly + curwy;

		/* limit schematic windows to their normal extent */
		if (framesize(&schx, &schy, np) == 0)
		{
			if (lx < -schx/2) { lx = -schx/2;   hx = lx + curwx; }
			if (hx >  schx/2) { hx =  schx/2;   lx = hx - curwx; }
			if (ly < -schy/2) { ly = -schy/2;   hy = ly + curwy; }
			if (hy >  schy/2) { hy =  schy/2;   ly = hy - curwy; }
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		startobjectchange((INTBIG)w, VWINDOWPART);
		(void)setval((INTBIG)w, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(w, el_curwindowpart->state);
		endobjectchange((INTBIG)w, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "cursor-centered", l) == 0 && l >= 2)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;

		/* cannot manipulate a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		if (us_demandxy(&xcur, &ycur)) return;

		/* pre-compute current window size */
		curwx = el_curwindowpart->screenhx - el_curwindowpart->screenlx;
		curwy = el_curwindowpart->screenhy - el_curwindowpart->screenly;

		lx = xcur - curwx/2;   ly = ycur - curwy/2;
		hx = lx + curwx;       hy = ly + curwy;

		/* limit schematic windows to their normal extent */
		if (framesize(&schx, &schy, np) == 0)
		{
			if (lx < -schx/2) { lx = -schx/2;   hx = lx + curwx; }
			if (hx >  schx/2) { hx =  schx/2;   lx = hx - curwx; }
			if (ly < -schy/2) { ly = -schy/2;   hy = ly + curwy; }
			if (hy >  schy/2) { hy =  schy/2;   ly = hy - curwy; }
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	/* handle deletion of separate windows */
	if (namesamen(pp, "delete", l) == 0 && l >= 2)
	{
		/* close the messages window if in front and can be closed */
		if (closefrontmostmessages() != 0) return;

		/* delete split if there are no multiple window frames */
		if (graphicshas(CANUSEFRAMES) == 0)
		{
			if (us_needwindow()) return;
			us_killcurrentwindow(1);
			return;
		}

		/* disallow if this is the last window and it must remain */
		if (graphicshas(CANHAVENOWINDOWS) == 0)
		{
			/* disallow deletion if this is the last window */
			i = 0;
			for(wf = el_firstwindowframe; wf != NOWINDOWFRAME; wf = wf->nextwindowframe)
				if (wf->floating == 0) i++;
			if (i <= 1)
			{
				ttyputerr("Sorry, cannot delete the last window");
				return;
			}
		}

		/* get the current frame */
		wf = getwindowframe(0);
		if (wf == NOWINDOWFRAME)
		{
			us_abortcommand("No current window to delete");
			return;
		}

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

		startobjectchange((INTBIG)us_aid, VAID);

		/* kill all editor windows on this frame */
		neww = NOWINDOWPART;
		for(w = el_topwindowpart; w != NOWINDOWPART; w = nextw)
		{
			nextw = w->nextwindowpart;
			if (w->frame != wf)
			{
				neww = w;
				continue;
			}

			/* kill this window */
			killwindowpart(w);
		}
		endobjectchange((INTBIG)us_aid, VAID);

		/* if (neww == NOWINDOWPART) */
		el_curwindowpart = NOWINDOWPART;
		(void)setvalkey((INTBIG)us_aid, VAID, us_current_window, (INTBIG)neww, VWINDOWPART|VDONTSAVE);
		if (neww != NOWINDOWPART) np = neww->curnodeproto; else np = NONODEPROTO;
		(void)setval((INTBIG)el_curlib, VLIBRARY, "curnodeproto", (INTBIG)np, VNODEPROTO);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "down", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "move";
			newpar[2] = "down";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* pan a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dpanview(el_curwindowpart, 0, 1);
			return;
		}

		/* cannot pan a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		if (us_needfacet() == NONODEPROTO) return;
		if (count < 2) pp = "0.5"; else pp = par[1];
		if (pp[strlen(pp)-1] == 'l') dist = atola(pp); else
			dist = muldiv(atofr(pp), (el_curwindowpart->screenhy - el_curwindowpart->screenly), WHOLE);
		us_slideup(-dist);
		return;
	}

	if (namesamen(pp, "dragging", l) == 0 && l >= 3)
	{
		if (count >= 2)
		{
			pp = par[1];
			if (namesame(pp, "on") == 0)
				(void)setval((INTBIG)us_aid, VAID, "aidstate", us_aid->aidstate | INTERACTIVE, VINTEGER);
			else if (namesamen(pp, "of", 2) == 0)
				(void)setval((INTBIG)us_aid, VAID, "aidstate", us_aid->aidstate & ~INTERACTIVE, VINTEGER);
			else
			{
				us_abortcommand("Usage: window dragging on|off");
				return;
			}
		}
		if ((us_aid->aidstate&INTERACTIVE) == 0)
			ttyputverbose("Cursor-based commands will act immediately"); else
				ttyputverbose("Cursor-based commands will drag their objects");
		return;
	}

	if (namesamen(pp, "explore", l) == 0)
	{
		extern INTBIG us_explorefirstline;

		/* see if the explorer window is already there */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
			if ((w->state&WINDOWTYPE) == EXPLORERWINDOW) break;
		if (w != NOWINDOWPART)
		{
			/* if the explorer shares a window, delete it */
			el_curwindowpart = w;
			us_killcurrentwindow(1);
			return;
		}

		/* make an explorer window */
		w = el_curwindowpart;
		if (w == NOWINDOWPART)
		{
			w = us_wantnewwindow(0);
			if (w == NOWINDOWPART)
			{
				us_abortcommand("Cannot create new window frame");
				return;
			}
		}
		if (strcmp(w->location, "entire") == 0)
		{
			w = us_splitcurrentwindow(2, 0);
			if (w == NOWINDOWPART) return;
		}

		/* find the other window */
		for(oldw = el_topwindowpart; oldw != NOWINDOWPART; oldw = oldw->nextwindowpart)
			if (oldw != w && oldw->frame == w->frame) break;

		us_explorefirstline = 0;
		us_createexplorerstruct();
		startobjectchange((INTBIG)w, VWINDOWPART);
		(void)setval((INTBIG)w, VWINDOWPART, "buttonhandler", (INTBIG)us_explorebuttonhandler,
			VADDRESS);
		(void)setval((INTBIG)w, VWINDOWPART, "charhandler", (INTBIG)us_explorecharhandler,
			VADDRESS);
		(void)setval((INTBIG)w, VWINDOWPART, "redisphandler", (INTBIG)us_exploreredisphandler,
			VADDRESS);
		(void)setval((INTBIG)w, VWINDOWPART, "state",
			(w->state & ~(WINDOWTYPE|WINDOWSIMULATING)) | EXPLORERWINDOW, VINTEGER);
		endobjectchange((INTBIG)w, VWINDOWPART);

		if (oldw != NOWINDOWPART)
		{
			(void)setvalkey((INTBIG)us_aid, VAID, us_current_window, (INTBIG)oldw,
				VWINDOWPART|VDONTSAVE);
			us_setfacetname(oldw);
		}
		return;
	}

	if (namesamen(pp, "highlight-displayed", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "zoom";
			newpar[2] = "cursor";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		np = us_getareabounds(&lx, &hx, &ly, &hy);
		if (np == NONODEPROTO)
		{
			us_abortcommand("Outline an area");
			return;
		}

		if (el_curwindowpart->curnodeproto == np) w = el_curwindowpart; else
		{
			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
				if (w->curnodeproto == np && (w->state&WINDOWTYPE) == DISPWINDOW)
					break;
			if (w == NOWINDOWPART)
			{
				us_abortcommand("Cannot find an editing window with highlighted objects");
				return;
			}
		}

		/* cannot manipulate a nonstandard window */
		if ((w->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only adjust circuit editing windows");
			return;
		}

		if (lx == hx && ly == hy)
		{
			lx -= el_curtech->deflambda;
			hx += el_curtech->deflambda;
			ly -= el_curtech->deflambda;
			hy += el_curtech->deflambda;
		}

		/* pre-compute current window size */
		curwx = w->screenhx - w->screenlx;
		curwy = w->screenhy - w->screenly;

		/* limit schematic windows to their normal extent */
		i = framesize(&schx, &schy, np);
		if (i == 0)
		{
			if (lx < -schx/2) lx = -schx/2;
			if (hx > schx/2) hx = schx/2;
			if (ly < -schy/2) ly = -schy/2;
			if (hy > schy/2) hy = schy/2;
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* make sure the new window has square pixels */
		us_squarescreen(w, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);

		/* make sure squaring didn't push extent of schematic frame */
		if (i == 0)
		{
			if (lx < -schx/2)
			{
				hx -= schx/2 + lx;
				lx = -schx/2;
			}
			if (hx > schx/2)
			{
				lx -= hx - schx/2;
				hx = schx/2;
			}
			if (ly < -schy/2)
			{
				hy -= schy/2 + ly;
				ly = -schy/2;
			}
			if (hy > schy/2)
			{
				ly -= hy - schy/2;
				hy = schy/2;
			}
		}

		startobjectchange((INTBIG)w, VWINDOWPART);
		(void)setval((INTBIG)w, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)w, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(w, el_curwindowpart->state);
		endobjectchange((INTBIG)w, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "in-zoom", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "zoom";
			newpar[2] = "in";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* zoom a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dzoomview(el_curwindowpart, 0.75f);
			return;
		}

		/* cannot zoom a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only zoom circuit editing windows");
			return;
		}

		if (us_needfacet() == NONODEPROTO) return;
		if (count >= 2) dist = atola(par[1]); else
			dist = 2 * el_curtech->deflambda;
		if (dist == 0)
		{
			us_abortcommand("Must zoom by a nonzero amount");
			return;
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		diffx = muldiv(el_curwindowpart->screenhx - el_curwindowpart->screenlx,
			el_curtech->deflambda, dist);
		diffy = muldiv(el_curwindowpart->screenhy - el_curwindowpart->screenly,
			el_curtech->deflambda, dist);
		lx = el_curwindowpart->screenlx;   hx = el_curwindowpart->screenhx;
		ly = el_curwindowpart->screenly;   hy = el_curwindowpart->screenhy;
		lx = (hx+lx-diffx)/2;   hx = lx + diffx;
		ly = (hy+ly-diffy)/2;   hy = ly + diffy;
		us_squarescreen(el_curwindowpart, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	/* handle killing of the other window specially */
	if (namesamen(pp, "join", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;
		us_killcurrentwindow(0);
		return;
	}

	/* handle killing of this window specially */
	if (namesamen(pp, "kill", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;
		us_killcurrentwindow(1);
		return;
	}

	if (namesamen(pp, "left", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "move";
			newpar[2] = "left";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* pan a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dpanview(el_curwindowpart, -1, 0);
			return;
		}

		/* cannot pan a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		if (us_needfacet() == NONODEPROTO) return;
		if (count < 2) pp = "0.5"; else pp = par[1];
		if (pp[strlen(pp)-1] == 'l') dist = atola(pp); else
			dist = muldiv(atofr(pp), (el_curwindowpart->screenhx - el_curwindowpart->screenlx), WHOLE);
		us_slideleft(dist);
		return;
	}

	if (namesamen(pp, "match", l) == 0 && l >= 3)
	{
		/* count the number of windows */
		for(i = 0, w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart) i++;
		if (i <= 1)
		{
			us_abortcommand("Must be multiple windows to match them");
			return;
		}

		/* if there are two windows, the other to match is obvious */
		if (i == 2)
		{
			if (el_curwindowpart == el_topwindowpart) w = el_topwindowpart->nextwindowpart; else
				w = el_topwindowpart;
		} else
		{
			if (count < 2)
			{
				count = ttygetparam("Other window to match: ", &us_windowmp, MAXPARS-1, &par[1]) + 1;
				if (count == 1)
				{
					us_abortedmsg();
					return;
				}
			}
			win = par[1];
			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
			{
				(void)initinfstr();
				(void)addstringtoinfstr(describenodeproto(w->curnodeproto));
				(void)addtoinfstr('(');
				(void)addstringtoinfstr(w->location);
				(void)addtoinfstr(')');
				if (namesame(win, returninfstr()) == 0) break;
			}
			if (w == NOWINDOWPART)
			{
				us_abortcommand("No window named '%s'", win);
				return;
			}
		}

		if (w == el_curwindowpart)
		{
			us_abortcommand("Choose a window other than the current one to match");
			return;
		}

		/* cannot match if they are editing the same thing */
		if ((w->state&WINDOWTYPE) != (el_curwindowpart->state&WINDOWTYPE))
		{
			us_abortcommand("Can only match windows that edit the same thing");
			return;
		}

		/* cannot match if they are not normal display windows */
		if ((w->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only match normal editing windows");
			return;
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* make window "el_curwindowpart" match the scale of "w" */
		diffx = muldiv(w->screenhx - w->screenlx, el_curwindowpart->usehx -
			el_curwindowpart->uselx, w->usehx - w->uselx);
		diffx = diffx - (el_curwindowpart->screenhx - el_curwindowpart->screenlx);
		diffy = muldiv(w->screenhy - w->screenly, el_curwindowpart->usehy -
			el_curwindowpart->usely, w->usehy - w->usely);
		diffy = diffy - (el_curwindowpart->screenhy - el_curwindowpart->screenly);

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx",
			el_curwindowpart->screenlx - diffx/2, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx",
			el_curwindowpart->screenhx + diffx/2, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly",
			el_curwindowpart->screenly - diffy/2, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy",
			el_curwindowpart->screenhy + diffy/2, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "measure", l) == 0 && l >= 2)
	{
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only measure distance in an edit window");
			return;
		}
		if (el_curwindowpart->curnodeproto == NONODEPROTO)
		{
			us_abortcommand("No facet in this window to measure");
			return;
		}
		us_clearhighlightcount();
		ttyputmsg("Type ENTER when done measuring distances");
		ttyputmsg("Type 'x' to set a new starting point");
		us_distanceinit();
		for(;;)
		{
			trackcursor(1, us_ignoreup, us_distancebegin, us_distancedown,
				us_distancechar, us_distanceup, TRACKDRAGGING);
			if (us_distanceaborted() != 0) break;
		}
		return;
	}

	if (namesamen(pp, "name", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;
		if (count <= 1)
		{
			us_abortcommand("Usage: window name VIEWNAME");
			return;
		}
		(void)initinfstr();
		(void)addstringtoinfstr("USER_windowview_");
		(void)addstringtoinfstr(par[1]);
		var = getval((INTBIG)us_aid, VAID, VINTEGER|VISARRAY, returninfstr());
		if (var == NOVARIABLE)
		{
			us_abortcommand("Cannot find saved window view '%s'", par[1]);
			return;
		}

		lx = ((INTBIG *)var->addr)[0];
		hx = ((INTBIG *)var->addr)[1];
		ly = ((INTBIG *)var->addr)[2];
		hy = ((INTBIG *)var->addr)[3];

		/* if the window extent changed, make sure the pixels are square */
		if (((INTBIG *)var->addr)[5] - ((INTBIG *)var->addr)[4] != el_curwindowpart->usehx - el_curwindowpart->uselx ||
			((INTBIG *)var->addr)[7] - ((INTBIG *)var->addr)[6] != el_curwindowpart->usehy - el_curwindowpart->usely)
		{
			us_squarescreen(el_curwindowpart, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	/* handle creating of separate windows */
	if (namesamen(pp, "new", l) == 0 && l >= 2)
	{
		/* create a new frame */
		w = us_wantnewwindow(0);
		if (w == NOWINDOWPART)
		{
			us_abortcommand("Cannot create new window frame");
			return;
		}
		return;
	}

	if (namesamen(pp, "normal-cursor", l) == 0 && l >= 2)
	{
		if (count < 2)
		{
			us_abortcommand("Usage: window normal-cursor CURSORNAME");
			return;
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "standard", l) == 0) setnormalcursor(NORMALCURSOR); else
			if (namesamen(pp, "pen", l) == 0) setnormalcursor(PENCURSOR); else
				if (namesamen(pp, "tee", l) == 0) setnormalcursor(TECHCURSOR); else
					us_abortcommand("Bad window cursor option: %s", pp);
		return;
	}

	if (namesamen(pp, "out-zoom", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "zoom";
			newpar[2] = "out";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* zoom a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dzoomview(el_curwindowpart, 1.5f);
			return;
		}

		/* cannot zoom a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only zoom circuit editing windows");
			return;
		}

		np = us_needfacet();
		if (np == NONODEPROTO) return;
		if (count >= 2) dist = atola(par[1]); else
			dist = 2 * el_curtech->deflambda;
		if (dist == 0)
		{
			us_abortcommand("Must zoom by a nonzero amount");
			return;
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		lx = el_curwindowpart->screenlx;   hx = el_curwindowpart->screenhx;
		ly = el_curwindowpart->screenly;   hy = el_curwindowpart->screenhy;
		i = muldiv(el_curwindowpart->screenhx - el_curwindowpart->screenlx, dist, el_curtech->deflambda);
		lx = (lx+hx-i)/2;   hx = lx + i;
		i = muldiv(el_curwindowpart->screenhy - el_curwindowpart->screenly, dist, el_curtech->deflambda);
		ly = (ly+hy-i)/2;   hy = ly + i;

		/* limit schematic windows to their normal extent */
		i = framesize(&schx, &schy, np);
		if (i == 0)
		{
			if (lx < -schx/2) lx = -schx/2;
			if (hx > schx/2) hx = schx/2;
			if (ly < -schy/2) ly = -schy/2;
			if (hy > schy/2) hy = schy/2;
		}
		us_squarescreen(el_curwindowpart, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);

		/* make sure squaring didn't push extent of schematic frame */
		if (i == 0)
		{
			if (lx < -schx/2)
			{
				hx -= schx/2 + lx;
				lx = -schx/2;
			}
			if (hx > schx/2)
			{
				lx -= hx - schx/2;
				hx = schx/2;
			}
			if (ly < -schy/2)
			{
				hy -= schy/2 + ly;
				ly = -schy/2;
			}
			if (hy > schy/2)
			{
				ly -= hy - schy/2;
				hy = schy/2;
			}
		}

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "overlappable-display", l) == 0 && l >= 5)
	{
		if (count >= 2)
		{
			pp = par[1];
			l = strlen(pp);
			if (namesamen(pp, "on", l) == 0)
			{
				us_state &= ~NONOVERLAPPABLEDISPLAY;
			} else if (namesamen(pp, "off", l) == 0)
			{
				us_state |= NONOVERLAPPABLEDISPLAY;
			} else
			{
				us_abortcommand("Usage: window overlappable-display [on|off]");
				return;
			}
		}
		if ((us_state&NONOVERLAPPABLEDISPLAY) != 0)
			ttyputverbose("Overlappable layers will not be handled"); else
				ttyputverbose("Overlappable layers will be drawn properly");
		return;
	}

	if (namesamen(pp, "overview", l) == 0 && l >= 5)
	{
		ttyputerr("Cannot make an overview window yet");
		return;
	}

	if (namesamen(pp, "peek", l) == 0 && l >= 2)
	{
		np = us_getareabounds(&lx, &hx, &ly, &hy);
		if (np == NONODEPROTO)
		{
			us_abortcommand("Enclose an area to be peeked");
			return;
		}

		if (el_curwindowpart->curnodeproto == np) w = el_curwindowpart; else
		{
			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
				if (w->curnodeproto == np && (w->state&WINDOWTYPE) == DISPWINDOW)
					break;
			if (w == NOWINDOWPART)
			{
				us_abortcommand("Cannot find an editing window with highlighted objects");
				return;
			}
		}

		/* clip this bounding box to the window extent */
		lx = maxi(lx, w->screenlx);
		hx = mini(hx, w->screenhx);
		ly = maxi(ly, w->screenly);
		hy = mini(hy, w->screenhy);

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* un-draw the peek area */
		maketruerectpoly(lx, hx, ly, hy, poly);
		poly->desc = &us_arbit;
		us_arbit.col = 0;
		us_arbit.bits = LAYERO;
		poly->style = FILLEDRECT;
		(void)us_showpoly(poly, w);

		/* get new window to describe sub-area */
		w = us_subwindow(lx, hx, ly, hy, w);

		/* do the peek operation */
		us_dopeek(lx, hx, ly, hy, np, w);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "right", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "move";
			newpar[2] = "right";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* pan a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dpanview(el_curwindowpart, 1, 0);
			return;
		}

		/* cannot pan a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		if (us_needfacet() == NONODEPROTO) return;
		if (count < 2) pp = "0.5"; else pp = par[1];
		if (pp[strlen(pp)-1] == 'l') dist = atola(pp); else
			dist = muldiv(atofr(pp), (el_curwindowpart->screenhx - el_curwindowpart->screenlx), WHOLE);
		us_slideleft(-dist);
		return;
	}

	if (namesamen(pp, "save", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;
		if (count <= 1)
		{
			us_abortcommand("Usage: window save VIEWNAME");
			return;
		}
		windowView[0] = el_curwindowpart->screenlx;
		windowView[1] = el_curwindowpart->screenhx;
		windowView[2] = el_curwindowpart->screenly;
		windowView[3] = el_curwindowpart->screenhy;
		windowView[4] = el_curwindowpart->uselx;
		windowView[5] = el_curwindowpart->usehx;
		windowView[6] = el_curwindowpart->usely;
		windowView[7] = el_curwindowpart->usehy;
		(void)initinfstr();
		(void)addstringtoinfstr("USER_windowview_");
		(void)addstringtoinfstr(par[1]);
		(void)setval((INTBIG)us_aid, VAID, returninfstr(), (INTBIG)windowView,
			VINTEGER|VISARRAY|(8<<VLENGTHSH)|VDONTSAVE);
		ttyputverbose("Window view %s saved", par[1]);
		return;
	}

	if (namesamen(pp, "split", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;
		splitkey = 0;
		if (count > 1)
		{
			l = strlen(pp = par[1]);
			if (namesamen(pp, "horizontal", l) == 0 && l >= 1) splitkey = 1; else
				if (namesamen(pp, "vertical", l) == 0 && l >= 1) splitkey = 2; else
			{
				us_abortcommand("Usage: window split horizontal|vertical");
				return;
			}
		}

		/* split the window */
		(void)us_splitcurrentwindow(splitkey, 1);
		return;
	}

	if (namesamen(pp, "status-bar", l) == 0 && l >= 2)
	{
		if (count < 2)
		{
			/* report all status bar locations */
			for(i=0; us_statusfields[i].fieldaddr != 0; i++)
			{
				sf = *us_statusfields[i].fieldaddr;
				if (sf == 0) continue;
				if (sf->line == 0)
					ttyputmsg("Window title has %s", us_statusfields[i].fieldaddr); else
						ttyputmsg("Line %d from %3d%% to %3d%% is %s", sf->line, sf->startper,
							sf->endper, us_statusfields[i].fieldaddr);
			}
			return;
		}

		if (namesamen(par[1], "current-node", strlen(par[1])) == 0)
		{
			if (count < 3)
			{
				if ((us_state&NONPERSISTENTCURNODE) != 0)
					ttyputmsg("Current node displayed temporarily"); else
						ttyputmsg("Current node display is persistent");
				return;
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "persistent", l) == 0)
			{
				us_state &= ~NONPERSISTENTCURNODE;
				ttyputverbose("Current node display is persistent");
				return;
			}
			if (namesamen(pp, "temporary", l) == 0)
			{
				us_state |= NONPERSISTENTCURNODE;
				ttyputverbose("Current node displayed temporarily");
				return;
			}
			us_abortcommand("Usage: find current-node [persistent|temporary]");
			return;
		}

		if (count < 3)
		{
			us_abortcommand("Usage: window status-bar %s FIELD...", pp);
			return;
		}

		/* determine area being controlled */
		l = strlen(pp = par[2]);
		for(i=0; us_statusfields[i].keyword != 0; i++)
			if (namesamen(pp, us_statusfields[i].keyword, l) == 0 &&
				l >= us_statusfields[i].unique)
		{
			whichstatus = us_statusfields[i].fieldaddr;
			break;
		}
		if (us_statusfields[i].keyword == 0)
		{
			us_abortcommand("Unknown status-bar location: %s", pp);
			return;
		}

		/* get option */
		l = strlen(pp = par[1]);
		if (namesamen(pp, "delete", l) == 0)
		{
			if (*whichstatus != 0) ttyfreestatusfield(*whichstatus);
			*whichstatus = 0;
			us_redostatus(NOWINDOWFRAME);
			return;
		}
		if (namesamen(pp, "add", l) == 0)
		{
			if (count < 6)
			{
				us_abortcommand("Usage: window status-bar add FIELD LINE STARTPER ENDPER [TITLE]");
				return;
			}
			lineno = atoi(par[3]);
			if (lineno < 0 || lineno > ttynumstatuslines())
			{
				us_abortcommand("Line number must range from 0 to %d", ttynumstatuslines());
				return;
			}
			startper = atoi(par[4]);
			if (startper < 0 || startper > 100)
			{
				us_abortcommand("Starting percentage must range from 0 to 100");
				return;
			}
			endper = atoi(par[5]);
			if (endper <= startper || endper > 100)
			{
				us_abortcommand("Ending percentage must range from %d to 100", startper+1);
				return;
			}
			if (count == 7) fieldname = par[6]; else fieldname = "";
			if (*whichstatus != 0) ttyfreestatusfield(*whichstatus);
			*whichstatus = ttydeclarestatusfield(lineno, startper, endper, fieldname);
			us_redostatus(NOWINDOWFRAME);
			return;
		}
		us_abortcommand("Usage: window status-bar [add | delete]");
		return;
	}

	if (namesamen(pp, "tiny-facets", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;
		if (count >= 2)
		{
			l = strlen(pp = par[1]);
			if (namesamen(pp, "draw", l) == 0)
			{
				startobjectchange((INTBIG)us_aid, VAID);
				(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
					us_useroptions | DRAWTINYFACETS, VINTEGER);
				if (count >= 3)
				{
					i = atofr(par[2]);
					(void)setvalkey((INTBIG)us_aid, VAID, us_tinylambdaperpixel,
						i, VFRACT);
				}
				endobjectchange((INTBIG)us_aid, VAID);
			} else if (namesamen(pp, "hash-out", l) == 0)
			{
				startobjectchange((INTBIG)us_aid, VAID);
				(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
					us_useroptions & ~DRAWTINYFACETS, VINTEGER);
				endobjectchange((INTBIG)us_aid, VAID);
			} else
			{
				us_abortcommand("Usage: window tiny-facets draw|(hash-out [LAMBDAPERPIXEL])");
				return;
			}
		}
		if ((us_useroptions&DRAWTINYFACETS) != 0)
		{
			ttyputverbose("Tiny facets will be drawn");
		} else
		{
			ttyputverbose("Tiny facets will be hashed-out after %s lambda per pixel",
				frtoa(us_tinyratio));
		}
		return;
	}

	if (namesamen(pp, "trace-displayed", l) == 0 && l >= 2)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;

		/* cannot manipulate a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only adjust circuit editing windows");
			return;
		}

		/* pre-compute current window size */
		curwx = el_curwindowpart->screenhx - el_curwindowpart->screenlx;
		curwy = el_curwindowpart->screenhy - el_curwindowpart->screenly;

		var = getval((INTBIG)us_aid, VAID, VINTEGER|VISARRAY, us_commandvarname('T'));
		if (var == NOVARIABLE)
		{
			us_abortcommand("Issue an outline before zooming into that area");
			return;
		}
		size = getlength(var) / 2;
		nogood = 0;
		for(i=0; i<size; i++)
		{
			x = ((INTBIG *)var->addr)[i*2];
			y = ((INTBIG *)var->addr)[i*2+1];
			nogood += us_setxy((INTSML)x, (INTSML)y);
			(void)getxy(&xcur, &ycur);
			if (i == 0)
			{
				lx = hx = xcur;   ly = hy = ycur;
			} else
			{
				lx = mini(lx, xcur);   hx = maxi(hx, xcur);
				ly = mini(ly, ycur);   hy = maxi(hy, ycur);
			}
		}
		if (nogood != 0)
		{
			us_abortcommand("Outline not inside window");
			return;
		}

		/* limit schematic windows to their normal extent */
		i = framesize(&schx, &schy, np);
		if (i == 0)
		{
			if (lx < -schx/2) lx = -schx/2;
			if (hx > schx/2) hx = schx/2;
			if (ly < -schy/2) ly = -schy/2;
			if (hy > schy/2) hy = schy/2;
		}

		/* save and erase highlighting */
		us_pushhighlight();
		us_clearhighlightcount();

		/* set the new window size */
		us_squarescreen(el_curwindowpart, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);

		/* make sure squaring didn't push extent of schematic frame */
		if (i == 0)
		{
			if (lx < -schx/2)
			{
				hx -= schx/2 + lx;
				lx = -schx/2;
			}
			if (hx > schx/2)
			{
				lx -= hx - schx/2;
				hx = schx/2;
			}
			if (ly < -schy/2)
			{
				hy -= schy/2 + ly;
				ly = -schy/2;
			}
			if (hy > schy/2)
			{
				ly -= hy - schy/2;
				hy = schy/2;
			}
		}

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenlx", lx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhx", hx, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenly", ly, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "screenhy", hy, VINTEGER);
		us_gridset(el_curwindowpart, el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "up", l) == 0 && l >= 2)
	{
		if (us_needwindow()) return;

#if SIMAID
		/* special case for waveform window */
		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			newpar[0] = "window";
			newpar[1] = "move";
			newpar[2] = "up";
			(void)tellaid(sim_aid, 3, newpar);
			return;
		}
#endif

		/* pan a 3D window */
		if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
		{
			us_3dpanview(el_curwindowpart, 0, -1);
			return;
		}

		/* cannot pan a nonstandard window */
		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
		{
			us_abortcommand("Can only pan circuit editing windows");
			return;
		}

		if (us_needfacet() == NONODEPROTO) return;
		if (count < 2) pp = "0.5"; else pp = par[1];
		if (pp[strlen(pp)-1] == 'l') dist = atola(pp); else
			dist = muldiv(atofr(pp), (el_curwindowpart->screenhy - el_curwindowpart->screenly), WHOLE);
		us_slideup(dist);
		return;
	}

	if (namesamen(pp, "use", l) == 0 && l >= 2)
	{
		if (count <= 1)
		{
			count = ttygetparam("Window to use: ", &us_windowup, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			(void)initinfstr();
			(void)addstringtoinfstr(describenodeproto(w->curnodeproto));
			(void)addtoinfstr('(');
			(void)addstringtoinfstr(w->location);
			(void)addtoinfstr(')');
			if (namesame(par[1], returninfstr()) == 0) break;
		}
		if (w == NOWINDOWPART)
		{
			us_abortcommand("No window named '%s'", par[1]);
			return;
		}
		(void)setvalkey((INTBIG)us_aid, VAID, us_current_window, (INTBIG)w, VWINDOWPART|VDONTSAVE);
		(void)setval((INTBIG)el_curlib, VLIBRARY, "curnodeproto", (INTBIG)w->curnodeproto, VNODEPROTO);
		return;
	}

	if (namesamen(pp, "zoom-scale", l) == 0 && l >= 1)
	{
		if (count >= 2)
		{
			l = strlen(pp = par[1]);
			if (namesamen(pp, "integral", l) == 0 && l >= 1)
				(void)setval((INTBIG)us_aid, VAID, "aidstate", us_aid->aidstate | INTEGRAL, VINTEGER);
			else if (namesamen(pp, "nonintegral", l) == 0 && l >= 1)
				(void)setval((INTBIG)us_aid, VAID, "aidstate", us_aid->aidstate & ~INTEGRAL, VINTEGER);
			else
			{
				us_abortcommand("Usage: window zoom-scale integral|nonintegral");
				return;
			}
		}
		if ((us_aid->aidstate&INTEGRAL) == 0)
			ttyputverbose("Window scaling will be continuous"); else
				ttyputverbose("Window scaling will force integral pixel alignment");
		return;
	}

	if (namesamen(pp, "1-window", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;

		if (strcmp(el_curwindowpart->location, "entire") == 0)
		{
			ttyputmsg("Already displaying only one window");
			return;
		}

		/* remember the current window */
		oldw = el_curwindowpart;

		/* turn off highlighting */
		us_pushhighlight();
		us_clearhighlightcount();
		(void)setvalkey((INTBIG)us_aid, VAID, us_current_window, (INTBIG)NOWINDOWPART, VWINDOWPART|VDONTSAVE);

		startobjectchange((INTBIG)us_aid, VAID);

		/* create a new window */
		neww = newwindowpart("entire", oldw);
		if (neww == NOWINDOWPART)
		{
			ttyputerr("No memory for windows");
			return;
		}

		/* if reducing to an editor window, move the editor structure */
		if ((oldw->state&WINDOWTYPE) == TEXTWINDOW ||
			(oldw->state&WINDOWTYPE) == POPTEXTWINDOW)
		{
			(void)setval((INTBIG)neww, VWINDOWPART, "editor", (INTBIG)oldw->editor, VADDRESS);
			(void)setval((INTBIG)oldw, VWINDOWPART, "editor", -1, VADDRESS);
		}

		/* now delete all other windows */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = nextw)
		{
			nextw = w->nextwindowpart;
			if (w->frame != neww->frame) continue;
			if (w != neww) killwindowpart(w);
		}

		/* set the window extents */
		us_windowfit(NOWINDOWFRAME, 0, 1);
		endobjectchange((INTBIG)us_aid, VAID);

		/* restore highlighting */
		(void)setvalkey((INTBIG)us_aid, VAID, us_current_window, (INTBIG)neww, VWINDOWPART|VDONTSAVE);
		(void)us_pophighlight(0);
		return;
	}

	if (namesamen(pp, "3-dimensional", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;
		w = el_curwindowpart;
		if (count < 2)
		{
			us_abortcommand("Usage: window 3-dimensional OPTION");
			return;
		}

		l = (INTSML)strlen(pp = par[1]);
		if (namesamen(pp, "begin", l) == 0 && l >= 1)
		{
			if ((w->state&WINDOWTYPE) == DISP3DWINDOW)
			{
				ttyputmsg("Window already shown in 3 dimensions");
				return;
			}
			if ((w->state&WINDOWTYPE) != DISPWINDOW)
			{
				us_abortcommand("Cannot view this window in 3 dimensions");
				return;
			}

			us_3dsetupviewing(w);

			/* clear highlighting */
			us_clearhighlightcount();

			/* set the window extents */
			startobjectchange((INTBIG)w, VWINDOWPART);
			(void)setval((INTBIG)w, VWINDOWPART, "buttonhandler", (INTBIG)us_3dbuttonhandler,
				VADDRESS);
			(void)setval((INTBIG)w, VWINDOWPART, "state", (w->state & ~WINDOWTYPE) | DISP3DWINDOW,
				VINTEGER);
			endobjectchange((INTBIG)w, VWINDOWPART);
			return;
		}
		if (namesamen(pp, "end", l) == 0 && l >= 1)
		{
			if ((w->state&WINDOWTYPE) == DISPWINDOW)
			{
				ttyputmsg("Window already shown in 2 dimensions");
				return;
			}
			if ((w->state&WINDOWTYPE) != DISP3DWINDOW)
			{
				us_abortcommand("Cannot view this window in 2 dimensions");
				return;
			}

			/* set the window extents */
			lx = w->screenlx;   hx = w->screenhx;
			ly = w->screenly;   hy = w->screenhy;
			us_squarescreen(w, NOWINDOWPART, 0, &lx, &hx, &ly, &hy);
			startobjectchange((INTBIG)w, VWINDOWPART);
			(void)setval((INTBIG)w, VWINDOWPART, "state", (w->state & ~WINDOWTYPE) | DISPWINDOW,
				VINTEGER);
			(void)setval((INTBIG)w, VWINDOWPART, "buttonhandler", (INTBIG)DEFAULTBUTTONHANDLER,
				VADDRESS);
			(void)setval((INTBIG)w, VWINDOWPART, "screenlx", lx, VINTEGER);
			(void)setval((INTBIG)w, VWINDOWPART, "screenhx", hx, VINTEGER);
			(void)setval((INTBIG)w, VWINDOWPART, "screenly", ly, VINTEGER);
			(void)setval((INTBIG)w, VWINDOWPART, "screenhy", hy, VINTEGER);
			us_gridset(w, el_curwindowpart->state);
			endobjectchange((INTBIG)w, VWINDOWPART);
			return;
		}
		if (namesamen(pp, "rotate", l) == 0 && l >= 1)
		{
			us_3dsetinteraction(0);
			return;
		}
		if (namesamen(pp, "zoom", l) == 0 && l >= 1)
		{
			us_3dsetinteraction(1);
			return;
		}
		if (namesamen(pp, "pan", l) == 0 && l >= 1)
		{
			us_3dsetinteraction(2);
			return;
		}
	}

	us_abortcommand("Bad WINDOW option: %s", par[0]);
}

void us_yanknode(INTSML count, char *par[])
{
	REGISTER INTSML found, total;
	REGISTER NODEINST *topno;
	REGISTER NODEPROTO *np;
	REGISTER GEOM **list;

	list = us_gethighlighted(OBJNODEINST);
	if (list[0] == NOGEOM)
	{
		us_abortcommand("Must highlight facet(s) to be yanked");
		return;
	}

	np = geomparent(list[0]);
	found = 0;
	for(total=0; list[total] != NOGEOM; total++)
	{
		topno = list[total]->entryaddr.ni;
		if (topno->proto->primindex != 0) continue;

		/* disallow yanking if lock is on */
		if (us_canedit(np, topno->proto, 1) != 0) return;

		/* turn off highlighting for the first facet */
		if (found == 0) us_clearhighlightcount();

		/* yank this facet */
		us_yankonenode(topno);
		found++;
	}

	if (found == 0) us_abortcommand("Can only yank facets");
}
