/*
 * Electric(tm) VLSI Design System
 *
 * File: usreditpac.c
 * Point-and-click editor
 * 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 "usreditpac.h"
#include "usrtrack.h"
#include "edialogs.h"

#define	THUMBSIZE   16					/* width of the thumb area in scroll slider */
#define	HEADERLINES  2					/* number of lines of text at top of editor */
#define	LEFTINDENT   4					/* space between left edge and text */
#define	DEFAULTWID   1					/* default number of characters on a line */

static INTSML   us_twid, us_thei;		/* size of a single letter */
static INTSML   us_editpacfont;			/* font size for editor */
static char    *us_editpacbuffer = 0;	/* the cut/copy/paste buffer */

/* variables for cursor tracking */
static WINDOWPART *us_editpaccurrentwin;
static INTSML      us_editpacoffset;
static INTBIG      us_originalcurline, us_originalcurchar;
static INTBIG      us_originalendline, us_originalendchar;
static INTBIG      us_editpaclastc, us_editpaclastl;

/* variables for undo */
static INTBIG      us_editpacundooldcurline, us_editpacundooldcurchar;
static INTBIG      us_editpacundonewcurline, us_editpacundonewcurchar;
static INTBIG      us_editpacundonewendline, us_editpacundonewendchar;
static char       *us_editpacundobuffer;
static INTBIG      us_editpacundobuffersize = 0;

extern GRAPHICS us_ebox, us_menutext, us_menufigs;
GRAPHICS us_drawgray = {LAYERA, DGRAY, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};

/* prototypes for local routines */
void us_editpacredraweditor(WINDOWPART*);
void us_editpacgotbutton(WINDOWPART*, INTSML, INTSML, INTSML);
INTSML us_editpacclickdown(INTBIG, INTBIG);
INTSML us_editpacdclickdown(INTBIG, INTBIG);
INTSML us_editpacdoclickdown(INTBIG, INTBIG, INTSML);
INTSML us_editpacdownarrow(INTBIG, INTBIG);
INTSML us_editpacuparrow(INTBIG, INTBIG);
INTSML us_editpacdownpage(INTBIG, INTBIG);
INTSML us_editpacuppage(INTBIG, INTBIG);
INTSML us_editpacrightarrow(INTBIG, INTBIG);
INTSML us_editpacleftarrow(INTBIG, INTBIG);
INTSML us_editpacrightpage(INTBIG, INTBIG);
INTSML us_editpacleftpage(INTBIG, INTBIG);
INTSML us_editpacimplementchar(WINDOWPART*, INTSML, INTSML);
void us_editpacremoveselection(WINDOWPART*);
void us_editpacredrawscreen(WINDOWPART*);
void us_drawsliders(WINDOWPART*);
void us_editpacdrawhscroll(WINDOWPART*);
void us_editpacdrawvscroll(WINDOWPART*);
void us_editpaccleanupline(WINDOWPART*, INTBIG);
void us_editpacredrawlines(WINDOWPART*);
void us_editpacsetheader(WINDOWPART*, char*);
void us_editpacinvertselection(WINDOWPART*);
void us_editpacdonetyping(EDITOR*);
void us_editpacamtyping(EDITOR*);
void us_editpactypeddelete(EDITOR*);
INTBIG us_editpaccharstocurrent(EDITOR*, INTBIG, INTBIG);
void us_editpacloadselection(EDITOR*, char*);
void us_editpaccomputebounds(WINDOWPART*, EDITOR*);
void us_editpacbeginwork(WINDOWPART*);
void us_editpacaddmorelines(EDITOR*);
void us_editpacaddmorechars(EDITOR*, INTBIG, INTBIG);
void us_editpacmovebox(WINDOWPART*, INTSML, INTSML, INTSML, INTSML, INTSML, INTSML);
void us_editpacclearbox(WINDOWPART*, INTSML, INTSML, INTSML, INTSML);
void us_editpacinvertrectframe(WINDOWPART*, INTSML, INTSML);
void us_editpactext(WINDOWPART*, char*, INTSML, INTSML);

/******************** ROUTINES IN THE EDITOR TABLE ********************/

/*
 * routine to convert window "oriwin" to a point-and-click text editor.
 * If "oriwin" is NOWINDOWPART, create a popup window with one line.  The window
 * header is in "header".  The number of characters and lines are placed in
 * "chars" and "lines".  Returns the window (NOWINDOWPART if the editor cannot be started).
 */
WINDOWPART *us_editpacmakeeditor(WINDOWPART *oriwin, char *header, INTSML *chars, INTSML *lines)
{
	INTBIG x, y;
	INTSML i, xs, ys, swid, shei, pwid;
	REGISTER EDITOR *e;
	REGISTER VARIABLE *var;
	REGISTER WINDOWPART *win;

	/*
	 * the font that is used by the editor is the fixed-width font, TXTEDITOR.
	 * This can be changed by setting the variable "USER_textedit_font" on the user aid
	 * object.  The value is the point size minus 4 divided by 2 (the value 0 is the
	 * default font TXTEDITOR).  For example, to set the font to be 14 points, type:
	 *     -var set aid:user.USER_textedit_font 5
	 */
	us_editpacfont = TXTEDITOR;
	var = getval((INTBIG)us_aid, VAID, VINTEGER, "USER_textedit_font");
	if (var != NOVARIABLE && var->addr >= TXT4P && var->addr <= TXT20P)
		us_editpacfont = (INTSML)var->addr;

	/* if no window exists, create one as a popup */
	win = oriwin;
	if (win == NOWINDOWPART)
	{
		getpaletteparameters(&swid, &shei, &pwid);
		if (el_curwindowpart == NOWINDOWPART)
		{
			x = swid / 2;
			y = shei / 2;
		} else
		{
			(void)getxy(&x, &y);
			x = applyxscale(el_curwindowpart, x-el_curwindowpart->screenlx) + el_curwindowpart->uselx;
			y = applyyscale(el_curwindowpart, y-el_curwindowpart->screenly) + el_curwindowpart->usely;
		}

		/* create a window that covers the popup */
		startobjectchange((INTBIG)us_aid, VAID);
		win = newwindowpart("popup", el_curwindowpart);
		screensettextsize(win, us_editpacfont);
		screengettextsize(win, "X", &us_twid, &us_thei);
		xs = 400;   ys = us_thei*(HEADERLINES+1)+DISPLAYSLIDERSIZE+4;
		win->uselx = (INTSML)maxi(x-xs/2, 0);
		win->usehx = (INTSML)mini(win->uselx+xs, swid-1);
		win->usely = (INTSML)maxi(y-ys, 0);
		win->usehy = (INTSML)mini(win->usely+ys, shei-1);
		win->state = (win->state & ~WINDOWTYPE) | POPTEXTWINDOW;
	} else
	{
		/* make sure there is room for at least one line */
		if (win->usehy - win->usely - us_thei*HEADERLINES - 1 - DISPLAYSLIDERSIZE < us_thei)
			return(NOWINDOWPART);

		if ((win->state&WINDOWTYPE) == DISPWINDOW)
		{
			win->usehx += DISPLAYSLIDERSIZE;
			win->usely -= DISPLAYSLIDERSIZE;
		}
		if ((win->state&WINDOWTYPE) == WAVEFORMWINDOW)
		{
			win->uselx -= DISPLAYSLIDERSIZE;
			win->usely -= DISPLAYSLIDERSIZE;
		}
		win->state = (win->state & ~(WINDOWTYPE|WINDOWSIMULATING)) | TEXTWINDOW;
		screensettextsize(win, us_editpacfont);
		screengettextsize(win, "X", &us_twid, &us_thei);
	}

	win->curnodeproto = NONODEPROTO;
	win->buttonhandler = us_editpacgotbutton;
	win->charhandler = us_editpacgotchar;
	win->termhandler = us_editpaceditorterm;
	win->redisphandler = us_editpacredraweditor;
	win->changehandler = 0;
	win->screenlx = win->uselx;   win->screenhx = win->usehx;
	win->screenly = win->usely;   win->screenhy = win->usehy;
	computewindowscale(win);

	/* create a new editor object */
	e = us_alloceditor();
	if (e == NOEDITOR) return(NOWINDOWPART);
	e->state = (e->state & ~(EDITORTYPE|EGRAPHICSOFF|LINESFIXED|TEXTTYPING|TEXTTYPED)) | PACEDITOR;
	(void)allocstring(&e->header, header, us_aid->cluster);
	e->curline = e->curchar = 0;
	e->endline = e->endchar = 0;
	e->firstline = 0;
	e->horizfactor = 0;
	e->linecount = 1;
	e->charposition = 0;
	us_editpaccomputebounds(win, e);
	*chars = e->screenchars;
	*lines = e->screenlines;

	/* initialize the EDITOR structure */
	if ((e->state&EDITORINITED) == 0)
	{
		/* first time: allocate buffers */
		e->state |= EDITORINITED;
		e->maxlines = e->screenlines;
		if (e->maxlines <= 0) e->maxlines = 1;
		e->mostchars = e->screenchars;
		e->formerline = (char *)emalloc(e->mostchars+1, us_aid->cluster);
		if (e->formerline == 0) ttyputnomemory();
		e->textarray = (char **)emalloc((e->maxlines * (sizeof (char *))), us_aid->cluster);
		if (e->textarray == 0) ttyputnomemory();
		e->maxchars = (INTSML *)emalloc((e->maxlines * SIZEOFINTSML), us_aid->cluster);
		if (e->maxchars == 0) ttyputnomemory();
		for(i=0; i<e->maxlines; i++)
		{
			e->textarray[i] = (char *)emalloc(DEFAULTWID, us_aid->cluster);
			if (e->textarray[i] == 0) ttyputnomemory();
			e->maxchars[i] = DEFAULTWID;
		}
	} else
	{
		/* make sure buffers cover the screen */
		while (e->screenlines > e->maxlines) us_editpacaddmorelines(e);
	}
	for(i=0; i<e->maxlines; i++)
		e->textarray[i][0] = 0;
	e->formerline[0] = 0;
	e->working = 0;
	e->dirty = 0;

	/* now finish initializing window */
	win->editor = e;
	if (oriwin != NOWINDOWPART)
	{
		/* clear window and write header */
		us_editpacredrawscreen(win);
	} else
	{
		/* finish initializing popup text window */
		e->savedbox = screensavebox(win, win->uselx, win->usehx, win->usely, win->usehy);
		endobjectchange((INTBIG)us_aid, VAID);
	}
	return(win);
}

