xref: /plan9-contrib/sys/src/cmd/tweak.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
33e12c5d1SDavid du Colombier #include <libg.h>
43e12c5d1SDavid du Colombier #include <bio.h>
53e12c5d1SDavid du Colombier 
63e12c5d1SDavid du Colombier /*
73e12c5d1SDavid du Colombier  * This program keeps the system's and the application's bitmap and
83e12c5d1SDavid du Colombier  * subfont structures consistent, but that is really not necessary;
93e12c5d1SDavid du Colombier  * in fact it isn't even necessary to hold the subfonts in the system
103e12c5d1SDavid du Colombier  * at all.  Doing it this way makes it easy to do I/O on subfonts.
113e12c5d1SDavid du Colombier  * Even so, edits to individual character's Fontchars are not passed
123e12c5d1SDavid du Colombier  * to the system.
133e12c5d1SDavid du Colombier  */
143e12c5d1SDavid du Colombier 
153e12c5d1SDavid du Colombier typedef struct	Thing	Thing;
163e12c5d1SDavid du Colombier 
173e12c5d1SDavid du Colombier struct Thing
183e12c5d1SDavid du Colombier {
193e12c5d1SDavid du Colombier 	Bitmap	*b;
203e12c5d1SDavid du Colombier 	Subfont *s;
213e12c5d1SDavid du Colombier 	char	*name;		/* file name */
223e12c5d1SDavid du Colombier 	int	face;		/* is 48x48 face file or cursor file*/
233e12c5d1SDavid du Colombier 	Rectangle r;		/* drawing region */
243e12c5d1SDavid du Colombier 	Rectangle tr;		/* text region */
253e12c5d1SDavid du Colombier 	Rectangle er;		/* entire region */
263e12c5d1SDavid du Colombier 	long	c;		/* character number in subfont */
273e12c5d1SDavid du Colombier 	int	mod;		/* modified */
283e12c5d1SDavid du Colombier 	int	mag;		/* magnification */
293e12c5d1SDavid du Colombier 	Rune	off;		/* offset for subfont indices */
303e12c5d1SDavid du Colombier 	Thing	*parent;	/* thing of which i'm an edit */
313e12c5d1SDavid du Colombier 	Thing	*next;
323e12c5d1SDavid du Colombier };
333e12c5d1SDavid du Colombier 
343e12c5d1SDavid du Colombier enum
353e12c5d1SDavid du Colombier {
363e12c5d1SDavid du Colombier 	Border	= 1,
373e12c5d1SDavid du Colombier 	Up	= 1,
383e12c5d1SDavid du Colombier 	Down	= 0,
393e12c5d1SDavid du Colombier 	Mag	= 4,
403e12c5d1SDavid du Colombier 	Maxmag	= 10,
413e12c5d1SDavid du Colombier };
423e12c5d1SDavid du Colombier 
433e12c5d1SDavid du Colombier enum
443e12c5d1SDavid du Colombier {
453e12c5d1SDavid du Colombier 	Mopen,
463e12c5d1SDavid du Colombier 	Mread,
473e12c5d1SDavid du Colombier 	Mwrite,
483e12c5d1SDavid du Colombier 	Mcopy,
493e12c5d1SDavid du Colombier 	Mchar,
503e12c5d1SDavid du Colombier 	Mclose,
513e12c5d1SDavid du Colombier 	Mexit,
523e12c5d1SDavid du Colombier };
533e12c5d1SDavid du Colombier 
543e12c5d1SDavid du Colombier 
553e12c5d1SDavid du Colombier char	*menu3str[] = {
563e12c5d1SDavid du Colombier 	[Mopen]		"open",
573e12c5d1SDavid du Colombier 	[Mread]		"read",
583e12c5d1SDavid du Colombier 	[Mwrite]	"write",
593e12c5d1SDavid du Colombier 	[Mcopy]		"copy",
603e12c5d1SDavid du Colombier 	[Mchar]		"char",
613e12c5d1SDavid du Colombier 	[Mclose]	"close",
623e12c5d1SDavid du Colombier 	[Mexit]		"exit",
633e12c5d1SDavid du Colombier 	0,
643e12c5d1SDavid du Colombier };
653e12c5d1SDavid du Colombier 
663e12c5d1SDavid du Colombier Menu	menu3 = {
673e12c5d1SDavid du Colombier 	menu3str
683e12c5d1SDavid du Colombier };
693e12c5d1SDavid du Colombier 
703e12c5d1SDavid du Colombier Cursor sweep0 = {
713e12c5d1SDavid du Colombier 	{-7, -7},
723e12c5d1SDavid du Colombier 	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
733e12c5d1SDavid du Colombier 	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
743e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
753e12c5d1SDavid du Colombier 	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0},
763e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
773e12c5d1SDavid du Colombier 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
783e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
793e12c5d1SDavid du Colombier 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00}
803e12c5d1SDavid du Colombier };
813e12c5d1SDavid du Colombier 
823e12c5d1SDavid du Colombier Cursor box = {
833e12c5d1SDavid du Colombier 	{-7, -7},
843e12c5d1SDavid du Colombier 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
853e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
863e12c5d1SDavid du Colombier 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
873e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
883e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
893e12c5d1SDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
903e12c5d1SDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
913e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
923e12c5d1SDavid du Colombier };
933e12c5d1SDavid du Colombier 
943e12c5d1SDavid du Colombier Cursor sight = {
953e12c5d1SDavid du Colombier 	{-7, -7},
963e12c5d1SDavid du Colombier 	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
973e12c5d1SDavid du Colombier 	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
983e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
993e12c5d1SDavid du Colombier 	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
1003e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
1013e12c5d1SDavid du Colombier 	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
1023e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
1033e12c5d1SDavid du Colombier 	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
1043e12c5d1SDavid du Colombier };
1053e12c5d1SDavid du Colombier 
1063e12c5d1SDavid du Colombier Cursor busy = {
1073e12c5d1SDavid du Colombier 	{-7, -7},
1083e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1093e12c5d1SDavid du Colombier 	 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7,
1103e12c5d1SDavid du Colombier 	 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe,
1113e12c5d1SDavid du Colombier 	 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,},
1123e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1133e12c5d1SDavid du Colombier 	 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
1143e12c5d1SDavid du Colombier 	 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe,
1153e12c5d1SDavid du Colombier 	 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,}
1163e12c5d1SDavid du Colombier };
1173e12c5d1SDavid du Colombier 
1183e12c5d1SDavid du Colombier Cursor skull = {
1193e12c5d1SDavid du Colombier 	{-7,-7},
1203e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7,
1213e12c5d1SDavid du Colombier 	 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8,
1223e12c5d1SDavid du Colombier 	 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff,
1233e12c5d1SDavid du Colombier 	 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
1243e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
1253e12c5d1SDavid du Colombier 	 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
1263e12c5d1SDavid du Colombier 	 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
1273e12c5d1SDavid du Colombier 	 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
1283e12c5d1SDavid du Colombier };
1293e12c5d1SDavid du Colombier 
1303e12c5d1SDavid du Colombier char	*fns[] = {
1313e12c5d1SDavid du Colombier 	"0",      "Zero",		0,
1323e12c5d1SDavid du Colombier 	"~(S|D)", "~(D|S)", "DnorS",	0,
1333e12c5d1SDavid du Colombier 	"~S&D",   "D&~S",   "DandnotS",	0,
1343e12c5d1SDavid du Colombier 	"~S",     "notS",		0,
1353e12c5d1SDavid du Colombier 	"S&~D",   "~D&S",   "notDandS",	0,
1363e12c5d1SDavid du Colombier 	"~D",     "notD",		0,
1373e12c5d1SDavid du Colombier 	"S^D",    "D^S",    "DxorS",	0,
1383e12c5d1SDavid du Colombier 	"~(S&D)", "~(D&S)", "DnandS",	0,
1393e12c5d1SDavid du Colombier 	"S&D",    "D&S",    "DandS",	0,
1403e12c5d1SDavid du Colombier 	"~(S^D)", "~(D^S)", "DxnorS",	0,
1413e12c5d1SDavid du Colombier 	"D",				0,
1423e12c5d1SDavid du Colombier 	"~S|D",   "D|~S",   "DornotS",	0,
1433e12c5d1SDavid du Colombier 	"S",				0,
1443e12c5d1SDavid du Colombier 	"S|~D",   "~D|S",   "notDorS",	0,
1453e12c5d1SDavid du Colombier 	"S|D",    "D|S",    "DorS",	0,
1463e12c5d1SDavid du Colombier 	"F",				0,
1473e12c5d1SDavid du Colombier 	0
1483e12c5d1SDavid du Colombier };
1493e12c5d1SDavid du Colombier 
1503e12c5d1SDavid du Colombier Rectangle	cntlr;		/* control region */
1513e12c5d1SDavid du Colombier Rectangle	editr;		/* editing region */
1523e12c5d1SDavid du Colombier Rectangle	textr;		/* text region */
1533e12c5d1SDavid du Colombier Thing		*thing;
1543e12c5d1SDavid du Colombier Mouse		mouse;
1553e12c5d1SDavid du Colombier char		hex[] = "0123456789abcdefABCDEF";
1563e12c5d1SDavid du Colombier jmp_buf		err;
1573e12c5d1SDavid du Colombier char		*file;
1583e12c5d1SDavid du Colombier int		mag;
1593e12c5d1SDavid du Colombier uint		copyfn = S;
1603e12c5d1SDavid du Colombier uint		but1fn = S;
1613e12c5d1SDavid du Colombier uint		but2fn = 0;
1623e12c5d1SDavid du Colombier Bitmap		*values;
1633e12c5d1SDavid du Colombier ulong		val = ~0;
1643e12c5d1SDavid du Colombier uchar		data[8192];
1653e12c5d1SDavid du Colombier 
1663e12c5d1SDavid du Colombier Thing*	tget(char*);
1673e12c5d1SDavid du Colombier void	mesg(char*, ...);
1683e12c5d1SDavid du Colombier void	draw(Thing*, int);
1693e12c5d1SDavid du Colombier void	select(void);
1703e12c5d1SDavid du Colombier void	menu(void);
1713e12c5d1SDavid du Colombier void	error(char*);
1723e12c5d1SDavid du Colombier void	buttons(int);
1733e12c5d1SDavid du Colombier char	*fntostr(uint);
1743e12c5d1SDavid du Colombier int	strtofn(char*);
1753e12c5d1SDavid du Colombier void	drawall(void);
1763e12c5d1SDavid du Colombier void	tclose1(Thing*);
1773e12c5d1SDavid du Colombier 
1783e12c5d1SDavid du Colombier void
1793e12c5d1SDavid du Colombier main(int argc, char *argv[])
1803e12c5d1SDavid du Colombier {
1813e12c5d1SDavid du Colombier 	int i, j;
1823e12c5d1SDavid du Colombier 	Event e;
1833e12c5d1SDavid du Colombier 	Thing *t;
1843e12c5d1SDavid du Colombier 
1853e12c5d1SDavid du Colombier 	mag = Mag;
1863e12c5d1SDavid du Colombier 	binit(error, 0, "tweak");
1873e12c5d1SDavid du Colombier 	values = balloc(Rect(0, 0, 256*Maxmag, Maxmag), screen.ldepth);
1883e12c5d1SDavid du Colombier 	if(values == 0)
1893e12c5d1SDavid du Colombier 		berror("can't balloc");
1903e12c5d1SDavid du Colombier 	for(i=0; i<256; i++)
1913e12c5d1SDavid du Colombier 		for(j=0; j<Maxmag; j++)
1923e12c5d1SDavid du Colombier 			segment(values, Pt(i*Maxmag, j), Pt((i+1)*Maxmag, j), i, S);
1933e12c5d1SDavid du Colombier 	einit(Emouse|Ekeyboard);
1943e12c5d1SDavid du Colombier 	ereshaped(screen.r);
1953e12c5d1SDavid du Colombier 	i = 1;
1963e12c5d1SDavid du Colombier 	setjmp(err);
1973e12c5d1SDavid du Colombier 	for(; i<argc; i++){
1983e12c5d1SDavid du Colombier 		file = argv[i];
1993e12c5d1SDavid du Colombier 		t = tget(argv[i]);
2003e12c5d1SDavid du Colombier 		if(t)
2013e12c5d1SDavid du Colombier 			draw(t, 1);
2023e12c5d1SDavid du Colombier 		bflush();
2033e12c5d1SDavid du Colombier 	}
2043e12c5d1SDavid du Colombier 	file = 0;
2053e12c5d1SDavid du Colombier 	setjmp(err);
2063e12c5d1SDavid du Colombier 	for(;;)
2073e12c5d1SDavid du Colombier 		switch(event(&e)){
2083e12c5d1SDavid du Colombier 		case Ekeyboard:
2093e12c5d1SDavid du Colombier 			break;
2103e12c5d1SDavid du Colombier 		case Emouse:
2113e12c5d1SDavid du Colombier 			mouse = e.mouse;
2123e12c5d1SDavid du Colombier 			if(mouse.buttons & 3){
2133e12c5d1SDavid du Colombier 				select();
2143e12c5d1SDavid du Colombier 				break;
2153e12c5d1SDavid du Colombier 			}
2163e12c5d1SDavid du Colombier 			if(mouse.buttons & 4)
2173e12c5d1SDavid du Colombier 				menu();
2183e12c5d1SDavid du Colombier 		}
2193e12c5d1SDavid du Colombier }
2203e12c5d1SDavid du Colombier 
2213e12c5d1SDavid du Colombier void
2223e12c5d1SDavid du Colombier error(char *s)
2233e12c5d1SDavid du Colombier {
2243e12c5d1SDavid du Colombier 	if(file)
2253e12c5d1SDavid du Colombier 		mesg("can't read %s: %s: %r", file, s);
2263e12c5d1SDavid du Colombier 	else
2273e12c5d1SDavid du Colombier 		mesg("/dev/bitblt error: %s", s);
2283e12c5d1SDavid du Colombier 	if(err[0])
2293e12c5d1SDavid du Colombier 		longjmp(err, 1);
2303e12c5d1SDavid du Colombier 	exits(s);
2313e12c5d1SDavid du Colombier }
2323e12c5d1SDavid du Colombier 
2333e12c5d1SDavid du Colombier void
2343e12c5d1SDavid du Colombier redraw(Thing *t)
2353e12c5d1SDavid du Colombier {
2363e12c5d1SDavid du Colombier 	Thing *nt;
2373e12c5d1SDavid du Colombier 	Point p;
2383e12c5d1SDavid du Colombier 
2393e12c5d1SDavid du Colombier 	if(thing==0 || thing==t)
2403e12c5d1SDavid du Colombier 		bitblt(&screen, editr.min, &screen, editr, 0);
2413e12c5d1SDavid du Colombier 	if(thing == 0)
2423e12c5d1SDavid du Colombier 		return;
2433e12c5d1SDavid du Colombier 	if(thing != t){
2443e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
2453e12c5d1SDavid du Colombier 			;
2463e12c5d1SDavid du Colombier 		bitblt(&screen, Pt(screen.r.min.x, nt->er.max.y), &screen,
2473e12c5d1SDavid du Colombier 			Rect(screen.r.min.x, nt->er.max.y, editr.max.x, editr.max.y), 0);
2483e12c5d1SDavid du Colombier 	}
2493e12c5d1SDavid du Colombier 	for(nt=t; nt; nt=nt->next){
2503e12c5d1SDavid du Colombier 		draw(nt, 0);
2513e12c5d1SDavid du Colombier 		if(nt->next == 0){
2523e12c5d1SDavid du Colombier 			p = Pt(editr.min.x, nt->er.max.y);
2533e12c5d1SDavid du Colombier 			bitblt(&screen, p, &screen, Rpt(p, editr.max), 0);
2543e12c5d1SDavid du Colombier 		}
2553e12c5d1SDavid du Colombier 	}
2563e12c5d1SDavid du Colombier 	mesg("");
2573e12c5d1SDavid du Colombier }
2583e12c5d1SDavid du Colombier 
2593e12c5d1SDavid du Colombier void
2603e12c5d1SDavid du Colombier ereshaped(Rectangle r)
2613e12c5d1SDavid du Colombier {
2623e12c5d1SDavid du Colombier 	USED(r);
2633e12c5d1SDavid du Colombier 
2643e12c5d1SDavid du Colombier 	screen.r = bscreenrect(&screen.clipr);
2653e12c5d1SDavid du Colombier 	cntlr = inset(screen.clipr, 1);
2663e12c5d1SDavid du Colombier 	editr = cntlr;
2673e12c5d1SDavid du Colombier 	textr = editr;
2683e12c5d1SDavid du Colombier 	textr.min.y = textr.max.y - font->height;
2693e12c5d1SDavid du Colombier 	cntlr.max.y = cntlr.min.y + font->height;
2703e12c5d1SDavid du Colombier 	editr.min.y = cntlr.max.y+1;
2713e12c5d1SDavid du Colombier 	editr.max.y = textr.min.y-1;
2723e12c5d1SDavid du Colombier 	bitblt(&screen, screen.clipr.min, &screen, screen.clipr, 0);
2733e12c5d1SDavid du Colombier 	segment(&screen, Pt(editr.min.x, editr.max.y),
2743e12c5d1SDavid du Colombier 			 Pt(editr.max.x+1, editr.max.y), ~0, F);
2753e12c5d1SDavid du Colombier 	clipr(&screen, editr);
2763e12c5d1SDavid du Colombier 	drawall();
2773e12c5d1SDavid du Colombier }
2783e12c5d1SDavid du Colombier 
2793e12c5d1SDavid du Colombier void
2803e12c5d1SDavid du Colombier mesgstr(Point p, int line, char *s)
2813e12c5d1SDavid du Colombier {
2823e12c5d1SDavid du Colombier 	Rectangle c, r;
2833e12c5d1SDavid du Colombier 
2843e12c5d1SDavid du Colombier 	r.min = p;
2853e12c5d1SDavid du Colombier 	r.min.y += line*font->height;
2863e12c5d1SDavid du Colombier 	r.max.y = r.min.y+font->height;
2873e12c5d1SDavid du Colombier 	r.max.x = editr.max.x;
2883e12c5d1SDavid du Colombier 	c = screen.clipr;
2893e12c5d1SDavid du Colombier 	clipr(&screen, r);
2903e12c5d1SDavid du Colombier 	bitblt(&screen, r.min, &screen, r, 0);
2913e12c5d1SDavid du Colombier 	r.min.x++;
2923e12c5d1SDavid du Colombier 	p = string(&screen, r.min, font, s, S);
2933e12c5d1SDavid du Colombier 	r.max.x = p.x + 1;
2943e12c5d1SDavid du Colombier 	r.min.x--;
2953e12c5d1SDavid du Colombier 	bitblt(&screen, r.min, &screen, r, ~D);
2963e12c5d1SDavid du Colombier 	clipr(&screen, c);
2973e12c5d1SDavid du Colombier 	bflush();
2983e12c5d1SDavid du Colombier }
2993e12c5d1SDavid du Colombier 
3003e12c5d1SDavid du Colombier void
3013e12c5d1SDavid du Colombier mesg(char *fmt, ...)
3023e12c5d1SDavid du Colombier {
3033e12c5d1SDavid du Colombier 	char buf[1024];
3043e12c5d1SDavid du Colombier 
3053e12c5d1SDavid du Colombier 	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
3063e12c5d1SDavid du Colombier 	mesgstr(textr.min, 0, buf);
3073e12c5d1SDavid du Colombier }
3083e12c5d1SDavid du Colombier 
3093e12c5d1SDavid du Colombier void
3103e12c5d1SDavid du Colombier tmesg(Thing *t, int line, char *fmt, ...)
3113e12c5d1SDavid du Colombier {
3123e12c5d1SDavid du Colombier 	char buf[1024];
3133e12c5d1SDavid du Colombier 
3143e12c5d1SDavid du Colombier 	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
3153e12c5d1SDavid du Colombier 	mesgstr(t->tr.min, line, buf);
3163e12c5d1SDavid du Colombier }
3173e12c5d1SDavid du Colombier 
3183e12c5d1SDavid du Colombier 
3193e12c5d1SDavid du Colombier void
3203e12c5d1SDavid du Colombier scntl(char *l)
3213e12c5d1SDavid du Colombier {
3223e12c5d1SDavid du Colombier 	char vals[16];
3233e12c5d1SDavid du Colombier 
3243e12c5d1SDavid du Colombier 	if(val & 0x80000000)
3253e12c5d1SDavid du Colombier 		sprint(vals, "~%lux", ~val);
3263e12c5d1SDavid du Colombier 	else
3273e12c5d1SDavid du Colombier 		sprint(vals, "%lux", val);
3283e12c5d1SDavid du Colombier 	sprint(l, "mag: %d val(hex): %s but1: %s but2: %s copy: %s",
3293e12c5d1SDavid du Colombier 		mag, vals, fntostr(but1fn), fntostr(but2fn), fntostr(copyfn));
3303e12c5d1SDavid du Colombier }
3313e12c5d1SDavid du Colombier 
3323e12c5d1SDavid du Colombier void
3333e12c5d1SDavid du Colombier cntl(void)
3343e12c5d1SDavid du Colombier {
3353e12c5d1SDavid du Colombier 	char buf[256];
3363e12c5d1SDavid du Colombier 
3373e12c5d1SDavid du Colombier 	scntl(buf);
3383e12c5d1SDavid du Colombier 	mesgstr(cntlr.min, 0, buf);
3393e12c5d1SDavid du Colombier }
3403e12c5d1SDavid du Colombier 
3413e12c5d1SDavid du Colombier void
3423e12c5d1SDavid du Colombier stext(Thing *t, char *l0, char *l1)
3433e12c5d1SDavid du Colombier {
3443e12c5d1SDavid du Colombier 	Fontchar *fc;
3453e12c5d1SDavid du Colombier 	char buf[256];
3463e12c5d1SDavid du Colombier 
3473e12c5d1SDavid du Colombier 	l1[0] = 0;
3483e12c5d1SDavid du Colombier 	sprint(buf, "ldepth:%d r:%d %d  %d %d ",
3493e12c5d1SDavid du Colombier 		t->b->ldepth, t->b->r.min.x, t->b->r.min.y,
3503e12c5d1SDavid du Colombier 		t->b->r.max.x, t->b->r.max.y);
3513e12c5d1SDavid du Colombier 	if(t->parent)
3523e12c5d1SDavid du Colombier 		sprint(buf+strlen(buf), "mag: %d ", t->mag);
3533e12c5d1SDavid du Colombier 	sprint(l0, "%s file: %s", buf, t->name);
3543e12c5d1SDavid du Colombier 	if(t->c >= 0){
3553e12c5d1SDavid du Colombier 		fc = &t->parent->s->info[t->c];
3563e12c5d1SDavid du Colombier 		sprint(l1, "c(hex): %x c(char): %C x: %d "
3573e12c5d1SDavid du Colombier 			   "top: %d bottom: %d left: %d width: %d iwidth: %d",
3583e12c5d1SDavid du Colombier 			t->c+t->parent->off, t->c+t->parent->off,
3593e12c5d1SDavid du Colombier 			fc->x, fc->top, fc->bottom, fc->left,
3603e12c5d1SDavid du Colombier 			fc->width, Dx(t->b->r));
3613e12c5d1SDavid du Colombier 	}else if(t->s)
3623e12c5d1SDavid du Colombier 		sprint(l1, "offset(hex): %ux n:%d  height:%d  ascent:%d",
3633e12c5d1SDavid du Colombier 			t->off, t->s->n, t->s->height, t->s->ascent);
3643e12c5d1SDavid du Colombier }
3653e12c5d1SDavid du Colombier 
3663e12c5d1SDavid du Colombier void
3673e12c5d1SDavid du Colombier text(Thing *t)
3683e12c5d1SDavid du Colombier {
3693e12c5d1SDavid du Colombier 	char l0[256], l1[256];
3703e12c5d1SDavid du Colombier 
3713e12c5d1SDavid du Colombier 	stext(t, l0, l1);
3723e12c5d1SDavid du Colombier 	tmesg(t, 0, l0);
3733e12c5d1SDavid du Colombier 	if(l1[0])
3743e12c5d1SDavid du Colombier 		tmesg(t, 1, l1);
3753e12c5d1SDavid du Colombier }
3763e12c5d1SDavid du Colombier 
3773e12c5d1SDavid du Colombier void
3783e12c5d1SDavid du Colombier drawall(void)
3793e12c5d1SDavid du Colombier {
3803e12c5d1SDavid du Colombier 	Thing *t;
3813e12c5d1SDavid du Colombier 
3823e12c5d1SDavid du Colombier 	cntl();
3833e12c5d1SDavid du Colombier 	for(t=thing; t; t=t->next)
3843e12c5d1SDavid du Colombier 		draw(t, 0);
3853e12c5d1SDavid du Colombier }
3863e12c5d1SDavid du Colombier 
3873e12c5d1SDavid du Colombier int
3883e12c5d1SDavid du Colombier value(Bitmap *b, int x)
3893e12c5d1SDavid du Colombier {
3903e12c5d1SDavid du Colombier 	int v, l, w;
3913e12c5d1SDavid du Colombier 	uchar mask;
3923e12c5d1SDavid du Colombier 
3933e12c5d1SDavid du Colombier 	l = b->ldepth;
3943e12c5d1SDavid du Colombier 	if(l > 3){
3953e12c5d1SDavid du Colombier 		mesg("ldepth too large");
3963e12c5d1SDavid du Colombier 		return 0;
3973e12c5d1SDavid du Colombier 	}
3983e12c5d1SDavid du Colombier 	w = 1<<l;
3993e12c5d1SDavid du Colombier 	mask = (2<<w)-1;		/* ones at right end of word */
4003e12c5d1SDavid du Colombier 	x -= b->r.min.x&~(7>>l);	/* adjust x relative to first pixel */
4013e12c5d1SDavid du Colombier 	v = data[x>>(3-l)];
4023e12c5d1SDavid du Colombier 	v >>= ((7>>l)<<l) - ((x&(7>>l))<<l);	/* pixel at right end of word */
4033e12c5d1SDavid du Colombier 	v &= mask;			/* pixel at right end of word */
4043e12c5d1SDavid du Colombier 	return v;
4053e12c5d1SDavid du Colombier }
4063e12c5d1SDavid du Colombier 
4073e12c5d1SDavid du Colombier int
4083e12c5d1SDavid du Colombier bvalue(int v, int l)
4093e12c5d1SDavid du Colombier {
410*219b2ee8SDavid du Colombier 	v &= (1<<(1<<l))-1;
4113e12c5d1SDavid du Colombier 	if(l > screen.ldepth)
412*219b2ee8SDavid du Colombier 		v >>= (1<<l) - (1<<screen.ldepth);
413*219b2ee8SDavid du Colombier 	else
414*219b2ee8SDavid du Colombier 		while(l < screen.ldepth){
415*219b2ee8SDavid du Colombier 			v |= v << (1<<l);
416*219b2ee8SDavid du Colombier 			l++;
4173e12c5d1SDavid du Colombier 		}
4183e12c5d1SDavid du Colombier 	return v*Maxmag;
4193e12c5d1SDavid du Colombier }
4203e12c5d1SDavid du Colombier 
4213e12c5d1SDavid du Colombier void
4223e12c5d1SDavid du Colombier draw(Thing *nt, int link)
4233e12c5d1SDavid du Colombier {
4243e12c5d1SDavid du Colombier 	int nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
4253e12c5d1SDavid du Colombier 	Thing *t;
4263e12c5d1SDavid du Colombier 	Subfont *s;
4273e12c5d1SDavid du Colombier 	Bitmap *b;
4283e12c5d1SDavid du Colombier 	Point p, p1, p2;
4293e12c5d1SDavid du Colombier 
4303e12c5d1SDavid du Colombier 	if(link){
4313e12c5d1SDavid du Colombier 		nt->next = 0;
4323e12c5d1SDavid du Colombier 		if(thing == 0){
4333e12c5d1SDavid du Colombier 			thing = nt;
4343e12c5d1SDavid du Colombier 			y = editr.min.y;
4353e12c5d1SDavid du Colombier 		}else{
4363e12c5d1SDavid du Colombier 			for(t=thing; t->next; t=t->next)
4373e12c5d1SDavid du Colombier 				;
4383e12c5d1SDavid du Colombier 			t->next = nt;
4393e12c5d1SDavid du Colombier 			y = t->er.max.y;
4403e12c5d1SDavid du Colombier 		}
4413e12c5d1SDavid du Colombier 	}else{
4423e12c5d1SDavid du Colombier 		if(thing == nt)
4433e12c5d1SDavid du Colombier 			y = editr.min.y;
4443e12c5d1SDavid du Colombier 		else{
4453e12c5d1SDavid du Colombier 			for(t=thing; t->next!=nt; t=t->next)
4463e12c5d1SDavid du Colombier 				;
4473e12c5d1SDavid du Colombier 			y = t->er.max.y;
4483e12c5d1SDavid du Colombier 		}
4493e12c5d1SDavid du Colombier 	}
4503e12c5d1SDavid du Colombier 	s = nt->s;
4513e12c5d1SDavid du Colombier 	b = nt->b;
4523e12c5d1SDavid du Colombier 	nl = font->height;
4533e12c5d1SDavid du Colombier 	if(s || nt->c>=0)
4543e12c5d1SDavid du Colombier 		nl += font->height;
4553e12c5d1SDavid du Colombier 	fdx = Dx(editr) - 2*Border;
4563e12c5d1SDavid du Colombier 	dx = Dx(b->r);
4573e12c5d1SDavid du Colombier 	dy = Dy(b->r);
4583e12c5d1SDavid du Colombier 	if(nt->mag > 1){
4593e12c5d1SDavid du Colombier 		dx *= nt->mag;
4603e12c5d1SDavid du Colombier 		dy *= nt->mag;
4613e12c5d1SDavid du Colombier 		fdx -= fdx%nt->mag;
4623e12c5d1SDavid du Colombier 	}
4633e12c5d1SDavid du Colombier 	nf = 1 + dx/fdx;
4643e12c5d1SDavid du Colombier 	nt->er.min.y = y;
4653e12c5d1SDavid du Colombier 	nt->er.min.x = editr.min.x;
4663e12c5d1SDavid du Colombier 	nt->er.max.x = nt->er.min.x + Border + dx + Border;
4673e12c5d1SDavid du Colombier 	if(nt->er.max.x > editr.max.x)
4683e12c5d1SDavid du Colombier 		nt->er.max.x = editr.max.x;
4693e12c5d1SDavid du Colombier 	nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
4703e12c5d1SDavid du Colombier 	nt->r = inset(nt->er, Border);
4713e12c5d1SDavid du Colombier 	nt->er.max.x = editr.max.x;
4723e12c5d1SDavid du Colombier 	bitblt(&screen, nt->er.min, &screen, nt->er, 0);
4733e12c5d1SDavid du Colombier 	for(i=0; i<nf; i++){
4743e12c5d1SDavid du Colombier 		p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
4753e12c5d1SDavid du Colombier 		/* draw portion of bitmap */
4763e12c5d1SDavid du Colombier 		p = Pt(p1.x+1, p1.y);
4773e12c5d1SDavid du Colombier 		if(nt->mag == 1)
4783e12c5d1SDavid du Colombier 			bitblt(&screen, p, b,
4793e12c5d1SDavid du Colombier 				Rect(b->r.min.x+i*fdx, b->r.min.y,
4803e12c5d1SDavid du Colombier 				     b->r.max.x+(i+1)*fdx, b->r.max.y), S);
4813e12c5d1SDavid du Colombier 		else{
4823e12c5d1SDavid du Colombier 			for(y=b->r.min.y; y<b->r.max.y; y++){
4833e12c5d1SDavid du Colombier 				sy = p.y+(y-b->r.min.y)*nt->mag;
4843e12c5d1SDavid du Colombier 				rdbitmap(b, y, y+1, data);
4853e12c5d1SDavid du Colombier 				for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
4863e12c5d1SDavid du Colombier 					sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
4873e12c5d1SDavid du Colombier 					if(sx >= nt->r.max.x)
4883e12c5d1SDavid du Colombier 						break;
4893e12c5d1SDavid du Colombier 					v = bvalue(value(b, x), b->ldepth);
4903e12c5d1SDavid du Colombier 					if(v)
4913e12c5d1SDavid du Colombier 						bitblt(&screen, Pt(sx, sy),
4923e12c5d1SDavid du Colombier 							values,
4933e12c5d1SDavid du Colombier 							Rect(v, 0, v+nt->mag, nt->mag), S);
4943e12c5d1SDavid du Colombier 				}
4953e12c5d1SDavid du Colombier 
4963e12c5d1SDavid du Colombier 			}
4973e12c5d1SDavid du Colombier 		}
4983e12c5d1SDavid du Colombier 		/* line down left */
4993e12c5d1SDavid du Colombier 		segment(&screen, p1, Pt(p1.x, p1.y+dy+Border+1), i==0? ~0 : 0, S);
5003e12c5d1SDavid du Colombier 		/* line across top */
5013e12c5d1SDavid du Colombier 		segment(&screen, Pt(p1.x, p1.y-1), Pt(nt->r.max.x+Border, p1.y-1), ~0, F);
5023e12c5d1SDavid du Colombier 		p2 = p1;
5033e12c5d1SDavid du Colombier 		if(i == nf-1)
5043e12c5d1SDavid du Colombier 			p2.x += 1 + dx%fdx;
5053e12c5d1SDavid du Colombier 		else
5063e12c5d1SDavid du Colombier 			p2.x = nt->r.max.x;
5073e12c5d1SDavid du Colombier 		/* line down right */
5083e12c5d1SDavid du Colombier 		segment(&screen, p2, Pt(p2.x, p2.y+dy+1), i==nf-1? ~0 : 0, S);
5093e12c5d1SDavid du Colombier 		/* line across bottom */
5103e12c5d1SDavid du Colombier 		if(i == nf-1){
5113e12c5d1SDavid du Colombier 			p1.y += Border+dy;
5123e12c5d1SDavid du Colombier 			segment(&screen, Pt(p1.x, p1.y-1), Pt(p2.x, p1.y-1), ~0, F);
5133e12c5d1SDavid du Colombier 		}
5143e12c5d1SDavid du Colombier 	}
5153e12c5d1SDavid du Colombier 	nt->tr.min.x = editr.min.x;
5163e12c5d1SDavid du Colombier 	nt->tr.max.x = editr.max.x;
5173e12c5d1SDavid du Colombier 	nt->tr.min.y = nt->er.max.y + Border;
5183e12c5d1SDavid du Colombier 	nt->tr.max.y = nt->tr.min.y + nl;
5193e12c5d1SDavid du Colombier 	nt->er.max.y = nt->tr.max.y + Border;
5203e12c5d1SDavid du Colombier 	text(nt);
5213e12c5d1SDavid du Colombier }
5223e12c5d1SDavid du Colombier 
5233e12c5d1SDavid du Colombier int
5243e12c5d1SDavid du Colombier tohex(int c)
5253e12c5d1SDavid du Colombier {
5263e12c5d1SDavid du Colombier 	if('0'<=c && c<='9')
5273e12c5d1SDavid du Colombier 		return c - '0';
5283e12c5d1SDavid du Colombier 	if('a'<=c && c<='f')
5293e12c5d1SDavid du Colombier 		return 10 + (c - 'a');
5303e12c5d1SDavid du Colombier 	if('A'<=c && c<='F')
5313e12c5d1SDavid du Colombier 		return 10 + (c - 'A');
5323e12c5d1SDavid du Colombier }
5333e12c5d1SDavid du Colombier 
5343e12c5d1SDavid du Colombier Thing*
5353e12c5d1SDavid du Colombier tget(char *file)
5363e12c5d1SDavid du Colombier {
5373e12c5d1SDavid du Colombier 	int i, j, fd, face, x, y, c, ld;
5383e12c5d1SDavid du Colombier 	Bitmap *b, *tb;
5393e12c5d1SDavid du Colombier 	Subfont *s;
5403e12c5d1SDavid du Colombier 	Thing *t;
5413e12c5d1SDavid du Colombier 	Dir d;
5423e12c5d1SDavid du Colombier 	jmp_buf oerr;
5433e12c5d1SDavid du Colombier 	uchar buf[256];
5443e12c5d1SDavid du Colombier 	char *data;
5453e12c5d1SDavid du Colombier 
5463e12c5d1SDavid du Colombier 	errstr((char*)buf);	/* flush pending error message */
5473e12c5d1SDavid du Colombier 	memmove(oerr, err, sizeof err);
5483e12c5d1SDavid du Colombier 	if(setjmp(err)){
5493e12c5d1SDavid du Colombier    Err:
5503e12c5d1SDavid du Colombier 		memmove(err, oerr, sizeof err);
5513e12c5d1SDavid du Colombier 		return 0;
5523e12c5d1SDavid du Colombier 	}
5533e12c5d1SDavid du Colombier 	fd = open(file, OREAD);
5543e12c5d1SDavid du Colombier 	if(fd < 0){
5553e12c5d1SDavid du Colombier 		mesg("can't open %s: %r", file);
5563e12c5d1SDavid du Colombier 		goto Err;
5573e12c5d1SDavid du Colombier 	}
5583e12c5d1SDavid du Colombier 	if(dirfstat(fd, &d) < 0){
5593e12c5d1SDavid du Colombier 		mesg("can't stat bitmap file %s: %r", file);
5603e12c5d1SDavid du Colombier 		close(fd);
5613e12c5d1SDavid du Colombier 		goto Err;
5623e12c5d1SDavid du Colombier 	}
5633e12c5d1SDavid du Colombier 	if(read(fd, buf, 2) != 2){
5643e12c5d1SDavid du Colombier 		mesg("can't read %s: %r", file);
5653e12c5d1SDavid du Colombier 		close(fd);
5663e12c5d1SDavid du Colombier 		goto Err;
5673e12c5d1SDavid du Colombier 	}
5683e12c5d1SDavid du Colombier 	seek(fd, 0, 0);
5693e12c5d1SDavid du Colombier 	if(buf[0]=='0' && buf[1]=='x'){
5703e12c5d1SDavid du Colombier 		/*
5713e12c5d1SDavid du Colombier 		 * face file
5723e12c5d1SDavid du Colombier 		 */
5733e12c5d1SDavid du Colombier 		face = 1;
5743e12c5d1SDavid du Colombier 		s = 0;
5753e12c5d1SDavid du Colombier 		data = malloc(d.length+1);
5763e12c5d1SDavid du Colombier 		if(data == 0){
5773e12c5d1SDavid du Colombier 			mesg("can't malloc buffer: %r");
5783e12c5d1SDavid du Colombier 			close(fd);
5793e12c5d1SDavid du Colombier 			goto Err;
5803e12c5d1SDavid du Colombier 		}
5813e12c5d1SDavid du Colombier 		data[d.length] = 0;
5823e12c5d1SDavid du Colombier 		if(read(fd, data, d.length) != d.length){
5833e12c5d1SDavid du Colombier 			mesg("can't read bitmap file %s: %r", file);
5843e12c5d1SDavid du Colombier 			close(fd);
5853e12c5d1SDavid du Colombier 			goto Err;
5863e12c5d1SDavid du Colombier 		}
5873e12c5d1SDavid du Colombier 		for(y=0,i=0; i<d.length; i++)
5883e12c5d1SDavid du Colombier 			if(data[i] == '\n')
5893e12c5d1SDavid du Colombier 				y++;
5903e12c5d1SDavid du Colombier 		if(y == 0){
5913e12c5d1SDavid du Colombier 	ill:
5923e12c5d1SDavid du Colombier 			mesg("ill-formed face file %s", file);
5933e12c5d1SDavid du Colombier 			close(fd);
5943e12c5d1SDavid du Colombier 			free(data);
5953e12c5d1SDavid du Colombier 			goto Err;
5963e12c5d1SDavid du Colombier 		}
5973e12c5d1SDavid du Colombier 		for(x=0,i=0; (c=data[i])!='\n'; ){
5983e12c5d1SDavid du Colombier 			if(c==',' || c==' ' || c=='\t'){
5993e12c5d1SDavid du Colombier 				i++;
6003e12c5d1SDavid du Colombier 				continue;
6013e12c5d1SDavid du Colombier 			}
6023e12c5d1SDavid du Colombier 			if(c=='0' && data[i+1] == 'x'){
6033e12c5d1SDavid du Colombier 				i += 2;
6043e12c5d1SDavid du Colombier 				continue;
6053e12c5d1SDavid du Colombier 			}
6063e12c5d1SDavid du Colombier 			if(strchr(hex, c)){
6073e12c5d1SDavid du Colombier 				x += 4;
6083e12c5d1SDavid du Colombier 				i++;
6093e12c5d1SDavid du Colombier 				continue;
6103e12c5d1SDavid du Colombier 			}
6113e12c5d1SDavid du Colombier 			goto ill;
6123e12c5d1SDavid du Colombier 		}
6133e12c5d1SDavid du Colombier 		if(x % y)
6143e12c5d1SDavid du Colombier 			goto ill;
6153e12c5d1SDavid du Colombier 		switch(x / y){
6163e12c5d1SDavid du Colombier 		default:
6173e12c5d1SDavid du Colombier 			goto ill;
6183e12c5d1SDavid du Colombier 		case 1:
6193e12c5d1SDavid du Colombier 			ld = 0;
6203e12c5d1SDavid du Colombier 			break;
6213e12c5d1SDavid du Colombier 		case 2:
6223e12c5d1SDavid du Colombier 			ld = 1;
6233e12c5d1SDavid du Colombier 			break;
6243e12c5d1SDavid du Colombier 		case 4:
6253e12c5d1SDavid du Colombier 			ld = 2;
6263e12c5d1SDavid du Colombier 			break;
6273e12c5d1SDavid du Colombier 		case 8:
6283e12c5d1SDavid du Colombier 			ld = 3;
6293e12c5d1SDavid du Colombier 			break;
6303e12c5d1SDavid du Colombier 		}
6313e12c5d1SDavid du Colombier 		b = balloc(Rect(0, 0, y, y), ld);
6323e12c5d1SDavid du Colombier 		if(b == 0){
6333e12c5d1SDavid du Colombier 			mesg("balloc failed file %s: %r", file);
6343e12c5d1SDavid du Colombier 			free(data);
6353e12c5d1SDavid du Colombier 			close(fd);
6363e12c5d1SDavid du Colombier 			goto Err;
6373e12c5d1SDavid du Colombier 		}
6383e12c5d1SDavid du Colombier 		i = 0;
6393e12c5d1SDavid du Colombier 		for(j=0; j<y; j++){
6403e12c5d1SDavid du Colombier 			for(x=0; (c=data[i])!='\n'; ){
6413e12c5d1SDavid du Colombier 				if(strchr(" \t,{}", c)){
6423e12c5d1SDavid du Colombier 					i++;
6433e12c5d1SDavid du Colombier 					continue;
6443e12c5d1SDavid du Colombier 				}
6453e12c5d1SDavid du Colombier 				if(c=='0' && data[i+1] == 'x'){
6463e12c5d1SDavid du Colombier 					i += 2;
6473e12c5d1SDavid du Colombier 					continue;
6483e12c5d1SDavid du Colombier 				}
6493e12c5d1SDavid du Colombier 				if(strchr(hex, c)){
6503e12c5d1SDavid du Colombier 					buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
6513e12c5d1SDavid du Colombier 					i += 2;
6523e12c5d1SDavid du Colombier 					continue;
6533e12c5d1SDavid du Colombier 				}
6543e12c5d1SDavid du Colombier 			}
6553e12c5d1SDavid du Colombier 			i++;
6563e12c5d1SDavid du Colombier 			wrbitmap(b, j, j+1, buf);
6573e12c5d1SDavid du Colombier 		}
6583e12c5d1SDavid du Colombier 		free(data);
6593e12c5d1SDavid du Colombier 	}else{
6603e12c5d1SDavid du Colombier 		/*
6613e12c5d1SDavid du Colombier 		 * plain bitmap file
6623e12c5d1SDavid du Colombier 		 */
6633e12c5d1SDavid du Colombier 		face = 0;
6643e12c5d1SDavid du Colombier 		s = 0;
6653e12c5d1SDavid du Colombier 		b = rdbitmapfile(fd);
6663e12c5d1SDavid du Colombier 		if(b == 0){
6673e12c5d1SDavid du Colombier 			mesg("can't read bitmap file %s: %r", file);
6683e12c5d1SDavid du Colombier 			close(fd);
6693e12c5d1SDavid du Colombier 			goto Err;
6703e12c5d1SDavid du Colombier 		}
6713e12c5d1SDavid du Colombier 		if(seek(fd, 0, 1) < d.length){
6723e12c5d1SDavid du Colombier 			/* rdsubfontfile drops bitmap; must make a copy */
6733e12c5d1SDavid du Colombier 			tb = balloc(b->r, b->ldepth);
6743e12c5d1SDavid du Colombier 			if(tb == 0){
6753e12c5d1SDavid du Colombier 				mesg("can't balloc bitmap for file %s: %r", file);
6763e12c5d1SDavid du Colombier 				close(fd);
6773e12c5d1SDavid du Colombier 				goto Err;
6783e12c5d1SDavid du Colombier 			}
6793e12c5d1SDavid du Colombier 			bitblt(tb, tb->r.min, b, b->r, S);
6803e12c5d1SDavid du Colombier 			s = rdsubfontfile(fd, tb);
6813e12c5d1SDavid du Colombier 		}
6823e12c5d1SDavid du Colombier 	}
6833e12c5d1SDavid du Colombier 	close(fd);
6843e12c5d1SDavid du Colombier 	t = malloc(sizeof(Thing));
6853e12c5d1SDavid du Colombier 	if(t == 0){
6863e12c5d1SDavid du Colombier    nomem:
6873e12c5d1SDavid du Colombier 		mesg("malloc failed: %r");
6883e12c5d1SDavid du Colombier 		if(s)
6893e12c5d1SDavid du Colombier 			subffree(s);
6903e12c5d1SDavid du Colombier 		bfree(b);
6913e12c5d1SDavid du Colombier 		goto Err;
6923e12c5d1SDavid du Colombier 	}
6933e12c5d1SDavid du Colombier 	t->name = strdup(file);
6943e12c5d1SDavid du Colombier 	if(t->name == 0){
6953e12c5d1SDavid du Colombier 		free(t);
6963e12c5d1SDavid du Colombier 		goto nomem;
6973e12c5d1SDavid du Colombier 	}
6983e12c5d1SDavid du Colombier 	t->b = b;
6993e12c5d1SDavid du Colombier 	t->s = s;
7003e12c5d1SDavid du Colombier 	t->face = face;
7013e12c5d1SDavid du Colombier 	t->mod = 0;
7023e12c5d1SDavid du Colombier 	t->parent = 0;
7033e12c5d1SDavid du Colombier 	t->c = -1;
7043e12c5d1SDavid du Colombier 	t->mag = 1;
7053e12c5d1SDavid du Colombier 	t->off = 0;
7063e12c5d1SDavid du Colombier 	memmove(err, oerr, sizeof err);
7073e12c5d1SDavid du Colombier 	return t;
7083e12c5d1SDavid du Colombier }
7093e12c5d1SDavid du Colombier 
7103e12c5d1SDavid du Colombier int
7113e12c5d1SDavid du Colombier atline(int x, Point p, char *line, char *buf)
7123e12c5d1SDavid du Colombier {
7133e12c5d1SDavid du Colombier 	char *s, *c, *word, *hit;
7143e12c5d1SDavid du Colombier 	int w, wasblank;
7153e12c5d1SDavid du Colombier 	Rune r;
7163e12c5d1SDavid du Colombier 
7173e12c5d1SDavid du Colombier 	wasblank = 1;
7183e12c5d1SDavid du Colombier 	hit = 0;
7193e12c5d1SDavid du Colombier 	word = 0;
7203e12c5d1SDavid du Colombier 	for(s=line; *s; s+=w){
7213e12c5d1SDavid du Colombier 		w = chartorune(&r, s);
7223e12c5d1SDavid du Colombier 		x += charwidth(font, r);
7233e12c5d1SDavid du Colombier 		if(wasblank && r!=' ')
7243e12c5d1SDavid du Colombier 			word = s;
7253e12c5d1SDavid du Colombier 		wasblank = 0;
7263e12c5d1SDavid du Colombier 		if(r == ' '){
7273e12c5d1SDavid du Colombier 			if(x >= p.x)
7283e12c5d1SDavid du Colombier 				break;
7293e12c5d1SDavid du Colombier 			wasblank = 1;
7303e12c5d1SDavid du Colombier 		}
7313e12c5d1SDavid du Colombier 		if(r == ':')
7323e12c5d1SDavid du Colombier 			hit = word;
7333e12c5d1SDavid du Colombier 	}
7343e12c5d1SDavid du Colombier 	if(x < p.x)
7353e12c5d1SDavid du Colombier 		return 0;
7363e12c5d1SDavid du Colombier 	c = utfrune(hit, ':');
7373e12c5d1SDavid du Colombier 	strncpy(buf, hit, c-hit);
7383e12c5d1SDavid du Colombier 	buf[c-hit] = 0;
7393e12c5d1SDavid du Colombier 	return 1;
7403e12c5d1SDavid du Colombier }
7413e12c5d1SDavid du Colombier 
7423e12c5d1SDavid du Colombier int
7433e12c5d1SDavid du Colombier attext(Thing *t, Point p, char *buf)
7443e12c5d1SDavid du Colombier {
7453e12c5d1SDavid du Colombier 	char l0[256], l1[256];
7463e12c5d1SDavid du Colombier 
7473e12c5d1SDavid du Colombier 	if(!ptinrect(p, t->tr))
7483e12c5d1SDavid du Colombier 		return 0;
7493e12c5d1SDavid du Colombier 	stext(t, l0, l1);
7503e12c5d1SDavid du Colombier 	if(p.y < t->tr.min.y+font->height)
7513e12c5d1SDavid du Colombier 		return atline(t->r.min.x, p, l0, buf);
7523e12c5d1SDavid du Colombier 	else
7533e12c5d1SDavid du Colombier 		return atline(t->r.min.x, p, l1, buf);
7543e12c5d1SDavid du Colombier }
7553e12c5d1SDavid du Colombier 
7563e12c5d1SDavid du Colombier int
7573e12c5d1SDavid du Colombier type(char *buf, char *tag)
7583e12c5d1SDavid du Colombier {
7593e12c5d1SDavid du Colombier 	Rune r;
7603e12c5d1SDavid du Colombier 	char *p;
7613e12c5d1SDavid du Colombier 
7623e12c5d1SDavid du Colombier 	cursorswitch(&busy);
7633e12c5d1SDavid du Colombier 	p = buf;
7643e12c5d1SDavid du Colombier 	for(;;){
7653e12c5d1SDavid du Colombier 		*p = 0;
7663e12c5d1SDavid du Colombier 		mesg("%s: %s", tag, buf);
7673e12c5d1SDavid du Colombier 		r = ekbd();
7683e12c5d1SDavid du Colombier 		switch(r){
7693e12c5d1SDavid du Colombier 		case '\n':
7703e12c5d1SDavid du Colombier 			mesg("");
7713e12c5d1SDavid du Colombier 			cursorswitch(0);
7723e12c5d1SDavid du Colombier 			return p-buf;
7733e12c5d1SDavid du Colombier 		case 0x15:	/* control-U */
7743e12c5d1SDavid du Colombier 			p = buf;
7753e12c5d1SDavid du Colombier 			break;
7763e12c5d1SDavid du Colombier 		case '\b':
7773e12c5d1SDavid du Colombier 			if(p > buf)
7783e12c5d1SDavid du Colombier 				--p;
7793e12c5d1SDavid du Colombier 			break;
7803e12c5d1SDavid du Colombier 		default:
7813e12c5d1SDavid du Colombier 			p += runetochar(p, &r);
7823e12c5d1SDavid du Colombier 		}
7833e12c5d1SDavid du Colombier 	}
7843e12c5d1SDavid du Colombier 	return 0;	/* shut up compiler */
7853e12c5d1SDavid du Colombier }
7863e12c5d1SDavid du Colombier 
7873e12c5d1SDavid du Colombier void
7883e12c5d1SDavid du Colombier textedit(Thing *t, char *tag)
7893e12c5d1SDavid du Colombier {
7903e12c5d1SDavid du Colombier 	char buf[256];
7913e12c5d1SDavid du Colombier 	char *s;
7923e12c5d1SDavid du Colombier 	Bitmap *b;
7933e12c5d1SDavid du Colombier 	Subfont *f;
7943e12c5d1SDavid du Colombier 	Fontchar *fc, *nfc;
7953e12c5d1SDavid du Colombier 	Rectangle r;
7963e12c5d1SDavid du Colombier 	int i, ld, w, c, doredraw, fdx, x;
7973e12c5d1SDavid du Colombier 	Thing *nt;
7983e12c5d1SDavid du Colombier 
7993e12c5d1SDavid du Colombier 	buttons(Up);
8003e12c5d1SDavid du Colombier 	if(type(buf, tag) == 0)
8013e12c5d1SDavid du Colombier 		return;
8023e12c5d1SDavid du Colombier 	if(strcmp(tag, "file") == 0){
8033e12c5d1SDavid du Colombier 		for(s=buf; *s; s++)
8043e12c5d1SDavid du Colombier 			if(*s <= ' '){
8053e12c5d1SDavid du Colombier 				mesg("illegal file name");
8063e12c5d1SDavid du Colombier 				return;
8073e12c5d1SDavid du Colombier 			}
8083e12c5d1SDavid du Colombier 		if(strcmp(t->name, buf) != 0){
8093e12c5d1SDavid du Colombier 			if(t->parent)
8103e12c5d1SDavid du Colombier 				t->parent->mod = 1;
8113e12c5d1SDavid du Colombier 			else
8123e12c5d1SDavid du Colombier 				t->mod = 1;
8133e12c5d1SDavid du Colombier 		}
8143e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
8153e12c5d1SDavid du Colombier 			if(t==nt || t->parent==nt || nt->parent==t){
8163e12c5d1SDavid du Colombier 				free(nt->name);
8173e12c5d1SDavid du Colombier 				nt->name = strdup(buf);
8183e12c5d1SDavid du Colombier 				if(nt->name == 0){
8193e12c5d1SDavid du Colombier 	nomem:
8203e12c5d1SDavid du Colombier 					mesg("malloc failed: %r");
8213e12c5d1SDavid du Colombier 					return;
8223e12c5d1SDavid du Colombier 				}
8233e12c5d1SDavid du Colombier 				text(nt);
8243e12c5d1SDavid du Colombier 			}
8253e12c5d1SDavid du Colombier 		return;
8263e12c5d1SDavid du Colombier 	}
8273e12c5d1SDavid du Colombier 	if(strcmp(tag, "ldepth") == 0){
8283e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>3){
8293e12c5d1SDavid du Colombier 			mesg("illegal ldepth");
8303e12c5d1SDavid du Colombier 			return;
8313e12c5d1SDavid du Colombier 		}
8323e12c5d1SDavid du Colombier 		if(ld == t->b->ldepth)
8333e12c5d1SDavid du Colombier 			return;
8343e12c5d1SDavid du Colombier 		if(t->parent)
8353e12c5d1SDavid du Colombier 			t->parent->mod = 1;
8363e12c5d1SDavid du Colombier 		else
8373e12c5d1SDavid du Colombier 			t->mod = 1;
8383e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next){
8393e12c5d1SDavid du Colombier 			if(nt!=t && nt!=t->parent && nt->parent!=t)
8403e12c5d1SDavid du Colombier 				continue;
8413e12c5d1SDavid du Colombier 			b = balloc(nt->b->r, ld);
8423e12c5d1SDavid du Colombier 			if(b == 0){
8433e12c5d1SDavid du Colombier 	nobmem:
8443e12c5d1SDavid du Colombier 				mesg("balloc failed: %r");
8453e12c5d1SDavid du Colombier 				return;
8463e12c5d1SDavid du Colombier 			}
8473e12c5d1SDavid du Colombier 			bitblt(b, b->r.min, nt->b, nt->b->r, S);
8483e12c5d1SDavid du Colombier 			bfree(nt->b);
8493e12c5d1SDavid du Colombier 			nt->b = b;
8503e12c5d1SDavid du Colombier 			if(nt->s){
8513e12c5d1SDavid du Colombier 				b = balloc(nt->b->r, ld);
8523e12c5d1SDavid du Colombier 				if(b == 0)
8533e12c5d1SDavid du Colombier 					goto nobmem;
8543e12c5d1SDavid du Colombier 				bitblt(b, b->r.min, nt->b, nt->b->r, S);
8553e12c5d1SDavid du Colombier 				f = subfalloc(nt->s->n, nt->s->height, nt->s->ascent,
8563e12c5d1SDavid du Colombier 					nt->s->info, b, ~0, ~0);
8573e12c5d1SDavid du Colombier 				if(f == 0){
8583e12c5d1SDavid du Colombier 	nofmem:
8593e12c5d1SDavid du Colombier 					mesg("can't make subfont: %r");
8603e12c5d1SDavid du Colombier 					return;
8613e12c5d1SDavid du Colombier 				}
8623e12c5d1SDavid du Colombier 				subffree(nt->s);
8633e12c5d1SDavid du Colombier 				nt->s = f;
8643e12c5d1SDavid du Colombier 			}
8653e12c5d1SDavid du Colombier 			draw(nt, 0);
8663e12c5d1SDavid du Colombier 		}
8673e12c5d1SDavid du Colombier 		return;
8683e12c5d1SDavid du Colombier 	}
8693e12c5d1SDavid du Colombier 	if(strcmp(tag, "mag") == 0){
8703e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
8713e12c5d1SDavid du Colombier 			mesg("illegal magnification");
8723e12c5d1SDavid du Colombier 			return;
8733e12c5d1SDavid du Colombier 		}
8743e12c5d1SDavid du Colombier 		if(t->mag == ld)
8753e12c5d1SDavid du Colombier 			return;
8763e12c5d1SDavid du Colombier 		t->mag = ld;
8773e12c5d1SDavid du Colombier 		redraw(t);
8783e12c5d1SDavid du Colombier 		return;
8793e12c5d1SDavid du Colombier 	}
8803e12c5d1SDavid du Colombier 	if(strcmp(tag, "ascent") == 0){
8813e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
8823e12c5d1SDavid du Colombier 			mesg("illegal ascent");
8833e12c5d1SDavid du Colombier 			return;
8843e12c5d1SDavid du Colombier 		}
8853e12c5d1SDavid du Colombier 		if(t->s->ascent == ld)
8863e12c5d1SDavid du Colombier 			return;
8873e12c5d1SDavid du Colombier 		t->s->ascent = ld;
8883e12c5d1SDavid du Colombier 		text(t);
8893e12c5d1SDavid du Colombier 		t->mod = 1;
8903e12c5d1SDavid du Colombier 		return;
8913e12c5d1SDavid du Colombier 	}
8923e12c5d1SDavid du Colombier 	if(strcmp(tag, "height") == 0){
8933e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
8943e12c5d1SDavid du Colombier 			mesg("illegal height");
8953e12c5d1SDavid du Colombier 			return;
8963e12c5d1SDavid du Colombier 		}
8973e12c5d1SDavid du Colombier 		if(t->s->height == ld)
8983e12c5d1SDavid du Colombier 			return;
8993e12c5d1SDavid du Colombier 		t->s->height = ld;
9003e12c5d1SDavid du Colombier 		text(t);
9013e12c5d1SDavid du Colombier 		t->mod = 1;
9023e12c5d1SDavid du Colombier 		return;
9033e12c5d1SDavid du Colombier 	}
9043e12c5d1SDavid du Colombier 	if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
9053e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
9063e12c5d1SDavid du Colombier 			mesg("illegal value");
9073e12c5d1SDavid du Colombier 			return;
9083e12c5d1SDavid du Colombier 		}
9093e12c5d1SDavid du Colombier 		fc = &t->parent->s->info[t->c];
9103e12c5d1SDavid du Colombier 		if(strcmp(tag, "left")==0){
9113e12c5d1SDavid du Colombier 			if(fc->left == ld)
9123e12c5d1SDavid du Colombier 				return;
9133e12c5d1SDavid du Colombier 			fc->left = ld;
9143e12c5d1SDavid du Colombier 		}else{
9153e12c5d1SDavid du Colombier 			if(fc->width == ld)
9163e12c5d1SDavid du Colombier 				return;
9173e12c5d1SDavid du Colombier 			fc->width = ld;
9183e12c5d1SDavid du Colombier 		}
9193e12c5d1SDavid du Colombier 		text(t);
9203e12c5d1SDavid du Colombier 		t->parent->mod = 1;
9213e12c5d1SDavid du Colombier 		return;
9223e12c5d1SDavid du Colombier 	}
9233e12c5d1SDavid du Colombier 	if(strcmp(tag, "offset(hex)") == 0){
9243e12c5d1SDavid du Colombier 		if(!strchr(hex, buf[0])){
9253e12c5d1SDavid du Colombier 	illoff:
9263e12c5d1SDavid du Colombier 			mesg("illegal offset");
9273e12c5d1SDavid du Colombier 			return;
9283e12c5d1SDavid du Colombier 		}
9293e12c5d1SDavid du Colombier 		s = 0;
9303e12c5d1SDavid du Colombier 		ld = strtoul(buf, &s, 16);
9313e12c5d1SDavid du Colombier 		if(*s)
9323e12c5d1SDavid du Colombier 			goto illoff;
9333e12c5d1SDavid du Colombier 		t->off = ld;
9343e12c5d1SDavid du Colombier 		text(t);
9353e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
9363e12c5d1SDavid du Colombier 			if(nt->parent == t)
9373e12c5d1SDavid du Colombier 				text(nt);
9383e12c5d1SDavid du Colombier 		return;
9393e12c5d1SDavid du Colombier 	}
9403e12c5d1SDavid du Colombier 	if(strcmp(tag, "n") == 0){
9413e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
9423e12c5d1SDavid du Colombier 			mesg("illegal n");
9433e12c5d1SDavid du Colombier 			return;
9443e12c5d1SDavid du Colombier 		}
9453e12c5d1SDavid du Colombier 		f = t->s;
9463e12c5d1SDavid du Colombier 		if(w == f->n)
9473e12c5d1SDavid du Colombier 			return;
9483e12c5d1SDavid du Colombier 		doredraw = 0;
9493e12c5d1SDavid du Colombier 	again:
9503e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
9513e12c5d1SDavid du Colombier 			if(nt->parent == t){
9523e12c5d1SDavid du Colombier 				doredraw = 1;
9533e12c5d1SDavid du Colombier 				tclose1(nt);
9543e12c5d1SDavid du Colombier 				goto again;
9553e12c5d1SDavid du Colombier 			}
9563e12c5d1SDavid du Colombier 		r = t->b->r;
9573e12c5d1SDavid du Colombier 		if(w < f->n)
9583e12c5d1SDavid du Colombier 			r.max.x = f->info[w].x;
9593e12c5d1SDavid du Colombier 		b = balloc(r, t->b->ldepth);
9603e12c5d1SDavid du Colombier 		if(b == 0)
9613e12c5d1SDavid du Colombier 			goto nobmem;
9623e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, t->b, r, S);
9633e12c5d1SDavid du Colombier 		fdx = Dx(editr) - 2*Border;
9643e12c5d1SDavid du Colombier 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
9653e12c5d1SDavid du Colombier 			doredraw = 1;
9663e12c5d1SDavid du Colombier 		bfree(t->b);
9673e12c5d1SDavid du Colombier 		t->b = b;
9683e12c5d1SDavid du Colombier 		b = balloc(r, t->b->ldepth);
9693e12c5d1SDavid du Colombier 		if(b == 0)
9703e12c5d1SDavid du Colombier 			goto nobmem;
9713e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, t->b, r, S);
9723e12c5d1SDavid du Colombier 		nfc = malloc((w+1)*sizeof(Fontchar));
9733e12c5d1SDavid du Colombier 		if(nfc == 0){
9743e12c5d1SDavid du Colombier 			mesg("malloc failed");
9753e12c5d1SDavid du Colombier 			bfree(b);
9763e12c5d1SDavid du Colombier 			return;
9773e12c5d1SDavid du Colombier 		}
9783e12c5d1SDavid du Colombier 		fc = f->info;
9793e12c5d1SDavid du Colombier 		for(i=0; i<=w && i<=f->n; i++)
9803e12c5d1SDavid du Colombier 			nfc[i] = fc[i];
9813e12c5d1SDavid du Colombier 		if(w+1 < i)
9823e12c5d1SDavid du Colombier 			memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
9833e12c5d1SDavid du Colombier 		x = fc[f->n].x;
9843e12c5d1SDavid du Colombier 		for(; i<=w; i++)
9853e12c5d1SDavid du Colombier 			nfc[i].x = x;
9863e12c5d1SDavid du Colombier 		f = subfalloc(w, f->height, f->ascent, nfc, b, ~0, ~0);
9873e12c5d1SDavid du Colombier 		if(f == 0)
9883e12c5d1SDavid du Colombier 			goto nofmem;
9893e12c5d1SDavid du Colombier 		subffree(t->s);
9903e12c5d1SDavid du Colombier 		f->info = nfc;
9913e12c5d1SDavid du Colombier 		t->s = f;
9923e12c5d1SDavid du Colombier 		if(doredraw)
9933e12c5d1SDavid du Colombier 			redraw(thing);
9943e12c5d1SDavid du Colombier 		else
9953e12c5d1SDavid du Colombier 			draw(t, 0);
9963e12c5d1SDavid du Colombier 		t->mod = 1;
9973e12c5d1SDavid du Colombier 		return;
9983e12c5d1SDavid du Colombier 	}
9993e12c5d1SDavid du Colombier 	if(strcmp(tag, "iwidth") == 0){
10003e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
10013e12c5d1SDavid du Colombier 			mesg("illegal iwidth");
10023e12c5d1SDavid du Colombier 			return;
10033e12c5d1SDavid du Colombier 		}
10043e12c5d1SDavid du Colombier 		w -= Dx(t->b->r);
10053e12c5d1SDavid du Colombier 		if(w == 0)
10063e12c5d1SDavid du Colombier 			return;
10073e12c5d1SDavid du Colombier 		r = t->parent->b->r;
10083e12c5d1SDavid du Colombier 		r.max.x += w;
10093e12c5d1SDavid du Colombier 		c = t->c;
10103e12c5d1SDavid du Colombier 		t = t->parent;
10113e12c5d1SDavid du Colombier 		f = t->s;
10123e12c5d1SDavid du Colombier 		b = balloc(r, t->b->ldepth);
10133e12c5d1SDavid du Colombier 		if(b == 0)
10143e12c5d1SDavid du Colombier 			goto nobmem;
10153e12c5d1SDavid du Colombier 		fc = &f->info[c];
10163e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, t->b,
10173e12c5d1SDavid du Colombier 			Rect(t->b->r.min.x, t->b->r.min.y,
10183e12c5d1SDavid du Colombier 			fc[1].x, t->b->r.max.y), S);
10193e12c5d1SDavid du Colombier 		bitblt(b, Pt(fc[1].x+w, b->r.min.y), t->b,
10203e12c5d1SDavid du Colombier 			Rect(fc[1].x, t->b->r.min.y,
10213e12c5d1SDavid du Colombier 			t->b->r.max.x, t->b->r.max.y), S);
10223e12c5d1SDavid du Colombier 		fdx = Dx(editr) - 2*Border;
10233e12c5d1SDavid du Colombier 		doredraw = 0;
10243e12c5d1SDavid du Colombier 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
10253e12c5d1SDavid du Colombier 			doredraw = 1;
10263e12c5d1SDavid du Colombier 		bfree(t->b);
10273e12c5d1SDavid du Colombier 		t->b = b;
10283e12c5d1SDavid du Colombier 		b = balloc(r, t->b->ldepth);
10293e12c5d1SDavid du Colombier 		if(b == 0)
10303e12c5d1SDavid du Colombier 			goto nobmem;
10313e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, t->b, t->b->r, S);
10323e12c5d1SDavid du Colombier 		fc = &f->info[c+1];
10333e12c5d1SDavid du Colombier 		for(i=c+1; i<=f->n; i++, fc++)
10343e12c5d1SDavid du Colombier 			fc->x += w;
10353e12c5d1SDavid du Colombier 		f = subfalloc(f->n, f->height, f->ascent,
10363e12c5d1SDavid du Colombier 			f->info, b, ~0, ~0);
10373e12c5d1SDavid du Colombier 		if(f == 0)
10383e12c5d1SDavid du Colombier 			goto nofmem;
10393e12c5d1SDavid du Colombier 		/* t->s and f share info; free carefully */
10403e12c5d1SDavid du Colombier 		fc = f->info;
10413e12c5d1SDavid du Colombier 		t->s->info = 0;
10423e12c5d1SDavid du Colombier 		subffree(t->s);
10433e12c5d1SDavid du Colombier 		f->info = fc;
10443e12c5d1SDavid du Colombier 		t->s = f;
10453e12c5d1SDavid du Colombier 		if(doredraw)
10463e12c5d1SDavid du Colombier 			redraw(t);
10473e12c5d1SDavid du Colombier 		else
10483e12c5d1SDavid du Colombier 			draw(t, 0);
10493e12c5d1SDavid du Colombier 		/* redraw all affected chars */
10503e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next){
10513e12c5d1SDavid du Colombier 			if(nt->parent!=t || nt->c<c)
10523e12c5d1SDavid du Colombier 				continue;
10533e12c5d1SDavid du Colombier 			fc = &f->info[nt->c];
10543e12c5d1SDavid du Colombier 			r.min.x = fc[0].x;
10553e12c5d1SDavid du Colombier 			r.min.y = nt->b->r.min.y;
10563e12c5d1SDavid du Colombier 			r.max.x = fc[1].x;
10573e12c5d1SDavid du Colombier 			r.max.y = nt->b->r.max.y;
10583e12c5d1SDavid du Colombier 			b = balloc(r, nt->b->ldepth);
10593e12c5d1SDavid du Colombier 			if(b == 0)
10603e12c5d1SDavid du Colombier 				goto nobmem;
10613e12c5d1SDavid du Colombier 			bitblt(b, r.min, t->b, r, S);
10623e12c5d1SDavid du Colombier 			doredraw = 0;
10633e12c5d1SDavid du Colombier 			if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
10643e12c5d1SDavid du Colombier 				doredraw = 1;
10653e12c5d1SDavid du Colombier 			bfree(nt->b);
10663e12c5d1SDavid du Colombier 			nt->b = b;
10673e12c5d1SDavid du Colombier 			if(c != nt->c)
10683e12c5d1SDavid du Colombier 				text(nt);
10693e12c5d1SDavid du Colombier 			else{
10703e12c5d1SDavid du Colombier 				if(doredraw)
10713e12c5d1SDavid du Colombier 					redraw(nt);
10723e12c5d1SDavid du Colombier 				else
10733e12c5d1SDavid du Colombier 					draw(nt, 0);
10743e12c5d1SDavid du Colombier 			}
10753e12c5d1SDavid du Colombier 		}
10763e12c5d1SDavid du Colombier 		t->mod = 1;
10773e12c5d1SDavid du Colombier 		return;
10783e12c5d1SDavid du Colombier 	}
10793e12c5d1SDavid du Colombier 	mesg("cannot edit %s in file %s", tag, t->name);
10803e12c5d1SDavid du Colombier }
10813e12c5d1SDavid du Colombier 
10823e12c5d1SDavid du Colombier void
10833e12c5d1SDavid du Colombier cntledit(char *tag)
10843e12c5d1SDavid du Colombier {
10853e12c5d1SDavid du Colombier 	char buf[256];
10863e12c5d1SDavid du Colombier 	char *p, *q;
10873e12c5d1SDavid du Colombier 	ulong l;
10883e12c5d1SDavid du Colombier 	int i, inv;
10893e12c5d1SDavid du Colombier 
10903e12c5d1SDavid du Colombier 	buttons(Up);
10913e12c5d1SDavid du Colombier 	if(type(buf, tag) == 0)
10923e12c5d1SDavid du Colombier 		return;
10933e12c5d1SDavid du Colombier 	if(strcmp(tag, "mag") == 0){
10943e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
10953e12c5d1SDavid du Colombier 			mesg("illegal magnification");
10963e12c5d1SDavid du Colombier 			return;
10973e12c5d1SDavid du Colombier 		}
10983e12c5d1SDavid du Colombier 		mag = l;
10993e12c5d1SDavid du Colombier 		cntl();
11003e12c5d1SDavid du Colombier 		return;
11013e12c5d1SDavid du Colombier 	}
11023e12c5d1SDavid du Colombier 	if(strcmp(tag, "val(hex)") == 0){
11033e12c5d1SDavid du Colombier 		inv = 0;
11043e12c5d1SDavid du Colombier 		p = buf;
11053e12c5d1SDavid du Colombier 		if(*p == '~'){
11063e12c5d1SDavid du Colombier 			p++;
11073e12c5d1SDavid du Colombier 			inv = 1;
11083e12c5d1SDavid du Colombier 		}
11093e12c5d1SDavid du Colombier 		l = strtoul(p, &q, 16);
11103e12c5d1SDavid du Colombier 		if(l==0 && q==p){
11113e12c5d1SDavid du Colombier 			mesg("illegal magnification");
11123e12c5d1SDavid du Colombier 			return;
11133e12c5d1SDavid du Colombier 		}
11143e12c5d1SDavid du Colombier 		if(inv)
11153e12c5d1SDavid du Colombier 			l = ~l;
11163e12c5d1SDavid du Colombier 		val = l;
11173e12c5d1SDavid du Colombier 		cntl();
11183e12c5d1SDavid du Colombier 		return;
11193e12c5d1SDavid du Colombier 	}
11203e12c5d1SDavid du Colombier 	if(strcmp(tag, "but1")==0
11213e12c5d1SDavid du Colombier 	|| strcmp(tag, "but2")==0
11223e12c5d1SDavid du Colombier 	|| strcmp(tag, "copy")==0){
11233e12c5d1SDavid du Colombier 		i = strtofn(buf);
11243e12c5d1SDavid du Colombier 		if(i < 0){
11253e12c5d1SDavid du Colombier 			mesg("unknown function");
11263e12c5d1SDavid du Colombier 			return;
11273e12c5d1SDavid du Colombier 		}
11283e12c5d1SDavid du Colombier 		if(strcmp(tag, "but1") == 0)
11293e12c5d1SDavid du Colombier 			but1fn = i;
11303e12c5d1SDavid du Colombier 		else if(strcmp(tag, "but2") == 0)
11313e12c5d1SDavid du Colombier 			but2fn = i;
11323e12c5d1SDavid du Colombier 		else
11333e12c5d1SDavid du Colombier 			copyfn = i;
11343e12c5d1SDavid du Colombier 		cntl();
11353e12c5d1SDavid du Colombier 		return;
11363e12c5d1SDavid du Colombier 	}
11373e12c5d1SDavid du Colombier 	mesg("cannot edit %s", tag);
11383e12c5d1SDavid du Colombier }
11393e12c5d1SDavid du Colombier 
11403e12c5d1SDavid du Colombier void
11413e12c5d1SDavid du Colombier buttons(int ud)
11423e12c5d1SDavid du Colombier {
11433e12c5d1SDavid du Colombier 	while((mouse.buttons==0) != ud)
11443e12c5d1SDavid du Colombier 		mouse = emouse();
11453e12c5d1SDavid du Colombier }
11463e12c5d1SDavid du Colombier 
11473e12c5d1SDavid du Colombier int
11483e12c5d1SDavid du Colombier sweep(int butmask, Rectangle *r)
11493e12c5d1SDavid du Colombier {
11503e12c5d1SDavid du Colombier 	Point p;
11513e12c5d1SDavid du Colombier 
11523e12c5d1SDavid du Colombier 	cursorswitch(&sweep0);
11533e12c5d1SDavid du Colombier 	buttons(Down);
11543e12c5d1SDavid du Colombier 	if(mouse.buttons != butmask){
11553e12c5d1SDavid du Colombier 		buttons(Up);
11563e12c5d1SDavid du Colombier 		cursorswitch(0);
11573e12c5d1SDavid du Colombier 		return 0;
11583e12c5d1SDavid du Colombier 	}
11593e12c5d1SDavid du Colombier 	p = mouse.xy;
11603e12c5d1SDavid du Colombier 	cursorswitch(&box);
11613e12c5d1SDavid du Colombier 	r->min = p;
11623e12c5d1SDavid du Colombier 	r->max = p;
11633e12c5d1SDavid du Colombier 	while(mouse.buttons == butmask){
11643e12c5d1SDavid du Colombier 		border(&screen, *r, -2, ~D);
11653e12c5d1SDavid du Colombier 		bflush();
11663e12c5d1SDavid du Colombier 		mouse = emouse();
11673e12c5d1SDavid du Colombier 		border(&screen, *r, -2, ~D);
11683e12c5d1SDavid du Colombier 		*r = rcanon(Rpt(p, mouse.xy));
11693e12c5d1SDavid du Colombier 	}
11703e12c5d1SDavid du Colombier 	cursorswitch(0);
11713e12c5d1SDavid du Colombier 	if(mouse.buttons){
11723e12c5d1SDavid du Colombier 		buttons(Up);
11733e12c5d1SDavid du Colombier 		return 0;
11743e12c5d1SDavid du Colombier 	}
11753e12c5d1SDavid du Colombier 	return 1;
11763e12c5d1SDavid du Colombier }
11773e12c5d1SDavid du Colombier 
11783e12c5d1SDavid du Colombier void
11793e12c5d1SDavid du Colombier openedit(Thing *t, Point pt, int c)
11803e12c5d1SDavid du Colombier {
11813e12c5d1SDavid du Colombier 	int x, y;
11823e12c5d1SDavid du Colombier 	Point p;
11833e12c5d1SDavid du Colombier 	Rectangle r;
11843e12c5d1SDavid du Colombier 	Rectangle br;
11853e12c5d1SDavid du Colombier 	Fontchar *fc;
11863e12c5d1SDavid du Colombier 	Thing *nt;
11873e12c5d1SDavid du Colombier 
11883e12c5d1SDavid du Colombier 	br = t->b->r;
11893e12c5d1SDavid du Colombier 	if(t->s == 0){
11903e12c5d1SDavid du Colombier 		c = -1;
11913e12c5d1SDavid du Colombier 		/* if big enough to bother, sweep box */
11923e12c5d1SDavid du Colombier 		if(Dx(br)<=16 && Dy(br)<=16)
11933e12c5d1SDavid du Colombier 			r = br;
11943e12c5d1SDavid du Colombier 		else{
11953e12c5d1SDavid du Colombier 			if(!sweep(1, &r))
11963e12c5d1SDavid du Colombier 				return;
11973e12c5d1SDavid du Colombier 			r = raddp(r, sub(br.min, t->r.min));
11983e12c5d1SDavid du Colombier 			if(!rectclip(&r, br))
11993e12c5d1SDavid du Colombier 				return;
12003e12c5d1SDavid du Colombier 			if(Dx(br) <= 8){
12013e12c5d1SDavid du Colombier 				r.min.x = br.min.x;
12023e12c5d1SDavid du Colombier 				r.max.x = br.max.x;
12033e12c5d1SDavid du Colombier 			}else if(Dx(r) < 4){
12043e12c5d1SDavid du Colombier 	    toosmall:
12053e12c5d1SDavid du Colombier 				mesg("rectangle too small");
12063e12c5d1SDavid du Colombier 				return;
12073e12c5d1SDavid du Colombier 			}
12083e12c5d1SDavid du Colombier 			if(Dy(br) <= 8){
12093e12c5d1SDavid du Colombier 				r.min.y = br.min.y;
12103e12c5d1SDavid du Colombier 				r.max.y = br.max.y;
12113e12c5d1SDavid du Colombier 			}else if(Dy(r) < 4)
12123e12c5d1SDavid du Colombier 				goto toosmall;
12133e12c5d1SDavid du Colombier 		}
12143e12c5d1SDavid du Colombier 	}else if(c >= 0){
12153e12c5d1SDavid du Colombier 		fc = &t->s->info[c];
12163e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
12173e12c5d1SDavid du Colombier 		r.min.y = br.min.y;
12183e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
12193e12c5d1SDavid du Colombier 		r.max.y = br.min.y + Dy(br);
12203e12c5d1SDavid du Colombier 	}else{
12213e12c5d1SDavid du Colombier 		/* just point at character */
12223e12c5d1SDavid du Colombier 		fc = t->s->info;
12233e12c5d1SDavid du Colombier 		p = add(pt, sub(br.min, t->r.min));
12243e12c5d1SDavid du Colombier 		x = br.min.x;
12253e12c5d1SDavid du Colombier 		y = br.min.y;
12263e12c5d1SDavid du Colombier 		for(c=0; c<t->s->n; c++,fc++){
12273e12c5d1SDavid du Colombier 	    again:
12283e12c5d1SDavid du Colombier 			r.min.x = x;
12293e12c5d1SDavid du Colombier 			r.min.y = y;
12303e12c5d1SDavid du Colombier 			r.max.x = x + fc[1].x - fc[0].x;
12313e12c5d1SDavid du Colombier 			r.max.y = y + Dy(br);
12323e12c5d1SDavid du Colombier 			if(ptinrect(p, r))
12333e12c5d1SDavid du Colombier 				goto found;
12343e12c5d1SDavid du Colombier 			if(r.max.x >= br.min.x+Dx(t->r)){
12353e12c5d1SDavid du Colombier 				x -= Dx(t->r);
12363e12c5d1SDavid du Colombier 				y += t->s->height;
12373e12c5d1SDavid du Colombier 				if(fc[1].x > fc[0].x)
12383e12c5d1SDavid du Colombier 					goto again;
12393e12c5d1SDavid du Colombier 			}
12403e12c5d1SDavid du Colombier 			x += fc[1].x - fc[0].x;
12413e12c5d1SDavid du Colombier 		}
12423e12c5d1SDavid du Colombier 		return;
12433e12c5d1SDavid du Colombier 	   found:
12443e12c5d1SDavid du Colombier 		r = br;
12453e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
12463e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
12473e12c5d1SDavid du Colombier 	}
12483e12c5d1SDavid du Colombier 	nt = malloc(sizeof(Thing));
12493e12c5d1SDavid du Colombier 	if(nt == 0){
12503e12c5d1SDavid du Colombier    nomem:
12513e12c5d1SDavid du Colombier 		mesg("can't allocate: %r");
12523e12c5d1SDavid du Colombier 		return;
12533e12c5d1SDavid du Colombier 	}
12543e12c5d1SDavid du Colombier 	memset(nt, 0, sizeof(Thing));
12553e12c5d1SDavid du Colombier 	nt->c = c;
12563e12c5d1SDavid du Colombier 	nt->b = balloc(r, t->b->ldepth);
12573e12c5d1SDavid du Colombier 	if(nt->b == 0){
12583e12c5d1SDavid du Colombier 		free(nt);
12593e12c5d1SDavid du Colombier 		goto nomem;
12603e12c5d1SDavid du Colombier 	}
12613e12c5d1SDavid du Colombier 	bitblt(nt->b, r.min, t->b, r, S);
12623e12c5d1SDavid du Colombier 	nt->name = strdup(t->name);
12633e12c5d1SDavid du Colombier 	if(nt->name == 0){
12643e12c5d1SDavid du Colombier 		bfree(nt->b);
12653e12c5d1SDavid du Colombier 		free(nt);
12663e12c5d1SDavid du Colombier 		goto nomem;
12673e12c5d1SDavid du Colombier 	}
12683e12c5d1SDavid du Colombier 	nt->parent = t;
12693e12c5d1SDavid du Colombier 	nt->mag = mag;
12703e12c5d1SDavid du Colombier 	draw(nt, 1);
12713e12c5d1SDavid du Colombier }
12723e12c5d1SDavid du Colombier 
12733e12c5d1SDavid du Colombier void
12743e12c5d1SDavid du Colombier ckinfo(Thing *t, Rectangle mod)
12753e12c5d1SDavid du Colombier {
12763e12c5d1SDavid du Colombier 	int i, j, k, top, bot, n, zero;
12773e12c5d1SDavid du Colombier 	Fontchar *fc;
12783e12c5d1SDavid du Colombier 	Rectangle r;
12793e12c5d1SDavid du Colombier 	Bitmap *b;
12803e12c5d1SDavid du Colombier 	Thing *nt;
12813e12c5d1SDavid du Colombier 
12823e12c5d1SDavid du Colombier 	if(t->parent)
12833e12c5d1SDavid du Colombier 		t = t->parent;
12843e12c5d1SDavid du Colombier 	if(t->s==0 || Dy(t->b->r)==0)
12853e12c5d1SDavid du Colombier 		return;
12863e12c5d1SDavid du Colombier 	b = 0;
12873e12c5d1SDavid du Colombier 	/* check bounding boxes */
12883e12c5d1SDavid du Colombier 	fc = &t->s->info[0];
12893e12c5d1SDavid du Colombier 	r.min.y = t->b->r.min.y;
12903e12c5d1SDavid du Colombier 	r.max.y = t->b->r.max.y;
12913e12c5d1SDavid du Colombier 	for(i=0; i<t->s->n; i++, fc++){
12923e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
12933e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
12943e12c5d1SDavid du Colombier 		if(!rectXrect(mod, r))
12953e12c5d1SDavid du Colombier 			continue;
12963e12c5d1SDavid du Colombier 		if(b==0 || Dx(b->r)<Dx(r)){
12973e12c5d1SDavid du Colombier 			if(b)
12983e12c5d1SDavid du Colombier 				bfree(b);
12993e12c5d1SDavid du Colombier 			b = balloc(rsubp(r, r.min), t->b->ldepth);
13003e12c5d1SDavid du Colombier 			if(b == 0){
13013e12c5d1SDavid du Colombier 				mesg("can't balloc");
13023e12c5d1SDavid du Colombier 				break;
13033e12c5d1SDavid du Colombier 			}
13043e12c5d1SDavid du Colombier 		}
13053e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, b, b->r, 0);
13063e12c5d1SDavid du Colombier 		bitblt(b, b->r.min, t->b, r, S);
13073e12c5d1SDavid du Colombier 		top = 100000;
13083e12c5d1SDavid du Colombier 		bot = 0;
13093e12c5d1SDavid du Colombier 		n = 2+(Dx(r)/8<<t->b->ldepth);
13103e12c5d1SDavid du Colombier 		for(j=0; j<b->r.max.y; j++){
13113e12c5d1SDavid du Colombier 			memset(data, 0, n);
13123e12c5d1SDavid du Colombier 			rdbitmap(b, j, j+1, data);
13133e12c5d1SDavid du Colombier 			zero = 1;
13143e12c5d1SDavid du Colombier 			for(k=0; k<n; k++)
13153e12c5d1SDavid du Colombier 				if(data[k]){
13163e12c5d1SDavid du Colombier 					zero = 0;
13173e12c5d1SDavid du Colombier 					break;
13183e12c5d1SDavid du Colombier 				}
13193e12c5d1SDavid du Colombier 			if(!zero){
13203e12c5d1SDavid du Colombier 				if(top > j)
13213e12c5d1SDavid du Colombier 					top = j;
13223e12c5d1SDavid du Colombier 				bot = j+1;
13233e12c5d1SDavid du Colombier 			}
13243e12c5d1SDavid du Colombier 		}
13253e12c5d1SDavid du Colombier 		if(top > j)
13263e12c5d1SDavid du Colombier 			top = 0;
13273e12c5d1SDavid du Colombier 		if(top!=fc->top || bot!=fc->bottom){
13283e12c5d1SDavid du Colombier 			fc->top = top;
13293e12c5d1SDavid du Colombier 			fc->bottom = bot;
13303e12c5d1SDavid du Colombier 			for(nt=thing; nt; nt=nt->next)
13313e12c5d1SDavid du Colombier 				if(nt->parent==t && nt->c==i)
13323e12c5d1SDavid du Colombier 					text(nt);
13333e12c5d1SDavid du Colombier 		}
13343e12c5d1SDavid du Colombier 	}
13353e12c5d1SDavid du Colombier 	if(b)
13363e12c5d1SDavid du Colombier 		bfree(b);
13373e12c5d1SDavid du Colombier }
13383e12c5d1SDavid du Colombier 
13393e12c5d1SDavid du Colombier Point
13403e12c5d1SDavid du Colombier screenpt(Thing *t, Point realp)
13413e12c5d1SDavid du Colombier {
13423e12c5d1SDavid du Colombier 	int fdx, n;
13433e12c5d1SDavid du Colombier 	Point p;
13443e12c5d1SDavid du Colombier 
13453e12c5d1SDavid du Colombier 	fdx = Dx(editr)-2*Border;
13463e12c5d1SDavid du Colombier 	if(t->mag > 1)
13473e12c5d1SDavid du Colombier 		fdx -= fdx%t->mag;
13483e12c5d1SDavid du Colombier 	p = mul(sub(realp, t->b->r.min), t->mag);
13493e12c5d1SDavid du Colombier 	if(fdx < Dx(t->b->r)*t->mag){
13503e12c5d1SDavid du Colombier 		n = p.x/fdx;
13513e12c5d1SDavid du Colombier 		p.y += n * (Dy(t->b->r)*t->mag+Border);
13523e12c5d1SDavid du Colombier 		p.x -= n * fdx;
13533e12c5d1SDavid du Colombier 	}
13543e12c5d1SDavid du Colombier 	p = add(p, t->r.min);
13553e12c5d1SDavid du Colombier 	return p;
13563e12c5d1SDavid du Colombier }
13573e12c5d1SDavid du Colombier 
13583e12c5d1SDavid du Colombier Point
13593e12c5d1SDavid du Colombier realpt(Thing *t, Point screenp)
13603e12c5d1SDavid du Colombier {
13613e12c5d1SDavid du Colombier 	int fdx, n, dy;
13623e12c5d1SDavid du Colombier 	Point p;
13633e12c5d1SDavid du Colombier 
13643e12c5d1SDavid du Colombier 	fdx = (Dx(editr)-2*Border);
13653e12c5d1SDavid du Colombier 	if(t->mag > 1)
13663e12c5d1SDavid du Colombier 		fdx -= fdx%t->mag;
13673e12c5d1SDavid du Colombier 	p.y = screenp.y-t->r.min.y;
13683e12c5d1SDavid du Colombier 	p.x = 0;
13693e12c5d1SDavid du Colombier 	if(fdx < Dx(t->b->r)*t->mag){
13703e12c5d1SDavid du Colombier 		dy = Dy(t->b->r)*t->mag+Border;
13713e12c5d1SDavid du Colombier 		n = (p.y/dy);
13723e12c5d1SDavid du Colombier 		p.x = n * fdx;
13733e12c5d1SDavid du Colombier 		p.y -= n * dy;
13743e12c5d1SDavid du Colombier 	}
13753e12c5d1SDavid du Colombier 	p.x += screenp.x-t->r.min.x;
13763e12c5d1SDavid du Colombier 	p = add(div(p, t->mag), t->b->r.min);
13773e12c5d1SDavid du Colombier 	return p;
13783e12c5d1SDavid du Colombier }
13793e12c5d1SDavid du Colombier 
13803e12c5d1SDavid du Colombier void
13813e12c5d1SDavid du Colombier twidpix(Thing *t, Point p, int set)
13823e12c5d1SDavid du Colombier {
13833e12c5d1SDavid du Colombier 	Bitmap *b;
13843e12c5d1SDavid du Colombier 	int v, c;
13853e12c5d1SDavid du Colombier 
13863e12c5d1SDavid du Colombier 	c = but1fn;
13873e12c5d1SDavid du Colombier 	if(!set)
13883e12c5d1SDavid du Colombier 		c = but2fn;
13893e12c5d1SDavid du Colombier 	b = t->b;
13903e12c5d1SDavid du Colombier 	if(!ptinrect(p, b->r))
13913e12c5d1SDavid du Colombier 		return;
13923e12c5d1SDavid du Colombier 	point(b, p, val, c);
13933e12c5d1SDavid du Colombier 	v = bvalue(val, b->ldepth);
13943e12c5d1SDavid du Colombier 	p = screenpt(t, p);
13953e12c5d1SDavid du Colombier 	bitblt(&screen, p, values, Rect(v, 0, v+t->mag, t->mag), c);
13963e12c5d1SDavid du Colombier }
13973e12c5d1SDavid du Colombier 
13983e12c5d1SDavid du Colombier void
13993e12c5d1SDavid du Colombier twiddle(Thing *t)
14003e12c5d1SDavid du Colombier {
14013e12c5d1SDavid du Colombier 	int set;
14023e12c5d1SDavid du Colombier 	Point p, lastp;
14033e12c5d1SDavid du Colombier 	Bitmap *b;
14043e12c5d1SDavid du Colombier 	Thing *nt;
14053e12c5d1SDavid du Colombier 	Rectangle mod;
14063e12c5d1SDavid du Colombier 
14073e12c5d1SDavid du Colombier 	if(mouse.buttons!=1 && mouse.buttons!=2){
14083e12c5d1SDavid du Colombier 		buttons(Up);
14093e12c5d1SDavid du Colombier 		return;
14103e12c5d1SDavid du Colombier 	}
14113e12c5d1SDavid du Colombier 	set = mouse.buttons==1;
14123e12c5d1SDavid du Colombier 	b = t->b;
14133e12c5d1SDavid du Colombier 	lastp = add(b->r.min, Pt(-1, -1));
14143e12c5d1SDavid du Colombier 	mod = Rpt(add(b->r.max, Pt(1, 1)), lastp);
14153e12c5d1SDavid du Colombier 	while(mouse.buttons){
14163e12c5d1SDavid du Colombier 		p = realpt(t, mouse.xy);
14173e12c5d1SDavid du Colombier 		if(!eqpt(p, lastp)){
14183e12c5d1SDavid du Colombier 			lastp = p;
14193e12c5d1SDavid du Colombier 			if(ptinrect(p, b->r)){
14203e12c5d1SDavid du Colombier 				for(nt=thing; nt; nt=nt->next)
14213e12c5d1SDavid du Colombier 					if(nt->parent==t->parent || nt==t->parent)
14223e12c5d1SDavid du Colombier 						twidpix(nt, p, set);
14233e12c5d1SDavid du Colombier 				if(t->parent)
14243e12c5d1SDavid du Colombier 					t->parent->mod = 1;
14253e12c5d1SDavid du Colombier 				else
14263e12c5d1SDavid du Colombier 					t->mod = 1;
14273e12c5d1SDavid du Colombier 				if(p.x < mod.min.x)
14283e12c5d1SDavid du Colombier 					mod.min.x = p.x;
14293e12c5d1SDavid du Colombier 				if(p.y < mod.min.y)
14303e12c5d1SDavid du Colombier 					mod.min.y = p.y;
14313e12c5d1SDavid du Colombier 				if(p.x >= mod.max.x)
14323e12c5d1SDavid du Colombier 					mod.max.x = p.x+1;
14333e12c5d1SDavid du Colombier 				if(p.y >= mod.max.y)
14343e12c5d1SDavid du Colombier 					mod.max.y = p.y+1;
14353e12c5d1SDavid du Colombier 			}
14363e12c5d1SDavid du Colombier 		}
14373e12c5d1SDavid du Colombier 		mouse = emouse();
14383e12c5d1SDavid du Colombier 	}
14393e12c5d1SDavid du Colombier 	ckinfo(t, mod);
14403e12c5d1SDavid du Colombier }
14413e12c5d1SDavid du Colombier 
14423e12c5d1SDavid du Colombier void
14433e12c5d1SDavid du Colombier select(void)
14443e12c5d1SDavid du Colombier {
14453e12c5d1SDavid du Colombier 	Thing *t;
14463e12c5d1SDavid du Colombier 	char line[128], buf[128];
14473e12c5d1SDavid du Colombier 	Point p;
14483e12c5d1SDavid du Colombier 
14493e12c5d1SDavid du Colombier 	if(ptinrect(mouse.xy, cntlr)){
14503e12c5d1SDavid du Colombier 		scntl(line);
14513e12c5d1SDavid du Colombier 		if(atline(cntlr.min.x, mouse.xy, line, buf)){
14523e12c5d1SDavid du Colombier 			if(mouse.buttons == 1)
14533e12c5d1SDavid du Colombier 				cntledit(buf);
14543e12c5d1SDavid du Colombier 			else
14553e12c5d1SDavid du Colombier 				buttons(Up);
14563e12c5d1SDavid du Colombier 			return;
14573e12c5d1SDavid du Colombier 		}
14583e12c5d1SDavid du Colombier 		return;
14593e12c5d1SDavid du Colombier 	}
14603e12c5d1SDavid du Colombier 	for(t=thing; t; t=t->next){
14613e12c5d1SDavid du Colombier 		if(attext(t, mouse.xy, buf)){
14623e12c5d1SDavid du Colombier 			if(mouse.buttons == 1)
14633e12c5d1SDavid du Colombier 				textedit(t, buf);
14643e12c5d1SDavid du Colombier 			else
14653e12c5d1SDavid du Colombier 				buttons(Up);
14663e12c5d1SDavid du Colombier 			return;
14673e12c5d1SDavid du Colombier 		}
14683e12c5d1SDavid du Colombier 		if(ptinrect(mouse.xy, t->r)){
14693e12c5d1SDavid du Colombier 			if(t->parent == 0){
14703e12c5d1SDavid du Colombier 				if(mouse.buttons == 1){
14713e12c5d1SDavid du Colombier 					p = mouse.xy;
14723e12c5d1SDavid du Colombier 					buttons(Up);
14733e12c5d1SDavid du Colombier 					openedit(t, p, -1);
14743e12c5d1SDavid du Colombier 				}else
14753e12c5d1SDavid du Colombier 					buttons(Up);
14763e12c5d1SDavid du Colombier 				return;
14773e12c5d1SDavid du Colombier 			}
14783e12c5d1SDavid du Colombier 			twiddle(t);
14793e12c5d1SDavid du Colombier 			return;
14803e12c5d1SDavid du Colombier 		}
14813e12c5d1SDavid du Colombier 	}
14823e12c5d1SDavid du Colombier }
14833e12c5d1SDavid du Colombier 
14843e12c5d1SDavid du Colombier void
14853e12c5d1SDavid du Colombier twrite(Thing *t)
14863e12c5d1SDavid du Colombier {
14873e12c5d1SDavid du Colombier 	int i, j, x, y, fd, ws, ld;
14883e12c5d1SDavid du Colombier 	Biobuf buf;
14893e12c5d1SDavid du Colombier 	Rectangle r;
14903e12c5d1SDavid du Colombier 
14913e12c5d1SDavid du Colombier 	if(t->parent)
14923e12c5d1SDavid du Colombier 		t = t->parent;
14933e12c5d1SDavid du Colombier 	cursorswitch(&busy);
14943e12c5d1SDavid du Colombier 	fd = create(t->name, OWRITE, 0666);
14953e12c5d1SDavid du Colombier 	if(fd < 0){
14963e12c5d1SDavid du Colombier 		mesg("can't write %s: %r", t->name);
14973e12c5d1SDavid du Colombier 		return;
14983e12c5d1SDavid du Colombier 	}
14993e12c5d1SDavid du Colombier 	if(t->face){
15003e12c5d1SDavid du Colombier 		r = t->b->r;
15013e12c5d1SDavid du Colombier 		ld = t->b->ldepth;
15023e12c5d1SDavid du Colombier 		/* This heuristic reflects peculiarly different formats */
15033e12c5d1SDavid du Colombier 		ws = 4;
15043e12c5d1SDavid du Colombier 		if(Dx(r) == 16)	/* probably cursor file */
15053e12c5d1SDavid du Colombier 			ws = 1;
15063e12c5d1SDavid du Colombier 		else if(Dx(r)<32 || ld==0)
15073e12c5d1SDavid du Colombier 			ws = 2;
15083e12c5d1SDavid du Colombier 		Binit(&buf, fd, OWRITE);
15093e12c5d1SDavid du Colombier 		for(y=r.min.y; y<r.max.y; y++){
15103e12c5d1SDavid du Colombier 			rdbitmap(t->b, y, y+1, data);
15113e12c5d1SDavid du Colombier 			j = 0;
15123e12c5d1SDavid du Colombier 			for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
15133e12c5d1SDavid du Colombier 				Bprint(&buf, "0x");
15143e12c5d1SDavid du Colombier 				for(i=0; i<ws; i++)
15153e12c5d1SDavid du Colombier 					Bprint(&buf, "%.2x", data[i+j]);
15163e12c5d1SDavid du Colombier 				Bprint(&buf, ", ");
15173e12c5d1SDavid du Colombier 			}
15183e12c5d1SDavid du Colombier 			Bprint(&buf, "\n");
15193e12c5d1SDavid du Colombier 		}
1520*219b2ee8SDavid du Colombier 		Bterm(&buf);
15213e12c5d1SDavid du Colombier 	}else{
15223e12c5d1SDavid du Colombier 		wrbitmapfile(fd, t->b);
15233e12c5d1SDavid du Colombier 		if(t->s)
15243e12c5d1SDavid du Colombier 			wrsubfontfile(fd, t->s);
15253e12c5d1SDavid du Colombier 	}
15263e12c5d1SDavid du Colombier 	t->mod = 0;
15273e12c5d1SDavid du Colombier 	close(fd);
15283e12c5d1SDavid du Colombier 	mesg("wrote %s", t->name);
15293e12c5d1SDavid du Colombier }
15303e12c5d1SDavid du Colombier 
15313e12c5d1SDavid du Colombier void
15323e12c5d1SDavid du Colombier tclose1(Thing *t)
15333e12c5d1SDavid du Colombier {
15343e12c5d1SDavid du Colombier 	Thing *nt;
15353e12c5d1SDavid du Colombier 
15363e12c5d1SDavid du Colombier 	if(t == thing)
15373e12c5d1SDavid du Colombier 		thing = t->next;
15383e12c5d1SDavid du Colombier 	else{
15393e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
15403e12c5d1SDavid du Colombier 			;
15413e12c5d1SDavid du Colombier 		nt->next = t->next;
15423e12c5d1SDavid du Colombier 	}
15433e12c5d1SDavid du Colombier 	do
15443e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
15453e12c5d1SDavid du Colombier 			if(nt->parent == t){
15463e12c5d1SDavid du Colombier 				tclose1(nt);
15473e12c5d1SDavid du Colombier 				break;
15483e12c5d1SDavid du Colombier 			}
15493e12c5d1SDavid du Colombier 	while(nt);
15503e12c5d1SDavid du Colombier 	if(t->s)
15513e12c5d1SDavid du Colombier 		subffree(t->s);
15523e12c5d1SDavid du Colombier 	bfree(t->b);
15533e12c5d1SDavid du Colombier 	free(t->name);
15543e12c5d1SDavid du Colombier 	free(t);
15553e12c5d1SDavid du Colombier }
15563e12c5d1SDavid du Colombier 
15573e12c5d1SDavid du Colombier void
15583e12c5d1SDavid du Colombier tclose(Thing *t)
15593e12c5d1SDavid du Colombier {
15603e12c5d1SDavid du Colombier 	Thing *ct;
15613e12c5d1SDavid du Colombier 
15623e12c5d1SDavid du Colombier 	if(t->mod){
15633e12c5d1SDavid du Colombier 		mesg("%s modified", t->name);
15643e12c5d1SDavid du Colombier 		t->mod = 0;
15653e12c5d1SDavid du Colombier 		return;
15663e12c5d1SDavid du Colombier 	}
15673e12c5d1SDavid du Colombier 	/* fiddle to save redrawing unmoved things */
15683e12c5d1SDavid du Colombier 	if(t == thing)
15693e12c5d1SDavid du Colombier 		ct = 0;
15703e12c5d1SDavid du Colombier 	else
15713e12c5d1SDavid du Colombier 		for(ct=thing; ct; ct=ct->next)
15723e12c5d1SDavid du Colombier 			if(ct->next==t || ct->next->parent==t)
15733e12c5d1SDavid du Colombier 				break;
15743e12c5d1SDavid du Colombier 	tclose1(t);
15753e12c5d1SDavid du Colombier 	if(ct)
15763e12c5d1SDavid du Colombier 		ct = ct->next;
15773e12c5d1SDavid du Colombier 	else
15783e12c5d1SDavid du Colombier 		ct = thing;
15793e12c5d1SDavid du Colombier 	redraw(ct);
15803e12c5d1SDavid du Colombier }
15813e12c5d1SDavid du Colombier 
15823e12c5d1SDavid du Colombier void
15833e12c5d1SDavid du Colombier tread(Thing *t)
15843e12c5d1SDavid du Colombier {
15853e12c5d1SDavid du Colombier 	Thing *nt, *new;
15863e12c5d1SDavid du Colombier 	Fontchar *i;
15873e12c5d1SDavid du Colombier 	Rectangle r;
15883e12c5d1SDavid du Colombier 	int nclosed;
15893e12c5d1SDavid du Colombier 
15903e12c5d1SDavid du Colombier 	if(t->parent)
15913e12c5d1SDavid du Colombier 		t = t->parent;
15923e12c5d1SDavid du Colombier 	new = tget(t->name);
15933e12c5d1SDavid du Colombier 	if(new == 0)
15943e12c5d1SDavid du Colombier 		return;
15953e12c5d1SDavid du Colombier 	nclosed = 0;
15963e12c5d1SDavid du Colombier     again:
15973e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
15983e12c5d1SDavid du Colombier 		if(nt->parent == t){
15993e12c5d1SDavid du Colombier 			if(!rectinrect(nt->b->r, new->b->r)
16003e12c5d1SDavid du Colombier 			|| new->b->ldepth!=nt->b->ldepth){
16013e12c5d1SDavid du Colombier     closeit:
16023e12c5d1SDavid du Colombier 				nclosed++;
16033e12c5d1SDavid du Colombier 				nt->parent = 0;
16043e12c5d1SDavid du Colombier 				tclose1(nt);
16053e12c5d1SDavid du Colombier 				goto again;
16063e12c5d1SDavid du Colombier 			}
16073e12c5d1SDavid du Colombier 			if((t->s==0) != (new->s==0))
16083e12c5d1SDavid du Colombier 				goto closeit;
16093e12c5d1SDavid du Colombier 			if((t->face==0) != (new->face==0))
16103e12c5d1SDavid du Colombier 				goto closeit;
16113e12c5d1SDavid du Colombier 			if(t->s){	/* check same char */
16123e12c5d1SDavid du Colombier 				if(nt->c >= new->s->n)
16133e12c5d1SDavid du Colombier 					goto closeit;
16143e12c5d1SDavid du Colombier 				i = &new->s->info[nt->c];
16153e12c5d1SDavid du Colombier 				r.min.x = i[0].x;
16163e12c5d1SDavid du Colombier 				r.max.x = i[1].x;
16173e12c5d1SDavid du Colombier 				r.min.y = new->b->r.min.y;
16183e12c5d1SDavid du Colombier 				r.max.y = new->b->r.max.y;
16193e12c5d1SDavid du Colombier 				if(!eqrect(r, nt->b->r))
16203e12c5d1SDavid du Colombier 					goto closeit;
16213e12c5d1SDavid du Colombier 			}
16223e12c5d1SDavid du Colombier 			nt->parent = new;
16233e12c5d1SDavid du Colombier 			bitblt(nt->b, nt->b->r.min, new->b,
16243e12c5d1SDavid du Colombier 				nt->b->r, S);
16253e12c5d1SDavid du Colombier 		}
16263e12c5d1SDavid du Colombier 	new->next = t->next;
16273e12c5d1SDavid du Colombier 	if(t == thing)
16283e12c5d1SDavid du Colombier 		thing = new;
16293e12c5d1SDavid du Colombier 	else{
16303e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
16313e12c5d1SDavid du Colombier 			;
16323e12c5d1SDavid du Colombier 		nt->next = new;
16333e12c5d1SDavid du Colombier 	}
16343e12c5d1SDavid du Colombier 	if(t->s)
16353e12c5d1SDavid du Colombier 		subffree(t->s);
16363e12c5d1SDavid du Colombier 	bfree(t->b);
16373e12c5d1SDavid du Colombier 	free(t->name);
16383e12c5d1SDavid du Colombier 	free(t);
16393e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
16403e12c5d1SDavid du Colombier 		if(nt==new || nt->parent==new)
16413e12c5d1SDavid du Colombier 			if(nclosed == 0)
16423e12c5d1SDavid du Colombier 				draw(nt, 0);	/* can draw in place */
16433e12c5d1SDavid du Colombier 			else{
16443e12c5d1SDavid du Colombier 				redraw(nt);	/* must redraw all below */
16453e12c5d1SDavid du Colombier 				break;
16463e12c5d1SDavid du Colombier 			}
16473e12c5d1SDavid du Colombier }
16483e12c5d1SDavid du Colombier 
16493e12c5d1SDavid du Colombier void
16503e12c5d1SDavid du Colombier tchar(Thing *t)
16513e12c5d1SDavid du Colombier {
1652*219b2ee8SDavid du Colombier 	char buf[256], *p;
16533e12c5d1SDavid du Colombier 	Rune r;
1654*219b2ee8SDavid du Colombier 	ulong c, d;
16553e12c5d1SDavid du Colombier 
16563e12c5d1SDavid du Colombier 	if(t->s == 0){
16573e12c5d1SDavid du Colombier 		mesg("not a subfont");
16583e12c5d1SDavid du Colombier 		return;
16593e12c5d1SDavid du Colombier 	}
1660*219b2ee8SDavid du Colombier 	if(type(buf, "char (hex or character or hex-hex)") == 0)
16613e12c5d1SDavid du Colombier 		return;
16623e12c5d1SDavid du Colombier 	if(utflen(buf) == 1){
16633e12c5d1SDavid du Colombier 		chartorune(&r, buf);
16643e12c5d1SDavid du Colombier 		c = r;
1665*219b2ee8SDavid du Colombier 		d = r;
16663e12c5d1SDavid du Colombier 	}else{
16673e12c5d1SDavid du Colombier 		if(!strchr(hex, buf[0])){
16683e12c5d1SDavid du Colombier 			mesg("illegal hex character");
16693e12c5d1SDavid du Colombier 			return;
16703e12c5d1SDavid du Colombier 		}
16713e12c5d1SDavid du Colombier 		c = strtoul(buf, 0, 16);
1672*219b2ee8SDavid du Colombier 		d = c;
1673*219b2ee8SDavid du Colombier 		p = utfrune(buf, '-');
1674*219b2ee8SDavid du Colombier 		if(p){
1675*219b2ee8SDavid du Colombier 			d = strtoul(p+1, 0, 16);
1676*219b2ee8SDavid du Colombier 			if(d < c){
1677*219b2ee8SDavid du Colombier 				mesg("invalid range");
1678*219b2ee8SDavid du Colombier 				return;
1679*219b2ee8SDavid du Colombier 			}
1680*219b2ee8SDavid du Colombier 		}
16813e12c5d1SDavid du Colombier 	}
16823e12c5d1SDavid du Colombier 	c -= t->off;
1683*219b2ee8SDavid du Colombier 	d -= t->off;
1684*219b2ee8SDavid du Colombier 	while(c <= d){
16853e12c5d1SDavid du Colombier 		if(c<0 || c>=t->s->n){
16863e12c5d1SDavid du Colombier 			mesg("0x%lux not in font %s", c+t->off, t->name);
16873e12c5d1SDavid du Colombier 			return;
16883e12c5d1SDavid du Colombier 		}
16893e12c5d1SDavid du Colombier 		openedit(t, Pt(0, 0), c);
1690*219b2ee8SDavid du Colombier 		c++;
1691*219b2ee8SDavid du Colombier 	}
16923e12c5d1SDavid du Colombier }
16933e12c5d1SDavid du Colombier 
16943e12c5d1SDavid du Colombier void
16953e12c5d1SDavid du Colombier apply(void (*f)(Thing*))
16963e12c5d1SDavid du Colombier {
16973e12c5d1SDavid du Colombier 	Thing *t;
16983e12c5d1SDavid du Colombier 
16993e12c5d1SDavid du Colombier 	cursorswitch(&sight);
17003e12c5d1SDavid du Colombier 	buttons(Down);
17013e12c5d1SDavid du Colombier 	if(mouse.buttons == 4)
17023e12c5d1SDavid du Colombier 		for(t=thing; t; t=t->next)
17033e12c5d1SDavid du Colombier 			if(ptinrect(mouse.xy, t->er)){
17043e12c5d1SDavid du Colombier 				buttons(Up);
17053e12c5d1SDavid du Colombier 				f(t);
17063e12c5d1SDavid du Colombier 				break;
17073e12c5d1SDavid du Colombier 			}
17083e12c5d1SDavid du Colombier 	buttons(Up);
17093e12c5d1SDavid du Colombier 	cursorswitch(0);
17103e12c5d1SDavid du Colombier }
17113e12c5d1SDavid du Colombier 
17123e12c5d1SDavid du Colombier void
17133e12c5d1SDavid du Colombier copy(void)
17143e12c5d1SDavid du Colombier {
17153e12c5d1SDavid du Colombier 	Thing *st, *dt, *nt;
17163e12c5d1SDavid du Colombier 	Rectangle sr, dr, fr;
17173e12c5d1SDavid du Colombier 	Point p1, p2;
17183e12c5d1SDavid du Colombier 	int but, up;
17193e12c5d1SDavid du Colombier 
17203e12c5d1SDavid du Colombier 	if(!sweep(4, &sr))
17213e12c5d1SDavid du Colombier 		return;
17223e12c5d1SDavid du Colombier 	for(st=thing; st; st=st->next)
17233e12c5d1SDavid du Colombier 		if(rectXrect(sr, st->r))
17243e12c5d1SDavid du Colombier 			break;
17253e12c5d1SDavid du Colombier 	if(st == 0)
17263e12c5d1SDavid du Colombier 		return;
17273e12c5d1SDavid du Colombier 	/* click gives full rectangle */
17283e12c5d1SDavid du Colombier 	if(Dx(sr)<4 && Dy(sr)<4)
17293e12c5d1SDavid du Colombier 		sr = st->r;
17303e12c5d1SDavid du Colombier 	rectclip(&sr, st->r);
17313e12c5d1SDavid du Colombier 	p1 = realpt(st, sr.min);
17323e12c5d1SDavid du Colombier 	p2 = realpt(st, Pt(sr.min.x, sr.max.y));
17333e12c5d1SDavid du Colombier 	if(p1.x != p2.x){	/* swept across a fold */
17343e12c5d1SDavid du Colombier    onafold:
17353e12c5d1SDavid du Colombier 		mesg("sweep spans a fold");
17363e12c5d1SDavid du Colombier 		return;
17373e12c5d1SDavid du Colombier 	}
17383e12c5d1SDavid du Colombier 	p2 = realpt(st, sr.max);
17393e12c5d1SDavid du Colombier 	sr.min = p1;
17403e12c5d1SDavid du Colombier 	sr.max = p2;
17413e12c5d1SDavid du Colombier 	fr.min = screenpt(st, sr.min);
17423e12c5d1SDavid du Colombier 	fr.max = screenpt(st, sr.max);
17433e12c5d1SDavid du Colombier 	border(&screen, fr, -3, ~D);
17443e12c5d1SDavid du Colombier 	p1 = sub(p2, p1);	/* diagonal */
17453e12c5d1SDavid du Colombier 	if(p1.x==0 || p1.y==0){
17463e12c5d1SDavid du Colombier     Return:
17473e12c5d1SDavid du Colombier 		border(&screen, fr, -3, ~D);
17483e12c5d1SDavid du Colombier 		return;
17493e12c5d1SDavid du Colombier 	}
17503e12c5d1SDavid du Colombier 	cursorswitch(&box);
17513e12c5d1SDavid du Colombier 	up = 0;
17523e12c5d1SDavid du Colombier 	for(; mouse.buttons==0; mouse=emouse()){
17533e12c5d1SDavid du Colombier 		for(dt=thing; dt; dt=dt->next)
17543e12c5d1SDavid du Colombier 			if(ptinrect(mouse.xy, dt->er))
17553e12c5d1SDavid du Colombier 				break;
17563e12c5d1SDavid du Colombier 		if(up)
17573e12c5d1SDavid du Colombier 			border(&screen, dr, -2, ~D);
17583e12c5d1SDavid du Colombier 		up = 0;
17593e12c5d1SDavid du Colombier 		if(dt == 0)
17603e12c5d1SDavid du Colombier 			continue;
17613e12c5d1SDavid du Colombier 		dr.max = screenpt(dt, realpt(dt, mouse.xy));
17623e12c5d1SDavid du Colombier 		dr.min = sub(dr.max, mul(p1, dt->mag));
17633e12c5d1SDavid du Colombier 		if(!rectXrect(dr, dt->r))
17643e12c5d1SDavid du Colombier 			continue;
17653e12c5d1SDavid du Colombier 		border(&screen, dr, -2, ~D);
17663e12c5d1SDavid du Colombier 		up = 1;
17673e12c5d1SDavid du Colombier 	}
17683e12c5d1SDavid du Colombier 	/* if up==1, we had a hit */
17693e12c5d1SDavid du Colombier 	cursorswitch(0);
17703e12c5d1SDavid du Colombier 	if(up)
17713e12c5d1SDavid du Colombier 		border(&screen, dr, -2, ~D);
17723e12c5d1SDavid du Colombier 	but = mouse.buttons;
17733e12c5d1SDavid du Colombier 	border(&screen, fr, -3, ~D);
17743e12c5d1SDavid du Colombier 	buttons(Up);
17753e12c5d1SDavid du Colombier 	if(!up || but!=4)
17763e12c5d1SDavid du Colombier 		return;
17773e12c5d1SDavid du Colombier 	dt = 0;
17783e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
17793e12c5d1SDavid du Colombier 		if(rectXrect(dr, nt->r)){
17803e12c5d1SDavid du Colombier 			if(dt){
17813e12c5d1SDavid du Colombier 				mesg("ambiguous sweep");
17823e12c5d1SDavid du Colombier 				return;
17833e12c5d1SDavid du Colombier 			}
17843e12c5d1SDavid du Colombier 			dt = nt;
17853e12c5d1SDavid du Colombier 		}
17863e12c5d1SDavid du Colombier 	if(dt == 0)
17873e12c5d1SDavid du Colombier 		return;
17883e12c5d1SDavid du Colombier 	p1 = realpt(dt, dr.min);
17893e12c5d1SDavid du Colombier 	p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
17903e12c5d1SDavid du Colombier 	if(p1.x != p2.x)
17913e12c5d1SDavid du Colombier 		goto onafold;
17923e12c5d1SDavid du Colombier 	p2 = realpt(dt, dr.max);
17933e12c5d1SDavid du Colombier 	dr.min = p1;
17943e12c5d1SDavid du Colombier 	dr.max = p2;
17953e12c5d1SDavid du Colombier 	bitblt(dt->b, dr.min, st->b, sr, copyfn);
17963e12c5d1SDavid du Colombier 	if(dt->parent){
17973e12c5d1SDavid du Colombier 		bitblt(dt->parent->b, dr.min, dt->b, dr, S);
17983e12c5d1SDavid du Colombier 		dt = dt->parent;
17993e12c5d1SDavid du Colombier 	}
18003e12c5d1SDavid du Colombier 	draw(dt, 0);
18013e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
18023e12c5d1SDavid du Colombier 		if(nt->parent==dt && rectXrect(dr, nt->b->r)){
18033e12c5d1SDavid du Colombier 			bitblt(nt->b, dr.min, dt->b, dr, S);
18043e12c5d1SDavid du Colombier 			draw(nt, 0);
18053e12c5d1SDavid du Colombier 		}
18063e12c5d1SDavid du Colombier 	ckinfo(dt, dr);
18073e12c5d1SDavid du Colombier 	dt->mod = 1;
18083e12c5d1SDavid du Colombier }
18093e12c5d1SDavid du Colombier 
18103e12c5d1SDavid du Colombier void
18113e12c5d1SDavid du Colombier menu(void)
18123e12c5d1SDavid du Colombier {
18133e12c5d1SDavid du Colombier 	Thing *t;
18143e12c5d1SDavid du Colombier 	char *mod;
18153e12c5d1SDavid du Colombier 	int sel;
18163e12c5d1SDavid du Colombier 	char buf[256];
18173e12c5d1SDavid du Colombier 
18183e12c5d1SDavid du Colombier 	sel = menuhit(3, &mouse, &menu3);
18193e12c5d1SDavid du Colombier 	switch(sel){
18203e12c5d1SDavid du Colombier 	case Mopen:
18213e12c5d1SDavid du Colombier 		if(type(buf, "file")){
18223e12c5d1SDavid du Colombier 			t = tget(buf);
18233e12c5d1SDavid du Colombier 			if(t)
18243e12c5d1SDavid du Colombier 				draw(t, 1);
18253e12c5d1SDavid du Colombier 		}
18263e12c5d1SDavid du Colombier 		break;
18273e12c5d1SDavid du Colombier 	case Mwrite:
18283e12c5d1SDavid du Colombier 		apply(twrite);
18293e12c5d1SDavid du Colombier 		break;
18303e12c5d1SDavid du Colombier 	case Mread:
18313e12c5d1SDavid du Colombier 		apply(tread);
18323e12c5d1SDavid du Colombier 		break;
18333e12c5d1SDavid du Colombier 	case Mchar:
18343e12c5d1SDavid du Colombier 		apply(tchar);
18353e12c5d1SDavid du Colombier 		break;
18363e12c5d1SDavid du Colombier 	case Mcopy:
18373e12c5d1SDavid du Colombier 		copy();
18383e12c5d1SDavid du Colombier 		break;
18393e12c5d1SDavid du Colombier 	case Mclose:
18403e12c5d1SDavid du Colombier 		apply(tclose);
18413e12c5d1SDavid du Colombier 		break;
18423e12c5d1SDavid du Colombier 	case Mexit:
18433e12c5d1SDavid du Colombier 		mod = 0;
18443e12c5d1SDavid du Colombier 		for(t=thing; t; t=t->next)
18453e12c5d1SDavid du Colombier 			if(t->mod){
18463e12c5d1SDavid du Colombier 				mod = t->name;
18473e12c5d1SDavid du Colombier 				t->mod = 0;
18483e12c5d1SDavid du Colombier 			}
18493e12c5d1SDavid du Colombier 		if(mod){
18503e12c5d1SDavid du Colombier 			mesg("%s modified", mod);
18513e12c5d1SDavid du Colombier 			break;
18523e12c5d1SDavid du Colombier 		}
18533e12c5d1SDavid du Colombier 		cursorswitch(&skull);
18543e12c5d1SDavid du Colombier 		buttons(Down);
18553e12c5d1SDavid du Colombier 		if(mouse.buttons == 4){
18563e12c5d1SDavid du Colombier 			buttons(Up);
18573e12c5d1SDavid du Colombier 			exits(0);
18583e12c5d1SDavid du Colombier 		}
18593e12c5d1SDavid du Colombier 		buttons(Up);
18603e12c5d1SDavid du Colombier 		cursorswitch(0);
18613e12c5d1SDavid du Colombier 		break;
18623e12c5d1SDavid du Colombier 	}
18633e12c5d1SDavid du Colombier }
18643e12c5d1SDavid du Colombier 
18653e12c5d1SDavid du Colombier char*
18663e12c5d1SDavid du Colombier fntostr(uint f)
18673e12c5d1SDavid du Colombier {
18683e12c5d1SDavid du Colombier 	char **s;
18693e12c5d1SDavid du Colombier 	int i;
18703e12c5d1SDavid du Colombier 
18713e12c5d1SDavid du Colombier 	f &= F;
18723e12c5d1SDavid du Colombier 	s = fns;
18733e12c5d1SDavid du Colombier 	for(i=0; i<f; i++)
18743e12c5d1SDavid du Colombier 		do; while(*s++);
18753e12c5d1SDavid du Colombier 	return *s;
18763e12c5d1SDavid du Colombier }
18773e12c5d1SDavid du Colombier 
18783e12c5d1SDavid du Colombier int
18793e12c5d1SDavid du Colombier strtofn(char *str)
18803e12c5d1SDavid du Colombier {
18813e12c5d1SDavid du Colombier 	char **s;
18823e12c5d1SDavid du Colombier 	int i;
18833e12c5d1SDavid du Colombier 
18843e12c5d1SDavid du Colombier 	s = fns;
18853e12c5d1SDavid du Colombier 	i = 0;
18863e12c5d1SDavid du Colombier 	while(i <= F){
18873e12c5d1SDavid du Colombier 		if(*s == 0)
18883e12c5d1SDavid du Colombier 			i++;
18893e12c5d1SDavid du Colombier 		else
18903e12c5d1SDavid du Colombier 			if(strcmp(str, *s) == 0)
18913e12c5d1SDavid du Colombier 				return i;
18923e12c5d1SDavid du Colombier 		s++;
18933e12c5d1SDavid du Colombier 	}
18943e12c5d1SDavid du Colombier 	return -1;
18953e12c5d1SDavid du Colombier }
1896