/*
 * routine to free all memory associated with this module (at exit time)
 */
void us_freeedpacmemory(void)
{
	if (us_editpacbuffer != 0) efree(us_editpacbuffer);
}

/*
 * routine to free all memory associated with this editor
 */
void us_editpacterminate(EDITOR *e)
{
	REGISTER INTBIG i;

	efree((char *)e->formerline);
	for(i=0; i<e->maxlines; i++)
		efree((char *)e->textarray[i]);
	efree((char *)e->textarray);
	efree((char *)e->maxchars);
	if (us_editpacundobuffersize > 0)
	{
		efree(us_editpacundobuffer);
		us_editpacundobuffersize = 0;
	}
}

/*
 * routine to return the total number of valid lines in the edit buffer
 */
INTBIG us_editpactotallines(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return(0);
	return(e->linecount);
}

/*
 * routine to get the string on line "lindex" (0 based).  A negative line
 * returns the current line.  Returns -1 if the index is beyond the file limit
 */
char *us_editpacgetline(WINDOWPART *win, INTBIG lindex)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return("");
	if (lindex < 0) lindex = e->curline;
	if (lindex >= e->linecount) return(NOSTRING);
	return(e->textarray[lindex]);
}

/*
 * routine to add line "str" to the text facet to become line "lindex"
 */
void us_editpacaddline(WINDOWPART *win, INTBIG lindex, char *str)
{
	REGISTER char *pt;
	REGISTER INTBIG savedsline, savedschar, savedeline, savedechar;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	if ((e->state&EGRAPHICSOFF) == 0) screensettextsize(win, us_editpacfont);

	/* send out any pending changes */
	us_editpacshipchanges(win);

	/* change selection to the current line */
	us_editpacinvertselection(win);
	savedschar = e->curchar;   savedsline = e->curline;
	savedechar = e->endchar;   savedeline = e->endline;
	e->curline = e->endline = lindex;   e->curchar = e->endchar = 0;
	us_editpacbeginwork(win);

	/* fake the characters */
	for(pt = str; *pt != 0; pt++) (void)us_editpacimplementchar(win, *pt, 0);
	(void)us_editpacimplementchar(win, '\n', 0);

	/* send out pending changes */
	us_editpacshipchanges(win);

	/* restore selection */
	e->curchar = savedschar;   e->curline = savedsline;
	e->endchar = savedechar;   e->endline = savedeline;
	us_editpacinvertselection(win);
	us_editpacbeginwork(win);
}

/*
 * routine to replace the line number "lindex" with the string "str".
 */
void us_editpacreplaceline(WINDOWPART *win, INTBIG lindex, char *str)
{
	REGISTER char *pt;
	REGISTER INTBIG savedsline, savedschar, savedeline, savedechar;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	if ((e->state&EGRAPHICSOFF) == 0) screensettextsize(win, us_editpacfont);

	/* send out any pending changes */
	us_editpacshipchanges(win);

	/* change selection to the current line */
	us_editpacinvertselection(win);
	savedschar = e->curchar;   savedsline = e->curline;
	savedechar = e->endchar;   savedeline = e->endline;
	e->curline = e->endline = lindex;
	e->curchar = 0;   e->endchar = strlen(e->textarray[lindex]);
	us_editpacbeginwork(win);

	/* fake the characters */
	for(pt = str; *pt != 0; pt++) (void)us_editpacimplementchar(win, *pt, 0);

	/* send out pending changes */
	us_editpacshipchanges(win);

	/* restore selection */
	e->curchar = savedschar;   e->curline = savedsline;
	e->endchar = savedechar;   e->endline = savedeline;
	us_editpacinvertselection(win);
	us_editpacbeginwork(win);
}

/*
 * routine to delete line number "lindex"
 */
void us_editpacdeleteline(WINDOWPART *win, INTBIG lindex)
{
	REGISTER INTBIG savedsline, savedschar, savedeline, savedechar;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	if ((e->state&EGRAPHICSOFF) == 0) screensettextsize(win, us_editpacfont);

	/* send out any pending changes */
	us_editpacshipchanges(win);

	/* change selection to the current line */
	us_editpacinvertselection(win);
	savedschar = e->curchar;   savedsline = e->curline;
	savedechar = e->endchar;   savedeline = e->endline;
	e->curline = lindex;   e->endline = lindex+1;
	e->curchar = e->endchar = 0;
	us_editpacbeginwork(win);

	/* fake the deletion character */
	(void)us_editpacimplementchar(win, DELETEKEY, 0);

	/* send out pending changes */
	us_editpacshipchanges(win);

	/* restore selection */
	e->curchar = savedschar;   e->curline = savedsline;
	e->endchar = savedechar;   e->endline = savedeline;
	us_editpacinvertselection(win);
	us_editpacbeginwork(win);
}

/*
 * routine to highlight line "lindex" in the text window
 */
void us_editpachighlightline(WINDOWPART *win, INTBIG lindex)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	screensettextsize(win, us_editpacfont);
	us_editpacinvertselection(win);
	e->curline = e->endline = lindex;
	e->curchar = 0;   e->endchar = e->maxchars[lindex];
	us_editpacinvertselection(win);
	us_editpacbeginwork(win);

	/* ensure that line "lindex" is shown in the display */
	if (lindex-e->firstline >= e->screenlines || lindex < e->firstline)
	{
		e->firstline = maxi(0, lindex - e->screenlines/2);
		us_editpacredrawlines(win);
		if ((win->state&WINDOWTYPE) != POPTEXTWINDOW)
			us_editpacdrawvscroll(win);
	}
}

/*
 * routine to stop the graphic display of changes (for batching)
 */
void us_editpacsuspendgraphics(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	e->state |= EGRAPHICSOFF;
}

/*
 * routine to restart the graphic display of changes and redisplay (for batching)
 */
void us_editpacresumegraphics(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	e->state &= ~EGRAPHICSOFF;
	screensettextsize(win, us_editpacfont);
	us_editpacredrawscreen(win);
}

/*
 * routine to write the text file to "file"
 */
void us_editpacwritetextfile(WINDOWPART *win, char *file)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG i;
	REGISTER FILE *f;
	char *truename;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* find the last line */
	if (e->linecount <= 0)
	{
		ttyputerr(_("File is empty"));
		return;
	}

	f = xcreate(file, el_filetypetext, _("Text File"), &truename);
	if (f == NULL)
	{
		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
		return;
	}

	for(i = 0; i < e->linecount; i++) xprintf(f, "%s\n", e->textarray[i]);
	xclose(f);
	ttyputmsg(_("%s written"), truename);
}

/*
 * routine to read the text file "file"
 */
void us_editpacreadtextfile(WINDOWPART *win, char *file)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG i, filelength, charsread;
	REGISTER INTSML c;
	REGISTER FILE *f;
	char *filename;
	extern DIALOG us_progressdialog;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* get the file */
	f = xopen(file, el_filetypetext, "", &filename);
	if (f == NULL)
	{
		ttyputerr(_("Cannot read %s"), file);
		return;
	}
	filelength = filesize(f);
	if (filelength > 0)
	{
		if (DiaInitDialog(&us_progressdialog) != 0)
		{
			xclose(f);
			return;
		}
		DiaPercent(1, 0);
		(void)initinfstr();
		(void)formatinfstr(_("Reading %s..."), file);
		DiaSetText(2, returninfstr());
	} else
		ttyputmsg(_("Reading %s"), file);

	e->state |= EGRAPHICSOFF;
	for(i=0; i<e->maxlines; i++) e->textarray[i][0] = 0;
	e->curline = e->curchar = e->firstline = 0;
	e->endline = e->endchar = 0;
	e->dirty = 0;
	e->linecount = 0;

	/* read the file */
	charsread = 0;
	for(;;)
	{
		c = xgetc(f);
		if (c == EOF) break;
		charsread++;
		if (c == '\t') c = ' ';
		if (c == '\n' || c == '\r')
		{
			if (e->curline >= e->maxlines-1) us_editpacaddmorelines(e);
			e->linecount++;
			e->curline++;
			e->curchar = 0;
		} else
		{
			/* see if line needs to be extended */
			i = strlen(e->textarray[e->curline]) + 2;
			if (i > e->maxchars[e->curline])
				us_editpacaddmorechars(e, e->curline, i+10);

			e->textarray[e->curline][e->curchar] = (char)c;
			e->textarray[e->curline][e->curchar+1] = 0;
			e->curchar++;
		}
		if (filelength > 0 && (charsread%100) == 0)
		{
			DiaPercent(1, charsread*100/filelength);
		}
	}
	xclose(f);

	/* announce the changes (go backwards so the array expands only once) */
	if (filelength > 0)
	{
		DiaPercent(1, 100);
		DiaSetText(2, _("Cleaning up..."));
	}
	(*win->changehandler)(win, REPLACEALLTEXT, "", (char *)e->textarray, e->linecount);
	if (filelength > 0) DiaDoneDialog();

	/* restore the display */
	e->state &= ~EGRAPHICSOFF;
	screensettextsize(win, us_editpacfont);
	e->curline = e->curchar = 0;
	e->endline = e->endchar = 0;
	us_editpacbeginwork(win);
	us_editpacredrawlines(win);
	if ((win->state&WINDOWTYPE) != POPTEXTWINDOW) us_editpacdrawvscroll(win);
	us_editpacdrawhscroll(win);
}

void us_editpaceditorterm(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* first flush any pending edit changes */
	us_editpacshipchanges(win);

	/* restore window state */
	if ((win->state&WINDOWTYPE) == POPTEXTWINDOW)
	{
		(void)screenrestorebox(e->savedbox, 0);
	}
}

void us_editpacshipchanges(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	if (e->working >= 0 && e->dirty != 0)
	{
		if (win->changehandler != 0)
			(*win->changehandler)(win, REPLACETEXTLINE, e->formerline, e->textarray[e->working],
				e->working);
		e->dirty = 0;
		e->working = -1;
	}
}

/*
 * keyboard interrupt routine for the text window
 */
INTSML us_editpacgotchar(WINDOWPART *win, INTSML i)
{
	REGISTER EDITOR *e;

	if ((i&0200) != 0)
	{
		return((*DEFAULTCHARHANDLER)(win, i));
	}

	e = win->editor;
	if (e == NOEDITOR) return(1);
	screensettextsize(win, us_editpacfont);
	us_editpacinvertselection(win);
	if (us_editpacimplementchar(win, i, 1) != 0) return(1);
	us_editpacinvertselection(win);
	setactivity(_("Text Editing"));
	return(0);
}

void us_editpaccut(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* copy the selection to the cut/copy buffer */
	us_editpaccopy(win);

	/* remove the selection from the edit buffer */
	screensettextsize(win, us_editpacfont);
	us_editpacinvertselection(win);
	(void)us_editpacimplementchar(win, DELETEKEY, 1);
	us_editpacinvertselection(win);
}

void us_editpaccopy(WINDOWPART *win)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG len;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* free the former cut/copy buffer */
	if (us_editpacbuffer != 0)
	{
		efree(us_editpacbuffer);
		us_editpacbuffer = 0;
	}

	/* determine number of characters that are selected */
	len = us_editpaccharstocurrent(e, e->endchar, e->endline);
	if (len <= 0) return;

	/* allocate a new cut/copy buffer */
	us_editpacbuffer = (char *)emalloc(len+1, us_aid->cluster);
	if (us_editpacbuffer == 0) return;

	/* fill the buffer */
	us_editpacloadselection(e, us_editpacbuffer);

	/* store it in the system-wide cut buffer */
	setcutbuffer(us_editpacbuffer);
}

void us_editpacpaste(WINDOWPART *win)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG i;

	INTBIG len;
	char *str;

	/* flush the internal buffer and get the system-wide cut buffer */
	if (us_editpacbuffer != 0) efree(us_editpacbuffer);
	us_editpacbuffer = 0;
	str = getcutbuffer();
	len = strlen(str);
	if (len > 0)
	{
		us_editpacbuffer = (char *)emalloc(len+1, us_aid->cluster);
		if (us_editpacbuffer == 0) return;
		strcpy(us_editpacbuffer, str);
	}
	if (us_editpacbuffer == 0) return;

	e = win->editor;
	if (e == NOEDITOR) return;
	screensettextsize(win, us_editpacfont);

	us_editpacinvertselection(win);
	for(i=0; us_editpacbuffer[i] != 0; i++)
		(void)us_editpacimplementchar(win, us_editpacbuffer[i], 1);
	us_editpacinvertselection(win);
}

void us_editpacundo(WINDOWPART *win)
{
	char *savebuffer;
	REGISTER EDITOR *e;
	REGISTER INTBIG i;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* if no change made, stop */
	if ((e->state&TEXTTYPED) == 0) return;

	/* finish a set of changes */
	us_editpacdonetyping(e);

	/* save the replacement buffer */
	(void)allocstring(&savebuffer, us_editpacundobuffer, el_tempcluster);

	/* replace what was typed */
	screensettextsize(win, us_editpacfont);
	us_editpacinvertselection(win);
	e->curline = us_editpacundonewcurline;    e->curchar = us_editpacundonewcurchar;
	e->endline = us_editpacundonewendline;    e->endchar = us_editpacundonewendchar;
	us_editpacbeginwork(win);
	if (savebuffer[0] == 0) (void)us_editpacimplementchar(win, DELETEKEY, 1); else
		for(i=0; savebuffer[i] != 0; i++)
			(void)us_editpacimplementchar(win, savebuffer[i], 1);
	us_editpacinvertselection(win);

	efree(savebuffer);
}

void us_editpacsearch(WINDOWPART *win, char *str, INTSML fromtop)
{
	REGISTER INTBIG i, chr, lne, len, want, mchr, mlne, mlen, startchr, startlne;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	if (fromtop != 0)
	{
		/* search from the top of the buffer */
		startchr = 0;   startlne = 0;
	} else
	{
		/* search from the end of the current selection */
		startchr = e->endchar;   startlne = e->endline;
	}
	chr = startchr;   lne = startlne;
	len = strlen(e->textarray[lne]);
	for(;;)
	{
		/* see if the string matches at this point */
		mchr = chr;   mlne = lne;   mlen = len;
		for(i=0; str[i] != 0; i++)
		{
			want = str[i];
			if (want == '\n') want = 0;
			if (tolower(e->textarray[mlne][mchr]) != tolower(str[i])) break;

			/* advance match pointer to the next character in the file */
			mchr++;
			if (mchr > mlen)
			{
				mchr = 0;
				mlne++;
				if (mlne >= e->linecount) mlne = 0;
				mlen = strlen(e->textarray[mlne]);
			}
		}
		if (str[i] == 0) break;

		/* advance to the next character in the file */
		chr++;
		if (chr > len)
		{
			chr = 0;
			lne++;
			if (lne >= e->linecount) lne = 0;
			len = strlen(e->textarray[lne]);
		}

		/* if it came all the way around, stop */
		if (chr == startchr && lne == startlne)
		{
			ttybeep();
			return;
		}
	}

	/* string found */
	screensettextsize(win, us_editpacfont);
	us_editpacdonetyping(e);
	us_editpacinvertselection(win);
	e->curline = lne;   e->curchar = chr;
	for(i=0; str[i] != 0; i++)
	{
		chr++;
		if (chr > len)
		{
			chr = 0;
			lne++;
			if (lne >= e->linecount) lne = 0;
			len = strlen(e->textarray[lne]);
		}
	}
	e->endline = lne;   e->endchar = chr;
	us_editpacinvertselection(win);
	us_editpacbeginwork(win);

	/* ensure that line "e->curline" is shown in the display */
	if (e->curline-e->firstline >= e->screenlines || e->curline < e->firstline)
	{
		e->firstline = maxi(0, e->curline - e->screenlines/2);
		us_editpacredrawlines(win);
		if ((win->state&WINDOWTYPE) != POPTEXTWINDOW)
			us_editpacdrawvscroll(win);
	}
}

/******************** ROUTINES IN THE WINDOW STRUCTURE ********************/

void us_editpacredraweditor(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	screensettextsize(win, us_editpacfont);

	/* compute window extents */
	us_editpaccomputebounds(win, e);

	while (e->screenlines > e->maxlines) us_editpacaddmorelines(e);

	us_editpacredrawscreen(win);
}

void us_editpacgotbutton(WINDOWPART *win, INTSML but, INTSML x, INTSML y)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG xc, yc, len, i;
	INTSML (*clickroutine)(INTBIG, INTBIG);

	e = win->editor;
	if (e == NOEDITOR) return;
	screensettextsize(win, us_editpacfont);
	us_editpaccurrentwin = win;

	if ((win->state&WINDOWTYPE) != POPTEXTWINDOW && x > e->offx + e->swid)
	{
		/* cursor in vertical slider */
		if (e->linecount <= e->screenlines) return;
		if (y < e->revy - e->shei + DISPLAYSLIDERSIZE)
		{
			/* the down arrow */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacdownarrow,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (y > e->revy - DISPLAYSLIDERSIZE)
		{
			/* the up arrow */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacuparrow,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (y < e->vthumbpos-THUMBSIZE/2 && y >= e->revy - e->shei + DISPLAYSLIDERSIZE)
		{
			/* scroll down one page */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacdownpage,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (y > e->vthumbpos+THUMBSIZE/2 && y <= e->revy - DISPLAYSLIDERSIZE)
		{
			/* scroll up one page */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacuppage,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (y >= e->vthumbpos-THUMBSIZE/2 && y <= e->vthumbpos+THUMBSIZE/2)
		{
			/* drag slider appropriately */
			us_vthumbbegin(y, win, win->usehx-DISPLAYSLIDERSIZE, win->usely+DISPLAYSLIDERSIZE,
				win->usehy-us_thei*HEADERLINES, e->linecount, 0);
			trackcursor(0, us_nullup, us_nullvoid, us_vthumbdown, us_nullchar,
				us_evthumbdone, TRACKNORMAL);
			e->firstline = (INTSML)us_evthumbend();
			us_editpacredrawlines(win);
			us_editpacdrawvscroll(win);
			return;
		}
	}

	if (y <= e->revy - e->shei)
	{
		/* cursor in horizontal slider */
		if (x > e->offx + e->swid - DISPLAYSLIDERSIZE)
		{
			/* the right arrow */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacrightarrow,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (x < e->offx + DISPLAYSLIDERSIZE - LEFTINDENT)
		{
			/* the left arrow */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacleftarrow,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (x > e->hthumbpos+THUMBSIZE/2 && x <= e->offx + e->swid - DISPLAYSLIDERSIZE)
		{
			/* scroll right one page */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacrightpage,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (x < e->hthumbpos-THUMBSIZE/2 && x >= e->offx + DISPLAYSLIDERSIZE - LEFTINDENT)
		{
			/* scroll left one page */
			trackcursor(0, us_nullup, us_nullvoid, us_editpacleftpage,
				us_nullchar, us_nullvoid, TRACKNORMAL);
			return;
		}
		if (x >= e->hthumbpos-THUMBSIZE/2 && x <= e->hthumbpos+THUMBSIZE/2)
		{
			/* drag slider appropriately */
			us_hthumbbegin(x, win, win->usely+DISPLAYSLIDERSIZE, win->uselx,
				win->usehx-DISPLAYSLIDERSIZE);
			trackcursor(0, us_nullup, us_nullvoid, us_hthumbdown, us_nullchar,
				us_ehthumbdone, TRACKNORMAL);
			i = e->swid - DISPLAYSLIDERSIZE*2 + LEFTINDENT - THUMBSIZE;
			i = ((us_ehthumbend() - (e->offx+DISPLAYSLIDERSIZE-LEFTINDENT+THUMBSIZE/2)) *
				e->screenchars*10 + i/2) / i;
			if (i < 0) i = 0;
			if (i == e->horizfactor) return;
			e->horizfactor = (INTSML)i;
			us_editpacredrawlines(win);
			us_editpacdrawhscroll(win);
			return;
		}
	}

	/* find the character position in the edit buffer */
	if (y > e->revy) return;
	yc = (e->revy-y) / us_thei + e->firstline;
	if (yc >= e->screenlines + e->firstline) yc = e->screenlines - 1 + e->firstline;
	if (yc >= e->linecount)
	{
		yc = e->linecount-1;
		xc = strlen(e->textarray[yc]);
	} else
	{
		xc = (x-e->offx+us_twid/2) / us_twid + e->horizfactor;
		if (xc < 0) xc = 0;
		len = strlen(e->textarray[yc]);
		if (xc > len) xc = len;
	}

	/* turn off the former selection */
	us_editpacinvertselection(win);
	us_editpacdonetyping(e);

	/* determine the selection from this click */
	if (shiftbutton(but) != 0)
	{
		if (yc < e->curline || (yc == e->curline && xc < e->curchar))
		{
			e->curline = yc;   e->curchar = xc;
		} else
		{
			e->endline = yc;   e->endchar = xc;
		}
	} else
	{
		e->curline = e->endline = yc;
		e->curchar = e->endchar = xc;
	}
	if (doublebutton(but) != 0)
	{
		/* double click: select word about cursor */
		for(e->curchar = xc-1; e->curchar >= 0; e->curchar--)
			if (!isalnum(e->textarray[yc][e->curchar])) break;
		e->curchar++;
		for(e->endchar = e->curchar; e->textarray[yc][e->endchar] != 0; e->endchar++)
			if (!isalnum(e->textarray[yc][e->endchar])) break;
		clickroutine = us_editpacdclickdown;
	} else clickroutine = us_editpacclickdown;

	/* show the new selection */
	us_editpacinvertselection(win);

	/* extend the selection while the cursor remains down */
	us_originalcurline = e->curline;   us_originalcurchar = e->curchar;
	us_originalendline = e->endline;   us_originalendchar = e->endchar;
	us_editpaclastc = us_editpaclastl = -1;
	trackcursor(0, us_nullup, us_nullvoid, clickroutine,
		us_nullchar, us_nullvoid, TRACKNORMAL);
	us_editpacbeginwork(win);
	e->charposition = e->endchar;
}

/******************** TRACKING ROUTINES ********************/

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

INTSML us_editpacdclickdown(INTBIG x, INTBIG y)
{
	return(us_editpacdoclickdown(x, y, 1));
}

INTSML us_editpacdoclickdown(INTBIG x, INTBIG y, INTSML bywords)
{
	REGISTER INTBIG c, l, len, low, high;
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;

	if (y > e->revy && (us_editpaccurrentwin->state&WINDOWTYPE) != POPTEXTWINDOW)
	{
		/* if cursor is above the window, may shift lines */
		if (e->firstline == 0) return(0);
		e->firstline--;
		us_editpacredrawlines(us_editpaccurrentwin);
		us_editpacdrawvscroll(us_editpaccurrentwin);
	}
	if (y < e->revy-e->shei && (us_editpaccurrentwin->state&WINDOWTYPE) != POPTEXTWINDOW)
	{
		/* if cursor is below the window, may shift lines */
		if (e->linecount-e->firstline <= e->screenlines) return(0);
		e->firstline++;
		us_editpacredrawlines(us_editpaccurrentwin);
		us_editpacdrawvscroll(us_editpaccurrentwin);
	}
	if (x < e->offx)
	{
		/* if cursor is to the left of the window, may shift screen */
		if (e->horizfactor > 0)
		{
			e->horizfactor--;
			us_editpacredrawlines(us_editpaccurrentwin);
			us_editpacdrawhscroll(us_editpaccurrentwin);
		}
	}
	if (x > e->offx+e->swid)
	{
		/* if cursor is to the right of the window, may shift screen */
		if (e->horizfactor < e->screenchars*10)
		{
			e->horizfactor++;
			us_editpacredrawlines(us_editpaccurrentwin);
			us_editpacdrawhscroll(us_editpaccurrentwin);
		}
	}
	l = (e->revy-y) / us_thei + e->firstline;
	if (l >= e->screenlines + e->firstline) l = e->screenlines - 1 + e->firstline;
	if (l >= e->linecount)
	{
		l = e->linecount-1;
		c = strlen(e->textarray[l]);
	} else
	{
		c = (x-e->offx+us_twid/2) / us_twid + e->horizfactor;
		if (c < 0) c = 0;
		len = strlen(e->textarray[l]);
		if (c > len) c = len;
	}

	/* only interested if the character position changed */
	if (us_editpaclastc != -1 && us_editpaclastl != -1 &&
		us_editpaclastc == c && us_editpaclastl == l) return(0);
	us_editpaclastc = c;   us_editpaclastl = l;

	/* if going by words, advance that much at a time */
	if (bywords != 0 && isalnum(e->textarray[l][c]) && c > 0 && isalnum(e->textarray[l][c-1]))
	{
		for(low = c-1; low >= 0; low--)
			if (!isalnum(e->textarray[l][low])) break;
		low++;
		for(high = low; e->textarray[l][high] != 0; high++)
			if (!isalnum(e->textarray[l][high])) break;
		if (l > us_originalendline || (l == us_originalendline && high > us_originalendchar))
			c = high;
		if (l < us_originalcurline || (l == us_originalcurline && low < us_originalcurchar))
			c = low;
	}

	us_editpacinvertselection(us_editpaccurrentwin);
	if (l > us_originalendline || (l == us_originalendline && c >= us_originalendchar))
	{
		e->curline = us_originalcurline;   e->curchar = us_originalcurchar;
		e->endline = l;                    e->endchar = c;
	}
	if (l < us_originalcurline || (l == us_originalcurline && c <= us_originalcurchar))
	{
		e->curline = l;                    e->curchar = c;
		e->endline = us_originalendline;   e->endchar = us_originalendchar;
	}
	us_editpacinvertselection(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacdownarrow(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG i;

	e = us_editpaccurrentwin->editor;
	if (x <= e->offx + e->swid || x > e->offx + e->swid + DISPLAYSLIDERSIZE) return(0);
	if (y >= e->revy - e->shei + DISPLAYSLIDERSIZE || y < e->revy - e->shei) return(0);
	if (e->linecount-e->firstline <= e->screenlines) return(1);

	us_editpacinvertselection(us_editpaccurrentwin);
	e->firstline++;

	/* shift the window contents up by 1 line (us_thei) */
	screenmovebox(us_editpaccurrentwin, (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->revy-e->shei),
		(INTSML)(e->swid+LEFTINDENT), (INTSML)(e->shei-us_thei), (INTSML)(e->offx-LEFTINDENT),
			(INTSML)(e->revy-e->shei+us_thei));

	/* fill in a new last line */
	screendrawbox(us_editpaccurrentwin, (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->offx+e->swid),
		(INTSML)(e->revy-e->shei), (INTSML)(e->revy-e->shei+us_thei), &us_ebox);
	i = e->firstline + e->screenlines - 1;
	if (i < e->linecount)
		us_editpactext(us_editpaccurrentwin, e->textarray[i], 0, (INTSML)(i-e->firstline));
	us_editpacinvertselection(us_editpaccurrentwin);
	us_editpacdrawvscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacuparrow(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (x <= e->offx + e->swid || x > e->offx + e->swid + DISPLAYSLIDERSIZE) return(0);
	if (y <= e->revy - DISPLAYSLIDERSIZE || y > e->revy) return(0);
	if (e->firstline <= 0) return(1);

	us_editpacinvertselection(us_editpaccurrentwin);
	e->firstline--;

	/* shift the window contents down by 1 line (us_thei) */
	screenmovebox(us_editpaccurrentwin, (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->revy-e->shei+us_thei), (INTSML)(e->swid+LEFTINDENT),
		(INTSML)(e->shei-us_thei), (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->revy-e->shei));

	/* fill in a new first line */
	screendrawbox(us_editpaccurrentwin, (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->offx+e->swid),
		(INTSML)(e->revy-us_thei), e->revy, &us_ebox);
	us_editpactext(us_editpaccurrentwin, e->textarray[e->firstline], 0, 0);
	us_editpacinvertselection(us_editpaccurrentwin);
	us_editpacdrawvscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacdownpage(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (x <= e->offx + e->swid || x > e->offx + e->swid + DISPLAYSLIDERSIZE) return(0);
	if (y >= e->vthumbpos-THUMBSIZE/2 || y < e->revy - e->shei + DISPLAYSLIDERSIZE) return(0);
	if (e->linecount-e->firstline <= e->screenlines) return(1);
	e->firstline += e->screenlines-1;
	if (e->linecount-e->firstline <= e->screenlines)
		e->firstline = e->linecount-e->screenlines;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawvscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacuppage(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (x <= e->offx + e->swid || x > e->offx + e->swid + DISPLAYSLIDERSIZE) return(0);
	if (y <= e->vthumbpos+THUMBSIZE/2 || y > e->revy - DISPLAYSLIDERSIZE) return(0);
	if (e->firstline <= 0) return(1);
	e->firstline -= e->screenlines-1;
	if (e->firstline < 0) e->firstline = 0;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawvscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacrightarrow(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (y > e->revy - e->shei || y < e->revy - e->shei - DISPLAYSLIDERSIZE) return(0);
	if (x <= e->offx + e->swid - DISPLAYSLIDERSIZE || x > e->offx + e->swid) return(0);
	if (e->horizfactor >= e->screenchars*10) return(1);
	e->horizfactor++;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawhscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacleftarrow(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (y > e->revy - e->shei || y < e->revy - e->shei - DISPLAYSLIDERSIZE) return(0);
	if (x >= e->offx + DISPLAYSLIDERSIZE - LEFTINDENT || x < e->offx - LEFTINDENT) return(0);
	if (e->horizfactor <= 0) return(1);
	e->horizfactor--;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawhscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacrightpage(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (y > e->revy - e->shei || y < e->revy - e->shei - DISPLAYSLIDERSIZE) return(0);
	if (x <= e->hthumbpos+THUMBSIZE/2 || x > e->offx + e->swid - DISPLAYSLIDERSIZE) return(0);
	if (e->horizfactor >= e->screenchars*10) return(1);
	e->horizfactor += 10;
	if (e->horizfactor >= e->screenchars*10) e->horizfactor = e->screenchars*10;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawhscroll(us_editpaccurrentwin);
	return(0);
}

INTSML us_editpacleftpage(INTBIG x, INTBIG y)
{
	REGISTER EDITOR *e;

	e = us_editpaccurrentwin->editor;
	if (y > e->revy - e->shei || y < e->revy - e->shei - DISPLAYSLIDERSIZE) return(0);
	if (x >= e->hthumbpos-THUMBSIZE/2 || x < e->offx + DISPLAYSLIDERSIZE - LEFTINDENT) return(0);
	if (e->horizfactor <= 0) return(1);
	e->horizfactor -= 10;
	if (e->horizfactor <= 0) e->horizfactor = 0;
	us_editpacredrawlines(us_editpaccurrentwin);
	us_editpacdrawhscroll(us_editpaccurrentwin);
	return(0);
}

/******************** CHANGES TO EDIT BUFFER ********************/

/*
 * routine to implement character "i".  The key is issued internally if "fromuser"
 * is zero.  Routine returns nonzero if the editor window has been terminated.
 */
INTSML us_editpacimplementchar(WINDOWPART *win, INTSML i, INTSML fromuser)
{
	REGISTER EDITOR *e;
	INTBIG j, len;
	INTSML save;
	char *nextline;

	e = win->editor;
	if (e == NOEDITOR) return(1);

	/* ESCAPE quits the editor */
	if (i == ESCKEY) return(1);

	if (i == LEFTARROWKEY)
	{
		us_editpacdonetyping(e);
		if (e->curline == e->endline && e->curchar == e->endchar)
		{
			if (e->curchar != 0) e->curchar--; else
			{
				if (e->curline > 0)
				{
					e->curline--;
					e->curchar = strlen(e->textarray[e->curline]);
					us_editpacbeginwork(win);
				}
			}
		}
		e->endline = e->curline;
		e->endchar = e->curchar;
		e->charposition = e->endchar;
		return(0);
	}

	if (i == RIGHTARROWKEY)
	{
		us_editpacdonetyping(e);
		if (e->curline == e->endline && e->curchar == e->endchar)
		{
			if (e->textarray[e->endline][e->endchar] != 0) e->endchar++; else
			{
				if (e->endline < e->linecount-1)
				{
					e->endline++;
					e->endchar = 0;
					us_editpacbeginwork(win);
				}
			}
		}
		e->curline = e->endline;
		e->curchar = e->endchar;
		e->charposition = e->endchar;
		return(0);
	}

	if (i == UPARROWKEY)
	{
		us_editpacdonetyping(e);
		if (e->curline > 0)
		{
			e->curline--;
			len = strlen(e->textarray[e->curline]);
			if (e->charposition > e->curchar) e->curchar = e->charposition;
			if (e->curchar > len) e->curchar = len;
			e->endline = e->curline;
			e->endchar = e->curchar;
			us_editpacbeginwork(win);
		}
		return(0);
	}

	if (i == DOWNARROWKEY)
	{
		us_editpacdonetyping(e);
		if (e->endline < e->linecount-1)
		{
			e->endline++;
			len = strlen(e->textarray[e->endline]);
			if (e->charposition > e->endchar) e->endchar = e->charposition;
			if (e->endchar > len) e->endchar = len;
			e->curline = e->endline;
			e->curchar = e->endchar;
			us_editpacbeginwork(win);
		}
		return(0);
	}

	/* if delete key hit and nothing selected, set to erase previous character */
	if (i == DELETEKEY || i == BACKSPACEKEY)
	{
		if (e->curline == e->endline && e->curchar == e->endchar)
		{
			if (e->curchar > 0) e->curchar--; else
			{
				if (e->curline > 0)
				{
					e->curline--;
					e->curchar = strlen(e->textarray[e->curline]);
					us_editpacbeginwork(win);
				}
			}
		}
	}

	/* see if lines are fixed */
	if ((e->state&LINESFIXED) != 0)
	{
		/* disallow line insertion, deletion or any action on the last line */
		if (e->curline != e->endline || i == '\n' || i == '\r' || e->curline == e->linecount-1)
		{
			ttybeep();
			return(0);
		}
	}

	/* mark a dirty edit buffer */
	e->dirty = 1;
	us_editpacamtyping(e);
	if (i == DELETEKEY || i == BACKSPACEKEY) us_editpactypeddelete(e);

	/* remove the selected characters */
	us_editpacremoveselection(win);
	e->endline = e->curline;
	e->endchar = e->curchar;
	e->charposition = e->endchar;

	/* ignore delete or backspace characters */
	if (i == DELETEKEY || i == BACKSPACEKEY) return(0);

	/* self-insert of normal characters */
	if (i == '\t') i = ' ';
	if (i >= ' ' && i <= '~')
	{
		/* see if line needs to be extended */
		j = strlen(e->textarray[e->curline]) + 2;
		if (j > e->maxchars[e->curline])
			us_editpacaddmorechars(e, e->curline, j+10);

		/* see if new character is at end of line */
		if (e->textarray[e->curline][e->curchar] != 0)
		{
			/* it is not: shift letters on the line */
			len = strlen(e->textarray[e->curline]);
			for(j = len+1; j > e->curchar; j--)
				e->textarray[e->curline][j] = e->textarray[e->curline][j-1];
		} else e->textarray[e->curline][e->curchar+1] = 0;

		/* clear from here to the end of the line */
		if (e->curline-e->firstline < e->screenlines && e->curline >= e->firstline &&
			(e->state&EGRAPHICSOFF) == 0)
		{
			e->textarray[e->curline][e->curchar] = 0;
			us_editpaccleanupline(win, e->curline);
		}

		/* insert the character */
		e->textarray[e->curline][e->curchar] = (char)i;

		/* put the rest of the line on the display */
		if (e->curline-e->firstline < e->screenlines && e->curline >= e->firstline &&
			e->curchar-e->horizfactor < e->screenchars && (e->state&EGRAPHICSOFF) == 0)
		{
			us_editpactext(win, &e->textarray[e->curline][e->curchar], (INTSML)e->curchar,
				(INTSML)(e->curline-e->firstline));
		}

		/* advance the character pointer */
		e->curchar++;   e->endchar++;
		e->charposition = e->endchar;
		return(0);
	}

	if (i == '\n' || i == '\r')
	{
		/* exit editor if only editing one line */
		if ((win->state&WINDOWTYPE) == POPTEXTWINDOW) return(1);

		/* see if there is room in the file */
		if (e->linecount >= e->maxlines) us_editpacaddmorelines(e);

		if (fromuser != 0)
		{
			if (e->curline == e->firstline+e->screenlines-1)
			{
				e->firstline++;
				if ((e->state&EGRAPHICSOFF) == 0)
				{
					us_editpacmovebox(win, 0, 0, e->swid, (INTSML)(us_thei*(e->screenlines-1)), 0, 1);
					us_editpacclearbox(win, 0, (INTSML)(e->screenlines-1), e->swid, us_thei);
					if (e->screenlines+e->firstline <= e->linecount)
						us_editpactext(win, e->textarray[e->screenlines-1+e->firstline], 0,
							(INTSML)(e->screenlines-1));
				}
			}
		}

		/* shift lines down */
		for(j = e->linecount; j > e->curline; j--)
		{
			if (j == e->curline+1) nextline = &e->textarray[e->curline][e->curchar]; else
				nextline = e->textarray[j-1];
			len = strlen(nextline) + 1;
			if (len > e->maxchars[j])
				us_editpacaddmorechars(e, j, len);
			(void)strcpy(e->textarray[j], nextline);
		}
		e->textarray[e->curline][e->curchar] = 0;
		if ((e->state&EGRAPHICSOFF) == 0 && e->curline-e->firstline < e->screenlines &&
			e->curline >= e->firstline) us_editpaccleanupline(win, e->curline);
		e->curline = e->endline = e->curline+1;
		e->linecount++;
		e->curchar = 0;   e->endchar = 0;

		/* ship changes on the current line */
		us_editpacshipchanges(win);
		if (win->changehandler != 0)
			(*win->changehandler)(win, INSERTTEXTLINE, "", e->textarray[e->curline], e->curline);
		us_editpacbeginwork(win);

		/* shift lines down */
		if (e->curline+1-e->firstline < e->screenlines &&
			e->curline+1 >= e->firstline && (e->state&EGRAPHICSOFF) == 0)
				us_editpacmovebox(win, 0, (INTSML)(e->curline+1-e->firstline),
					e->swid, (INTSML)(e->shei-(e->curline+1-e->firstline)*us_thei), 0,
						(INTSML)(e->curline-e->firstline));

		/* draw current line */
		if (e->curline-e->firstline < e->screenlines &&
			e->curline >= e->firstline && (e->state&EGRAPHICSOFF) == 0)
		{
			save = e->textarray[e->curline][0];
			e->textarray[e->curline][0] = 0;
			us_editpaccleanupline(win, e->curline);
			e->textarray[e->curline][0] = (char)save;
			us_editpactext(win, e->textarray[e->curline], 0, (INTSML)(e->curline-e->firstline));
		}
		if ((e->state&EGRAPHICSOFF) == 0) us_editpacdrawvscroll(win);
		e->charposition = e->endchar;
		return(0);
	}

	return(0);
}

void us_editpacremoveselection(WINDOWPART *win)
{
	REGISTER EDITOR *e;
	REGISTER INTBIG j, k, diff;
	REGISTER INTSML save;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* nothing to do if there is a null selection */
	diff = e->endline - e->curline;
	if (e->curline == e->endline && e->curchar == e->endchar) return;


	/* report the deletion */
	if (win->changehandler != 0)
	{
		/* send out changes made so far if this is a multiline delete */
		if (diff > 0) us_editpacshipchanges(win);
		for(j=diff-1; j>=0; j--)
			(*win->changehandler)(win, DELETETEXTLINE, e->textarray[e->curline+j], "",
				e->curline+j);
	}

	/* append text beyond end of selection to the start of the selection */
	j = strlen(&e->textarray[e->endline][e->endchar]) + e->curchar + 1;
	if (j > e->maxchars[e->curline])
		us_editpacaddmorechars(e, e->curline, j);
	(void)strcpy(&e->textarray[e->curline][e->curchar],
		&e->textarray[e->endline][e->endchar]);

	/* redraw this line */
	if (e->curline-e->firstline < e->screenlines && e->curline >= e->firstline &&
		e->curchar-e->horizfactor < e->screenchars-1 && (e->state&EGRAPHICSOFF) == 0)
	{
		save = e->textarray[e->curline][e->curchar];
		e->textarray[e->curline][e->curchar] = 0;
		us_editpaccleanupline(win, e->curline);
		e->textarray[e->curline][e->curchar] = (char)save;
		us_editpactext(win, &e->textarray[e->curline][e->curchar], (INTSML)e->curchar,
			(INTSML)(e->curline-e->firstline));
	}

	/* now shift up text (if a multiline delete) */
	if (diff > 0)
	{
		for(j=e->curline+1; j<e->linecount-diff; j++)
		{
			/* make sure there is room for the shifted line */
			k = strlen(e->textarray[j+diff]) + 1;
			if (k > e->maxchars[j]) us_editpacaddmorechars(e, j, k);

			/* copy new line */
			if (j-e->firstline < e->screenlines && j >= e->firstline &&
				(e->state&EGRAPHICSOFF) == 0)
			{
				/* erase this line */
				e->textarray[j][0] = 0;
				us_editpaccleanupline(win, j);

				(void)strcpy(e->textarray[j], e->textarray[j+diff]);
				us_editpactext(win, e->textarray[j], 0, (INTSML)(j-e->firstline));
			} else (void)strcpy(e->textarray[j], e->textarray[j+diff]);
		}
		for(j=e->linecount-diff; j<e->linecount; j++)
		{
			e->textarray[j][0] = 0;
			if (j-e->firstline < e->screenlines && j >= e->firstline && (e->state&EGRAPHICSOFF) == 0)
				us_editpaccleanupline(win, j);
		}
		e->linecount -= diff;
		us_editpacbeginwork(win);
	}
}

/******************** DISPLAY OF EDIT WINDOW ********************/

/*
 * routine to redisplay the text screen
 */
void us_editpacredrawscreen(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	if ((e->state&EGRAPHICSOFF) != 0) return;

	/* put header on display */
	us_editpacsetheader(win, e->header);
	us_drawsliders(win);

	us_editpacredrawlines(win);
}

void us_drawsliders(WINDOWPART *win)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* draw horizontal slider */
	us_drawhorizontalslider(win, (INTSML)(win->usely+DISPLAYSLIDERSIZE), win->uselx,
		(INTSML)(win->usehx-DISPLAYSLIDERSIZE));
	us_editpacdrawhscroll(win);

	/* draw vertical slider if appropriate */
	if ((win->state&WINDOWTYPE) != POPTEXTWINDOW)
	{
		us_drawverticalslider(win, (INTSML)(win->usehx-DISPLAYSLIDERSIZE),
			(INTSML)(win->usely+DISPLAYSLIDERSIZE), (INTSML)(win->usehy-us_thei*HEADERLINES), 0);

		/* the corner */
		us_drawslidercorner(win, win->usehx-DISPLAYSLIDERSIZE+1, win->usehx, win->usely,
			win->usely+DISPLAYSLIDERSIZE-1);
		us_editpacdrawvscroll(win);
	}
}

/*
 * Routine to set the horizontal scroll bar
 */
void us_editpacdrawhscroll(WINDOWPART *win)
{
	INTSML left, right;
	INTBIG f;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* get the area of the slider without arrows */
	left = e->offx+DISPLAYSLIDERSIZE-LEFTINDENT;
	right = e->offx + e->swid - DISPLAYSLIDERSIZE;

	/* compute position of vertical thumb area */
	f = e->horizfactor;   f *= (INTBIG)(right-left-THUMBSIZE);   f /= e->screenchars*10;
	e->hthumbpos = (INTSML)(left + THUMBSIZE/2 + f);
	if (e->hthumbpos > right-THUMBSIZE/2) e->hthumbpos = right-THUMBSIZE/2;

	/* draw the thumb */
	win->thumblx = e->hthumbpos - THUMBSIZE/2;   win->thumbhx = e->hthumbpos + THUMBSIZE/2;
	us_drawhorizontalsliderthumb(win, win->usely+DISPLAYSLIDERSIZE, win->uselx,
		win->usehx-DISPLAYSLIDERSIZE, win->thumblx, win->thumbhx);
}

/*
 * Routine to set the vertical scroll bar
 */
void us_editpacdrawvscroll(WINDOWPART *win)
{
	INTSML top, bottom;
	INTBIG den;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	/* get the area of the slider without arrows */
	top = e->revy - DISPLAYSLIDERSIZE;
	bottom = e->revy - e->shei + DISPLAYSLIDERSIZE;

	/* if there is nothing to scroll, clear this area */
	if (e->linecount <= e->screenlines)
	{
		us_drawverticalsliderthumb(win, win->usehx-DISPLAYSLIDERSIZE, win->usely+DISPLAYSLIDERSIZE,
			win->usehy-us_thei*HEADERLINES, 1, 0);
		return;
	}

	/* compute position of vertical thumb area */
	den = e->linecount - e->screenlines;
	e->vthumbpos = (INTSML)(e->revy - DISPLAYSLIDERSIZE - THUMBSIZE/2 -
		(e->firstline * (e->shei-DISPLAYSLIDERSIZE*2-THUMBSIZE) + den/2) / den);
	if (e->vthumbpos >= top-THUMBSIZE/2) e->vthumbpos = top-THUMBSIZE/2-1;
	if (e->vthumbpos <= bottom+THUMBSIZE/2) e->vthumbpos = bottom+THUMBSIZE/2+1;

	win->thumbly = e->vthumbpos - THUMBSIZE/2;
	win->thumbhy = e->vthumbpos + THUMBSIZE/2;
	us_drawverticalsliderthumb(win, win->usehx-DISPLAYSLIDERSIZE, win->usely+DISPLAYSLIDERSIZE,
		win->usehy-us_thei*HEADERLINES, win->thumbly, win->thumbhy);
}

/*
 * routine to clear the bits from the last character to the end of the line
 */
void us_editpaccleanupline(WINDOWPART *win, INTBIG line)
{
	REGISTER INTBIG residue, linelen;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	linelen = strlen(e->textarray[line]);
	residue = e->swid-(linelen - e->horizfactor)*us_twid;
	if (residue <= 0) return;
	us_editpacclearbox(win, (INTSML)(linelen - e->horizfactor), (INTSML)(line-e->firstline),
		(INTSML)residue, us_thei);
}

/*
 * routine to redisplay the text screen
 */
void us_editpacredrawlines(WINDOWPART *win)
{
	REGISTER INTBIG j;
	REGISTER INTSML bottom;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	if ((e->state&EGRAPHICSOFF) != 0) return;

	/* clear the screen */
	bottom = e->revy - e->shei;
	screendrawbox(win, (INTSML)(e->offx-LEFTINDENT), (INTSML)(e->offx+e->swid), bottom, e->revy,
		&us_ebox);

	/* rewrite each line */
	for(j=0; j<e->screenlines; j++) if (j+e->firstline < e->linecount)
		us_editpactext(win, e->textarray[j+e->firstline], 0, (INTSML)j);
	us_editpacinvertselection(win);
}

/*
 * routine to write the header string
 */
void us_editpacsetheader(WINDOWPART *win, char *header)
{
	REGISTER INTSML save, formerhf, bottom;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	screendrawbox(win, win->uselx, win->usehx, (INTSML)(e->revy+1),
		(INTSML)(e->revy+us_thei*HEADERLINES), &us_ebox);
	if ((INTBIG)strlen(header) > e->screenchars)
	{
		save = header[e->screenchars];
		header[e->screenchars] = 0;
	} else save = 0;
	formerhf = e->horizfactor;
	e->horizfactor = 0;
	us_editpactext(win, header, 0, -2);
	if ((win->state&WINDOWTYPE) == POPTEXTWINDOW)
		us_editpactext(win, _("Point-and-Click editor: type RETURN when done"), 0, -1); else
	{
		if (strcmp(win->location, "entire") != 0)
			us_editpactext(win, _("Point-and-Click editor: type ESC when done"), 0, -1); else
		{
			if (graphicshas(CANUSEFRAMES) != 0)
				us_editpactext(win, _("Point-and-Click editor: close the window when done"), 0, -1); else
					us_editpactext(win, _("Point-and-Click editor: text-only facet"), 0, -1);
		}
	}
	e->horizfactor = formerhf;

	if (save != 0) header[e->screenchars] = (char)save;
	bottom = e->revy - e->shei - 1;
	screeninvertbox(win, win->uselx, win->usehx, (INTSML)(e->revy+1),
		(INTSML)(e->revy+us_thei*HEADERLINES));
	us_menufigs.col = el_colmengly;
	screendrawline(win, win->uselx, e->revy, win->uselx, bottom, &us_menufigs, 0);
	if ((win->state&WINDOWTYPE) == POPTEXTWINDOW)
	{
		screendrawline(win, win->uselx, win->usely, win->uselx, win->usehy, &us_menufigs, 0);
		screendrawline(win, win->uselx, win->usely, win->usehx, win->usely, &us_menufigs, 0);
		screendrawline(win, win->usehx, win->usely, win->usehx, win->usehy, &us_menufigs, 0);
	}
}

/*
 * routine to invert the selected text, turning it on or off
 */
void us_editpacinvertselection(WINDOWPART *win)
{
	INTBIG i, startchar, endchar, ll, lc, hl, hc, lx, hx, ly, hy;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	if ((e->state&EGRAPHICSOFF) != 0) return;
	ll = e->curline;   lc = e->curchar;
	hl = e->endline;   hc = e->endchar;
	if (ll > hl || (ll == hl && lc > hc))
	{
		i = hl;   hl = ll;   ll = i;
		i = hc;   hc = lc;   lc = i;
	}
	for(i = ll; i <= hl; i++)
	{
		if (i < e->firstline) continue;
		if (i-e->firstline >= e->screenlines) continue;
		if (i == ll) startchar = lc; else startchar = 0;
		if (i == hl) endchar = hc; else endchar = e->mostchars+e->horizfactor;
		if (endchar == 0 && ll != hl) continue;

		/* compute bounds of box to invert */
		lx = e->offx + (startchar-e->horizfactor) * us_twid - 1;
		hx = e->offx + (endchar-e->horizfactor) * us_twid - 1;
		ly = e->revy - (i-e->firstline) * us_thei - us_thei;
		hy = e->revy - (i-e->firstline) * us_thei - 1;

		/* limit to display */
		if (lx > e->offx + e->swid || hx < e->offx-1) continue;
		if (lx < e->offx-1) lx = e->offx-1;
		if (hx > e->offx + e->swid) hx = e->offx + e->swid;
		if (ly > e->revy || hy < e->revy - e->shei) continue;
		if (ly < e->revy - e->shei) ly = e->revy - e->shei;
		if (hy > e->revy) hy = e->revy;

		screeninvertbox(win, (INTSML)lx, (INTSML)hx, (INTSML)ly, (INTSML)hy);
	}
}

/******************** UNDO CONTROL ********************/

void us_editpacdonetyping(EDITOR *e)
{
	if ((e->state&TEXTTYPING) == 0) return;

	if (e->curline < us_editpacundonewcurline ||
		(e->curline == us_editpacundonewcurline && e->curchar < us_editpacundonewcurchar))
	{
		us_editpacundonewcurline = e->curline;    us_editpacundonewcurchar = e->curchar;
	}
	if (e->endline > us_editpacundonewendline ||
		(e->endline == us_editpacundonewendline && e->endchar > us_editpacundonewendchar))
	{
		us_editpacundonewendline = e->endline;    us_editpacundonewendchar = e->endchar;
	}

	e->state &= ~TEXTTYPING;
}

void us_editpacamtyping(EDITOR *e)
{
	REGISTER INTBIG len;

	if ((e->state&TEXTTYPING) != 0)
	{
		if (e->curline < us_editpacundonewcurline ||
			(e->curline == us_editpacundonewcurline && e->curchar < us_editpacundonewcurchar))
		{
			us_editpacundonewcurline = e->curline;    us_editpacundonewcurchar = e->curchar;
		}
		if (e->endline > us_editpacundonewendline ||
			(e->endline == us_editpacundonewendline && e->endchar > us_editpacundonewendchar))
		{
			us_editpacundonewendline = e->endline;    us_editpacundonewendchar = e->endchar;
		}
		return;
	}
	e->state |= TEXTTYPING | TEXTTYPED;

	/* start of typing: save current position for what is typed */
	us_editpacundonewcurline = us_editpacundonewendline = e->curline;
	us_editpacundonewcurchar = us_editpacundonewendchar = e->curchar;

	/* remember former selection */
	us_editpacundooldcurline = e->curline;    us_editpacundooldcurchar = e->curchar;

	/* save what is selected */
	len = us_editpaccharstocurrent(e, e->endchar, e->endline) + 1;
	if (len > us_editpacundobuffersize)
	{
		if (us_editpacundobuffersize != 0) efree(us_editpacundobuffer);
		us_editpacundobuffersize = 0;
		us_editpacundobuffer = (char *)emalloc(len, us_aid->cluster);
		if (us_editpacundobuffer == 0) return;
		us_editpacundobuffersize = len;
	}
	us_editpacloadselection(e, us_editpacundobuffer);
}

void us_editpactypeddelete(EDITOR *e)
{
	REGISTER INTBIG len, i;
	REGISTER char *newbuffer;

	if ((e->state&TEXTTYPING) == 0) return;

	/* if still beyond start of typing, quit */
	if (e->curline > us_editpacundooldcurline ||
		(e->curline == us_editpacundooldcurline && e->curchar >= us_editpacundooldcurchar))
			return;

	/* make sure there is room for one more character */
	len = strlen(us_editpacundobuffer) +
		us_editpaccharstocurrent(e, us_editpacundooldcurchar, us_editpacundooldcurline) + 1;
	if (len > us_editpacundobuffersize)
	{
		newbuffer = (char *)emalloc(len, us_aid->cluster);
		if (newbuffer == 0) return;
		(void)strcpy(newbuffer, us_editpacundobuffer);
		if (us_editpacundobuffersize > 0) efree(us_editpacundobuffer);
		us_editpacundobuffer = newbuffer;
		us_editpacundobuffersize = len;
	}

	/* shift the buffer up */
	for(i=len-1; i>0; i--) us_editpacundobuffer[i] = us_editpacundobuffer[i-1];
	us_editpacundobuffer[0] = e->textarray[e->curline][e->curchar];
	if (us_editpacundobuffer[0] == 0) us_editpacundobuffer[0] = '\n';

	us_editpacundooldcurline = e->curline;    us_editpacundooldcurchar = e->curchar;

	us_editpacundonewcurline = us_editpacundonewendline = e->curline;
	us_editpacundonewcurchar = us_editpacundonewendchar = e->curchar;
}

INTBIG us_editpaccharstocurrent(EDITOR *e, INTBIG endchar, INTBIG endline)
{
	REGISTER INTBIG len, line, startchar, startline, fromchar, tochar;

	if (e->curline > endline || (e->curline == endline && e->curchar > endchar))
	{
		startchar = endchar;   endchar = e->curchar;
		startline = endline;   endline = e->curline;
	} else
	{
		startchar = e->curchar;
		startline = e->curline;
	}
	len = 0;
	for(line = startline; line <= endline; line++)
	{
		if (line == startline) fromchar = startchar; else fromchar = 0;
		if (line == endline) tochar = endchar; else
			tochar = strlen(e->textarray[line])+1;
		len += tochar - fromchar;
	}
	return(len);
}

void us_editpacloadselection(EDITOR *e, char *buf)
{
	REGISTER INTBIG len, line, startch, endch, i, ch;

	len = 0;
	for(line = e->curline; line <= e->endline; line++)
	{
		if (line == e->curline) startch = e->curchar; else startch = 0;
		if (line == e->endline) endch = e->endchar; else endch = strlen(e->textarray[line])+1;
		for(i=startch; i<endch; i++)
		{
			ch = e->textarray[line][i];
			if (ch == 0) ch = '\n';
			buf[len++] = (char)ch;
		}
	}
	buf[len] = 0;
}

/******************** SUPPORT ********************/

/*
 * routine to determine the extents of the editing window
 */
void us_editpaccomputebounds(WINDOWPART *win, EDITOR *e)
{
	REGISTER INTSML offset;

	e->offx = win->uselx + 1 + LEFTINDENT;
	e->swid = win->usehx - e->offx - 1;
	offset = 1;
	if ((win->state&WINDOWTYPE) != POPTEXTWINDOW) e->swid -= DISPLAYSLIDERSIZE;
		else offset++;
	e->shei = win->usehy - win->usely - us_thei*HEADERLINES - offset - DISPLAYSLIDERSIZE;
	e->revy = e->shei + win->usely + offset + DISPLAYSLIDERSIZE;
	e->screenlines = e->shei / us_thei;
	e->screenchars = e->swid / us_twid;
}

void us_editpacbeginwork(WINDOWPART *win)
{
	EDITOR *e;
	REGISTER char *newline;
	REGISTER INTBIG len;

	e = win->editor;
	if (e == NOEDITOR) return;
	if (e->working == e->curline) return;
	us_editpacshipchanges(win);
	e->working = e->curline;

	/* make sure there is room in the line-change buffer */
	len = strlen(e->textarray[e->working]);
	if (len >= e->mostchars)
	{
		newline = (char *)emalloc(len+1, us_aid->cluster);
		if (newline == 0) return;
		efree(e->formerline);
		e->formerline = newline;
		e->mostchars = (INTSML)len;
	}

	/* save the current line */
	(void)strcpy(e->formerline, e->textarray[e->working]);
}

/*
 * routine to double the number of lines in the text buffer
 */
void us_editpacaddmorelines(EDITOR *e)
{
	REGISTER INTBIG oldlines, i;
	REGISTER INTSML *maxchars;
	REGISTER char **textarray;

	/* save former buffer size, double it */
	oldlines = e->maxlines;
	e->maxlines *= 2;
	if (e->maxlines <= 0) e->maxlines = 1;

	/* allocate new arrays */
	maxchars = (INTSML *)emalloc((e->maxlines * SIZEOFINTSML), us_aid->cluster);
	if (maxchars == 0) ttyputnomemory();
	textarray = (char **)emalloc((e->maxlines * (sizeof (char *))), us_aid->cluster);
	if (textarray == 0) ttyputnomemory();

	/* copy old information */
	for(i=0; i<e->maxlines; i++)
	{
		if (i >= oldlines)
		{
			textarray[i] = (char *)emalloc(DEFAULTWID, us_aid->cluster);
			if (textarray[i] == 0) ttyputnomemory();
			textarray[i][0] = 0;
			maxchars[i] = DEFAULTWID;
		} else
		{
			textarray[i] = e->textarray[i];
			maxchars[i] = e->maxchars[i];
		}
	}

	/* free old arrays */
	efree((char *)e->textarray);
	efree((char *)e->maxchars);

	/* setup pointers correctly */
	e->textarray = textarray;
	e->maxchars = maxchars;
}

/*
 * routine to increase the number of characters in line "line" to "need" (+1 for the null)
 */
void us_editpacaddmorechars(EDITOR *e, INTBIG line, INTBIG need)
{
	REGISTER char *oldline;

	/* extend the current line */
	if (line >= e->maxlines) return;
	oldline = e->textarray[line];
	e->maxchars[line] = need+1;
	e->textarray[line] = (char *)emalloc(need+1, us_aid->cluster);
	(void)strcpy(e->textarray[line], oldline);
	efree(oldline);
}

/******************** LOW LEVEL GRAPHICS ********************/

/*
 * move text starting at character position (sx,sy) to character position
 * (dx, dy).
 */
void us_editpacmovebox(WINDOWPART *win, INTSML dx, INTSML dy, INTSML wid, INTSML hei, INTSML sx,
	INTSML sy)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;
	screenmovebox(win, (INTSML)(sx*us_twid+e->offx), (INTSML)(e->revy-(sy*us_thei+hei-1)), wid, hei,
		(INTSML)(dx*us_twid+e->offx), (INTSML)(e->revy-(dy*us_thei+hei-1)));
}

/*
 * erase text starting at character position (dx,dy) for (wid, hei)
 */
void us_editpacclearbox(WINDOWPART *win, INTSML dx, INTSML dy, INTSML wid, INTSML hei)
{
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	dx = (dx * us_twid) + e->offx;
	dy *= us_thei;
	screendrawbox(win, dx, (INTSML)(dx+wid-1), (INTSML)(e->revy-(dy+hei-1)), (INTSML)(e->revy-dy),
		&us_ebox);
}

/*
 * routine to invert a box about the dragged thumb point
 */
void us_editpacinvertrectframe(WINDOWPART *win, INTSML x, INTSML y)
{
	screeninvertbox(win, x, (INTSML)(x+THUMBSIZE), y, y);
	screeninvertbox(win, x, (INTSML)(x+THUMBSIZE), (INTSML)(y+THUMBSIZE), (INTSML)(y+THUMBSIZE));
	screeninvertbox(win, (INTSML)(x+THUMBSIZE), (INTSML)(x+THUMBSIZE), (INTSML)(y+1),
		(INTSML)(y+THUMBSIZE-1));
	screeninvertbox(win, x, x, (INTSML)(y+1), (INTSML)(y+THUMBSIZE-1));
}

/*
 * write text "str" starting at character position (x,y)
 */
void us_editpactext(WINDOWPART *win, char *str, INTSML x, INTSML y)
{
	INTSML xpos, len, diff, i, clip;
	REGISTER EDITOR *e;

	e = win->editor;
	if (e == NOEDITOR) return;

	len = strlen(str);
	diff = e->horizfactor - x;

	/* make sure string doesn't start to left of edit window */
	if (diff > 0)
	{
		if (diff > len) return;
		str = &str[diff];
		x = e->horizfactor;
		len -= diff;
	}

	/* make sure string doesn't start to the right of the edit window */
	if (x-e->horizfactor >= e->screenchars) return;

	xpos = (x - e->horizfactor) * us_twid;
	us_menutext.col = el_colmentxt;

	if (len + x - e->horizfactor > e->screenchars)
	{
		(void)initinfstr();
		clip = e->screenchars + e->horizfactor - x;
		for(i=0; i<clip; i++) (void)addtoinfstr(str[i]);
		str = returninfstr();
	}
	screendrawtext(win, (INTSML)(xpos + e->offx), (INTSML)(e->revy-(y+1)*us_thei), str, &us_menutext);
}
