xref: /plan9/sys/src/cmd/tweak.c (revision 43f733d75c04beba8aec964c72d613a997f33ac6)
13e12c5d1SDavid du Colombier #include <u.h>
23e12c5d1SDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier #include <cursor.h>
57dd7cddfSDavid du Colombier #include <event.h>
63e12c5d1SDavid du Colombier #include <bio.h>
73e12c5d1SDavid du Colombier 
83e12c5d1SDavid du Colombier typedef struct	Thing	Thing;
93e12c5d1SDavid du Colombier 
103e12c5d1SDavid du Colombier struct Thing
113e12c5d1SDavid du Colombier {
127dd7cddfSDavid du Colombier 	Image	*b;
133e12c5d1SDavid du Colombier 	Subfont 	*s;
143e12c5d1SDavid du Colombier 	char		*name;	/* file name */
153e12c5d1SDavid du Colombier 	int		face;		/* is 48x48 face file or cursor file*/
163e12c5d1SDavid du Colombier 	Rectangle r;		/* drawing region */
173e12c5d1SDavid du Colombier 	Rectangle tr;		/* text region */
183e12c5d1SDavid du Colombier 	Rectangle er;		/* entire region */
193e12c5d1SDavid du Colombier 	long		c;		/* character number in subfont */
203e12c5d1SDavid du Colombier 	int		mod;	/* modified */
213e12c5d1SDavid du Colombier 	int		mag;		/* magnification */
223e12c5d1SDavid du Colombier 	Rune		off;		/* offset for subfont indices */
233e12c5d1SDavid du Colombier 	Thing	*parent;	/* thing of which i'm an edit */
243e12c5d1SDavid du Colombier 	Thing	*next;
253e12c5d1SDavid du Colombier };
263e12c5d1SDavid du Colombier 
273e12c5d1SDavid du Colombier enum
283e12c5d1SDavid du Colombier {
293e12c5d1SDavid du Colombier 	Border	= 1,
303e12c5d1SDavid du Colombier 	Up		= 1,
313e12c5d1SDavid du Colombier 	Down	= 0,
323e12c5d1SDavid du Colombier 	Mag		= 4,
333e12c5d1SDavid du Colombier 	Maxmag	= 10,
343e12c5d1SDavid du Colombier };
353e12c5d1SDavid du Colombier 
363e12c5d1SDavid du Colombier enum
373e12c5d1SDavid du Colombier {
387dd7cddfSDavid du Colombier 	NORMAL	=0,
397dd7cddfSDavid du Colombier 	FACE	=1,
407dd7cddfSDavid du Colombier 	CURSOR	=2
417dd7cddfSDavid du Colombier };
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier enum
447dd7cddfSDavid du Colombier {
453e12c5d1SDavid du Colombier 	Mopen,
463e12c5d1SDavid du Colombier 	Mread,
473e12c5d1SDavid du Colombier 	Mwrite,
483e12c5d1SDavid du Colombier 	Mcopy,
493e12c5d1SDavid du Colombier 	Mchar,
507dd7cddfSDavid du Colombier 	Mpixels,
513e12c5d1SDavid du Colombier 	Mclose,
523e12c5d1SDavid du Colombier 	Mexit,
533e12c5d1SDavid du Colombier };
543e12c5d1SDavid du Colombier 
557dd7cddfSDavid du Colombier enum
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier 	Blue	= 54,
587dd7cddfSDavid du Colombier };
593e12c5d1SDavid du Colombier 
603e12c5d1SDavid du Colombier char	*menu3str[] = {
613e12c5d1SDavid du Colombier 	[Mopen]	"open",
623e12c5d1SDavid du Colombier 	[Mread]	"read",
633e12c5d1SDavid du Colombier 	[Mwrite]	"write",
643e12c5d1SDavid du Colombier 	[Mcopy]	"copy",
653e12c5d1SDavid du Colombier 	[Mchar]	"char",
667dd7cddfSDavid du Colombier 	[Mpixels]	"pixels",
673e12c5d1SDavid du Colombier 	[Mclose]	"close",
683e12c5d1SDavid du Colombier 	[Mexit]	"exit",
693e12c5d1SDavid du Colombier 	0,
703e12c5d1SDavid du Colombier };
713e12c5d1SDavid du Colombier 
723e12c5d1SDavid du Colombier Menu	menu3 = {
733e12c5d1SDavid du Colombier 	menu3str
743e12c5d1SDavid du Colombier };
753e12c5d1SDavid du Colombier 
763e12c5d1SDavid du Colombier Cursor sweep0 = {
773e12c5d1SDavid du Colombier 	{-7, -7},
783e12c5d1SDavid du Colombier 	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
793e12c5d1SDavid du Colombier 	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
803e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
813e12c5d1SDavid du Colombier 	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0},
823e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
833e12c5d1SDavid du Colombier 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
843e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
853e12c5d1SDavid du Colombier 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00}
863e12c5d1SDavid du Colombier };
873e12c5d1SDavid du Colombier 
883e12c5d1SDavid du Colombier Cursor box = {
893e12c5d1SDavid du Colombier 	{-7, -7},
903e12c5d1SDavid du Colombier 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
913e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
923e12c5d1SDavid du Colombier 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
933e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
943e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
953e12c5d1SDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
963e12c5d1SDavid du Colombier 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
973e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
983e12c5d1SDavid du Colombier };
993e12c5d1SDavid du Colombier 
1003e12c5d1SDavid du Colombier Cursor sight = {
1013e12c5d1SDavid du Colombier 	{-7, -7},
1023e12c5d1SDavid du Colombier 	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
1033e12c5d1SDavid du Colombier 	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
1043e12c5d1SDavid du Colombier 	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
1053e12c5d1SDavid du Colombier 	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
1063e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
1073e12c5d1SDavid du Colombier 	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
1083e12c5d1SDavid du Colombier 	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
1093e12c5d1SDavid du Colombier 	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
1103e12c5d1SDavid du Colombier };
1113e12c5d1SDavid du Colombier 
1127dd7cddfSDavid du Colombier Cursor pixel = {
1137dd7cddfSDavid du Colombier 	{-7, -7},
1147dd7cddfSDavid du Colombier 	{0x1f, 0xf8, 0x3f, 0xfc,  0x7f, 0xfe,  0xf8, 0x1f,
1157dd7cddfSDavid du Colombier 	0xf0, 0x0f,  0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f,
1167dd7cddfSDavid du Colombier 	0xfe, 0x7f, 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f,
1177dd7cddfSDavid du Colombier 	0x78, 0x1f, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, },
1187dd7cddfSDavid du Colombier 	{0x00, 0x00, 0x0f, 0xf0, 0x31, 0x8c, 0x21, 0x84,
1197dd7cddfSDavid du Colombier 	0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x40, 0x02,
1207dd7cddfSDavid du Colombier 	0x40, 0x02, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
1217dd7cddfSDavid du Colombier 	0x21, 0x84, 0x31, 0x8c, 0x0f, 0xf0, 0x00, 0x00, }
1227dd7cddfSDavid du Colombier };
1237dd7cddfSDavid du Colombier 
1243e12c5d1SDavid du Colombier Cursor busy = {
1253e12c5d1SDavid du Colombier 	{-7, -7},
1263e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1273e12c5d1SDavid du Colombier 	 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7,
1283e12c5d1SDavid du Colombier 	 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe,
1293e12c5d1SDavid du Colombier 	 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,},
1303e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1313e12c5d1SDavid du Colombier 	 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
1323e12c5d1SDavid du Colombier 	 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe,
1333e12c5d1SDavid du Colombier 	 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,}
1343e12c5d1SDavid du Colombier };
1353e12c5d1SDavid du Colombier 
1363e12c5d1SDavid du Colombier Cursor skull = {
1373e12c5d1SDavid du Colombier 	{-7,-7},
1383e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7,
1393e12c5d1SDavid du Colombier 	 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8,
1403e12c5d1SDavid du Colombier 	 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff,
1413e12c5d1SDavid du Colombier 	 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
1423e12c5d1SDavid du Colombier 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
1433e12c5d1SDavid du Colombier 	 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
1443e12c5d1SDavid du Colombier 	 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
1453e12c5d1SDavid du Colombier 	 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
1463e12c5d1SDavid du Colombier };
1473e12c5d1SDavid du Colombier 
1483e12c5d1SDavid du Colombier Rectangle	cntlr;		/* control region */
1493e12c5d1SDavid du Colombier Rectangle	editr;		/* editing region */
1503e12c5d1SDavid du Colombier Rectangle	textr;		/* text region */
1513e12c5d1SDavid du Colombier Thing		*thing;
1523e12c5d1SDavid du Colombier Mouse		mouse;
1533e12c5d1SDavid du Colombier char		hex[] = "0123456789abcdefABCDEF";
1543e12c5d1SDavid du Colombier jmp_buf		err;
1553e12c5d1SDavid du Colombier char		*file;
1563e12c5d1SDavid du Colombier int		mag;
1577dd7cddfSDavid du Colombier int		but1val = 0;
1587dd7cddfSDavid du Colombier int		but2val = 255;
1597dd7cddfSDavid du Colombier int		invert = 0;
1607dd7cddfSDavid du Colombier Image		*values[256];
1617dd7cddfSDavid du Colombier Image		*greyvalues[256];
1623e12c5d1SDavid du Colombier uchar		data[8192];
1633e12c5d1SDavid du Colombier 
1643e12c5d1SDavid du Colombier Thing*	tget(char*);
1653e12c5d1SDavid du Colombier void	mesg(char*, ...);
1667dd7cddfSDavid du Colombier void	drawthing(Thing*, int);
1673e12c5d1SDavid du Colombier void	select(void);
1683e12c5d1SDavid du Colombier void	menu(void);
1697dd7cddfSDavid du Colombier void	error(Display*, char*);
1703e12c5d1SDavid du Colombier void	buttons(int);
1713e12c5d1SDavid du Colombier void	drawall(void);
1723e12c5d1SDavid du Colombier void	tclose1(Thing*);
1733e12c5d1SDavid du Colombier 
1743e12c5d1SDavid du Colombier void
1753e12c5d1SDavid du Colombier main(int argc, char *argv[])
1763e12c5d1SDavid du Colombier {
1777dd7cddfSDavid du Colombier 	int i;
1783e12c5d1SDavid du Colombier 	Event e;
1793e12c5d1SDavid du Colombier 	Thing *t;
1803e12c5d1SDavid du Colombier 
1813e12c5d1SDavid du Colombier 	mag = Mag;
1827dd7cddfSDavid du Colombier 	if(initdraw(error, 0, "tweak") < 0){
1837dd7cddfSDavid du Colombier 		fprint(2, "tweak: initdraw failed: %r\n");
1847dd7cddfSDavid du Colombier 		exits("initdraw");
1857dd7cddfSDavid du Colombier 	}
1867dd7cddfSDavid du Colombier 	for(i=0; i<256; i++){
1877dd7cddfSDavid du Colombier 		values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i));
1887dd7cddfSDavid du Colombier 		greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF);
1897dd7cddfSDavid du Colombier 		if(values[i] == 0 || greyvalues[i] == 0)
1907dd7cddfSDavid du Colombier 			drawerror(display, "can't allocate image");
1917dd7cddfSDavid du Colombier 	}
1923e12c5d1SDavid du Colombier 	einit(Emouse|Ekeyboard);
1937dd7cddfSDavid du Colombier 	eresized(0);
1943e12c5d1SDavid du Colombier 	i = 1;
1953e12c5d1SDavid du Colombier 	setjmp(err);
1963e12c5d1SDavid du Colombier 	for(; i<argc; i++){
1973e12c5d1SDavid du Colombier 		file = argv[i];
1983e12c5d1SDavid du Colombier 		t = tget(argv[i]);
1993e12c5d1SDavid du Colombier 		if(t)
2007dd7cddfSDavid du Colombier 			drawthing(t, 1);
2017dd7cddfSDavid du Colombier 		flushimage(display, 1);
2023e12c5d1SDavid du Colombier 	}
2033e12c5d1SDavid du Colombier 	file = 0;
2043e12c5d1SDavid du Colombier 	setjmp(err);
2053e12c5d1SDavid du Colombier 	for(;;)
2063e12c5d1SDavid du Colombier 		switch(event(&e)){
2073e12c5d1SDavid du Colombier 		case Ekeyboard:
2083e12c5d1SDavid du Colombier 			break;
2093e12c5d1SDavid du Colombier 		case Emouse:
2103e12c5d1SDavid du Colombier 			mouse = e.mouse;
2113e12c5d1SDavid du Colombier 			if(mouse.buttons & 3){
2123e12c5d1SDavid du Colombier 				select();
2133e12c5d1SDavid du Colombier 				break;
2143e12c5d1SDavid du Colombier 			}
2153e12c5d1SDavid du Colombier 			if(mouse.buttons & 4)
2163e12c5d1SDavid du Colombier 				menu();
2173e12c5d1SDavid du Colombier 		}
2183e12c5d1SDavid du Colombier }
2193e12c5d1SDavid du Colombier 
2203e12c5d1SDavid du Colombier void
2217dd7cddfSDavid du Colombier error(Display*, char *s)
2223e12c5d1SDavid du Colombier {
2233e12c5d1SDavid du Colombier 	if(file)
2243e12c5d1SDavid du Colombier 		mesg("can't read %s: %s: %r", file, s);
2253e12c5d1SDavid du Colombier 	else
2263e12c5d1SDavid du Colombier 		mesg("/dev/bitblt error: %s", s);
2273e12c5d1SDavid du Colombier 	if(err[0])
2283e12c5d1SDavid du Colombier 		longjmp(err, 1);
2293e12c5d1SDavid du Colombier 	exits(s);
2303e12c5d1SDavid du Colombier }
2313e12c5d1SDavid du Colombier 
2323e12c5d1SDavid du Colombier void
2333e12c5d1SDavid du Colombier redraw(Thing *t)
2343e12c5d1SDavid du Colombier {
2353e12c5d1SDavid du Colombier 	Thing *nt;
2363e12c5d1SDavid du Colombier 	Point p;
2373e12c5d1SDavid du Colombier 
2383e12c5d1SDavid du Colombier 	if(thing==0 || thing==t)
2397dd7cddfSDavid du Colombier 		draw(screen, editr, display->white, nil, ZP);
2403e12c5d1SDavid du Colombier 	if(thing == 0)
2413e12c5d1SDavid du Colombier 		return;
2423e12c5d1SDavid du Colombier 	if(thing != t){
2433e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
2443e12c5d1SDavid du Colombier 			;
2457dd7cddfSDavid du Colombier 		draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
2467dd7cddfSDavid du Colombier 			display->white, nil, ZP);
2473e12c5d1SDavid du Colombier 	}
2483e12c5d1SDavid du Colombier 	for(nt=t; nt; nt=nt->next){
2497dd7cddfSDavid du Colombier 		drawthing(nt, 0);
2503e12c5d1SDavid du Colombier 		if(nt->next == 0){
2513e12c5d1SDavid du Colombier 			p = Pt(editr.min.x, nt->er.max.y);
2527dd7cddfSDavid du Colombier 			draw(screen, Rpt(p, editr.max), display->white, nil, ZP);
2533e12c5d1SDavid du Colombier 		}
2543e12c5d1SDavid du Colombier 	}
2553e12c5d1SDavid du Colombier 	mesg("");
2563e12c5d1SDavid du Colombier }
2573e12c5d1SDavid du Colombier 
2583e12c5d1SDavid du Colombier void
2597dd7cddfSDavid du Colombier eresized(int new)
2603e12c5d1SDavid du Colombier {
2617dd7cddfSDavid du Colombier 	if(new && getwindow(display, Refnone) < 0)
2627dd7cddfSDavid du Colombier 		error(display, "can't reattach to window");
2637dd7cddfSDavid du Colombier 	cntlr = insetrect(screen->clipr, 1);
2643e12c5d1SDavid du Colombier 	editr = cntlr;
2653e12c5d1SDavid du Colombier 	textr = editr;
2663e12c5d1SDavid du Colombier 	textr.min.y = textr.max.y - font->height;
2673e12c5d1SDavid du Colombier 	cntlr.max.y = cntlr.min.y + font->height;
2683e12c5d1SDavid du Colombier 	editr.min.y = cntlr.max.y+1;
2693e12c5d1SDavid du Colombier 	editr.max.y = textr.min.y-1;
2707dd7cddfSDavid du Colombier 	draw(screen, screen->clipr, display->white, nil, ZP);
2717dd7cddfSDavid du Colombier 	draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP);
2727dd7cddfSDavid du Colombier 	replclipr(screen, 0, editr);
2733e12c5d1SDavid du Colombier 	drawall();
2743e12c5d1SDavid du Colombier }
2753e12c5d1SDavid du Colombier 
2763e12c5d1SDavid du Colombier void
2773e12c5d1SDavid du Colombier mesgstr(Point p, int line, char *s)
2783e12c5d1SDavid du Colombier {
2793e12c5d1SDavid du Colombier 	Rectangle c, r;
2803e12c5d1SDavid du Colombier 
2813e12c5d1SDavid du Colombier 	r.min = p;
2823e12c5d1SDavid du Colombier 	r.min.y += line*font->height;
2833e12c5d1SDavid du Colombier 	r.max.y = r.min.y+font->height;
2843e12c5d1SDavid du Colombier 	r.max.x = editr.max.x;
2857dd7cddfSDavid du Colombier 	c = screen->clipr;
2867dd7cddfSDavid du Colombier 	replclipr(screen, 0, r);
2877dd7cddfSDavid du Colombier 	draw(screen, r, values[0xDD], nil, ZP);
2883e12c5d1SDavid du Colombier 	r.min.x++;
2897dd7cddfSDavid du Colombier 	string(screen, r.min, display->black, ZP, font, s);
2907dd7cddfSDavid du Colombier 	replclipr(screen, 0, c);
2917dd7cddfSDavid du Colombier 	flushimage(display, 1);
2923e12c5d1SDavid du Colombier }
2933e12c5d1SDavid du Colombier 
2943e12c5d1SDavid du Colombier void
2953e12c5d1SDavid du Colombier mesg(char *fmt, ...)
2963e12c5d1SDavid du Colombier {
2973e12c5d1SDavid du Colombier 	char buf[1024];
2987dd7cddfSDavid du Colombier 	va_list arg;
2993e12c5d1SDavid du Colombier 
3007dd7cddfSDavid du Colombier 	va_start(arg, fmt);
3019a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
3027dd7cddfSDavid du Colombier 	va_end(arg);
3033e12c5d1SDavid du Colombier 	mesgstr(textr.min, 0, buf);
3043e12c5d1SDavid du Colombier }
3053e12c5d1SDavid du Colombier 
3063e12c5d1SDavid du Colombier void
3073e12c5d1SDavid du Colombier tmesg(Thing *t, int line, char *fmt, ...)
3083e12c5d1SDavid du Colombier {
3093e12c5d1SDavid du Colombier 	char buf[1024];
3107dd7cddfSDavid du Colombier 	va_list arg;
3113e12c5d1SDavid du Colombier 
3127dd7cddfSDavid du Colombier 	va_start(arg, fmt);
3139a747e4fSDavid du Colombier 	vseprint(buf, buf+sizeof(buf), fmt, arg);
3147dd7cddfSDavid du Colombier 	va_end(arg);
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 {
3227dd7cddfSDavid du Colombier 	sprint(l, "mag: %d  but1: %d  but2: %d  invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
3233e12c5d1SDavid du Colombier }
3243e12c5d1SDavid du Colombier 
3253e12c5d1SDavid du Colombier void
3263e12c5d1SDavid du Colombier cntl(void)
3273e12c5d1SDavid du Colombier {
3283e12c5d1SDavid du Colombier 	char buf[256];
3293e12c5d1SDavid du Colombier 
3303e12c5d1SDavid du Colombier 	scntl(buf);
3313e12c5d1SDavid du Colombier 	mesgstr(cntlr.min, 0, buf);
3323e12c5d1SDavid du Colombier }
3333e12c5d1SDavid du Colombier 
3343e12c5d1SDavid du Colombier void
3353e12c5d1SDavid du Colombier stext(Thing *t, char *l0, char *l1)
3363e12c5d1SDavid du Colombier {
3373e12c5d1SDavid du Colombier 	Fontchar *fc;
3383e12c5d1SDavid du Colombier 	char buf[256];
3393e12c5d1SDavid du Colombier 
3403e12c5d1SDavid du Colombier 	l1[0] = 0;
3417dd7cddfSDavid du Colombier 	sprint(buf, "depth:%d r:%d %d  %d %d ",
3427dd7cddfSDavid du Colombier 		t->b->depth, t->b->r.min.x, t->b->r.min.y,
3433e12c5d1SDavid du Colombier 		t->b->r.max.x, t->b->r.max.y);
3443e12c5d1SDavid du Colombier 	if(t->parent)
3453e12c5d1SDavid du Colombier 		sprint(buf+strlen(buf), "mag: %d ", t->mag);
3463e12c5d1SDavid du Colombier 	sprint(l0, "%s file: %s", buf, t->name);
3473e12c5d1SDavid du Colombier 	if(t->c >= 0){
3483e12c5d1SDavid du Colombier 		fc = &t->parent->s->info[t->c];
3493e12c5d1SDavid du Colombier 		sprint(l1, "c(hex): %x c(char): %C x: %d "
3503e12c5d1SDavid du Colombier 			   "top: %d bottom: %d left: %d width: %d iwidth: %d",
3517dd7cddfSDavid du Colombier 			(int)(t->c+t->parent->off), (int)(t->c+t->parent->off),
3523e12c5d1SDavid du Colombier 			fc->x, fc->top, fc->bottom, fc->left,
3533e12c5d1SDavid du Colombier 			fc->width, Dx(t->b->r));
3543e12c5d1SDavid du Colombier 	}else if(t->s)
3553e12c5d1SDavid du Colombier 		sprint(l1, "offset(hex): %ux n:%d  height:%d  ascent:%d",
3563e12c5d1SDavid du Colombier 			t->off, t->s->n, t->s->height, t->s->ascent);
3573e12c5d1SDavid du Colombier }
3583e12c5d1SDavid du Colombier 
3593e12c5d1SDavid du Colombier void
3603e12c5d1SDavid du Colombier text(Thing *t)
3613e12c5d1SDavid du Colombier {
3623e12c5d1SDavid du Colombier 	char l0[256], l1[256];
3633e12c5d1SDavid du Colombier 
3643e12c5d1SDavid du Colombier 	stext(t, l0, l1);
3653e12c5d1SDavid du Colombier 	tmesg(t, 0, l0);
3663e12c5d1SDavid du Colombier 	if(l1[0])
3673e12c5d1SDavid du Colombier 		tmesg(t, 1, l1);
3683e12c5d1SDavid du Colombier }
3693e12c5d1SDavid du Colombier 
3703e12c5d1SDavid du Colombier void
3713e12c5d1SDavid du Colombier drawall(void)
3723e12c5d1SDavid du Colombier {
3733e12c5d1SDavid du Colombier 	Thing *t;
3743e12c5d1SDavid du Colombier 
3753e12c5d1SDavid du Colombier 	cntl();
3763e12c5d1SDavid du Colombier 	for(t=thing; t; t=t->next)
3777dd7cddfSDavid du Colombier 		drawthing(t, 0);
3783e12c5d1SDavid du Colombier }
3793e12c5d1SDavid du Colombier 
3803e12c5d1SDavid du Colombier int
3817dd7cddfSDavid du Colombier value(Image *b, int x)
3823e12c5d1SDavid du Colombier {
3833e12c5d1SDavid du Colombier 	int v, l, w;
3843e12c5d1SDavid du Colombier 	uchar mask;
3853e12c5d1SDavid du Colombier 
3867dd7cddfSDavid du Colombier 	w = b->depth;
3877dd7cddfSDavid du Colombier 	if(w > 8){
3883e12c5d1SDavid du Colombier 		mesg("ldepth too large");
3893e12c5d1SDavid du Colombier 		return 0;
3903e12c5d1SDavid du Colombier 	}
3917dd7cddfSDavid du Colombier 	l = log2[w];
3927dd7cddfSDavid du Colombier 	mask = (1<<w)-1;		/* ones at right end of word */
3933e12c5d1SDavid du Colombier 	x -= b->r.min.x&~(7>>l);	/* adjust x relative to first pixel */
3943e12c5d1SDavid du Colombier 	v = data[x>>(3-l)];
3953e12c5d1SDavid du Colombier 	v >>= ((7>>l)<<l) - ((x&(7>>l))<<l);	/* pixel at right end of word */
3963e12c5d1SDavid du Colombier 	v &= mask;			/* pixel at right end of word */
3973e12c5d1SDavid du Colombier 	return v;
3983e12c5d1SDavid du Colombier }
3993e12c5d1SDavid du Colombier 
4003e12c5d1SDavid du Colombier int
4017dd7cddfSDavid du Colombier bvalue(int v, int d)
4023e12c5d1SDavid du Colombier {
4037dd7cddfSDavid du Colombier 	v &= (1<<d)-1;
4047dd7cddfSDavid du Colombier 	if(d > screen->depth)
4057dd7cddfSDavid du Colombier 		v >>= d - screen->depth;
406219b2ee8SDavid du Colombier 	else
4077dd7cddfSDavid du Colombier 		while(d < screen->depth && d < 8){
4087dd7cddfSDavid du Colombier 			v |= v << d;
4097dd7cddfSDavid du Colombier 			d <<= 1;
4103e12c5d1SDavid du Colombier 		}
4117dd7cddfSDavid du Colombier 	if(v<0 || v>255){
4127dd7cddfSDavid du Colombier 		mesg("internal error: bad color");
4137dd7cddfSDavid du Colombier 		return Blue;
4147dd7cddfSDavid du Colombier 	}
4157dd7cddfSDavid du Colombier 	return v;
4163e12c5d1SDavid du Colombier }
4173e12c5d1SDavid du Colombier 
4183e12c5d1SDavid du Colombier void
4197dd7cddfSDavid du Colombier drawthing(Thing *nt, int link)
4203e12c5d1SDavid du Colombier {
4213e12c5d1SDavid du Colombier 	int nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
4223e12c5d1SDavid du Colombier 	Thing *t;
4233e12c5d1SDavid du Colombier 	Subfont *s;
4247dd7cddfSDavid du Colombier 	Image *b, *col;
4253e12c5d1SDavid du Colombier 	Point p, p1, p2;
4263e12c5d1SDavid du Colombier 
4273e12c5d1SDavid du Colombier 	if(link){
4283e12c5d1SDavid du Colombier 		nt->next = 0;
4293e12c5d1SDavid du Colombier 		if(thing == 0){
4303e12c5d1SDavid du Colombier 			thing = nt;
4313e12c5d1SDavid du Colombier 			y = editr.min.y;
4323e12c5d1SDavid du Colombier 		}else{
4333e12c5d1SDavid du Colombier 			for(t=thing; t->next; t=t->next)
4343e12c5d1SDavid du Colombier 				;
4353e12c5d1SDavid du Colombier 			t->next = nt;
4363e12c5d1SDavid du Colombier 			y = t->er.max.y;
4373e12c5d1SDavid du Colombier 		}
4383e12c5d1SDavid du Colombier 	}else{
4393e12c5d1SDavid du Colombier 		if(thing == nt)
4403e12c5d1SDavid du Colombier 			y = editr.min.y;
4413e12c5d1SDavid du Colombier 		else{
4423e12c5d1SDavid du Colombier 			for(t=thing; t->next!=nt; t=t->next)
4433e12c5d1SDavid du Colombier 				;
4443e12c5d1SDavid du Colombier 			y = t->er.max.y;
4453e12c5d1SDavid du Colombier 		}
4463e12c5d1SDavid du Colombier 	}
4473e12c5d1SDavid du Colombier 	s = nt->s;
4483e12c5d1SDavid du Colombier 	b = nt->b;
4493e12c5d1SDavid du Colombier 	nl = font->height;
4503e12c5d1SDavid du Colombier 	if(s || nt->c>=0)
4513e12c5d1SDavid du Colombier 		nl += font->height;
4523e12c5d1SDavid du Colombier 	fdx = Dx(editr) - 2*Border;
4533e12c5d1SDavid du Colombier 	dx = Dx(b->r);
4543e12c5d1SDavid du Colombier 	dy = Dy(b->r);
4553e12c5d1SDavid du Colombier 	if(nt->mag > 1){
4563e12c5d1SDavid du Colombier 		dx *= nt->mag;
4573e12c5d1SDavid du Colombier 		dy *= nt->mag;
4583e12c5d1SDavid du Colombier 		fdx -= fdx%nt->mag;
4593e12c5d1SDavid du Colombier 	}
4603e12c5d1SDavid du Colombier 	nf = 1 + dx/fdx;
4613e12c5d1SDavid du Colombier 	nt->er.min.y = y;
4623e12c5d1SDavid du Colombier 	nt->er.min.x = editr.min.x;
4633e12c5d1SDavid du Colombier 	nt->er.max.x = nt->er.min.x + Border + dx + Border;
4643e12c5d1SDavid du Colombier 	if(nt->er.max.x > editr.max.x)
4653e12c5d1SDavid du Colombier 		nt->er.max.x = editr.max.x;
4663e12c5d1SDavid du Colombier 	nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
4677dd7cddfSDavid du Colombier 	nt->r = insetrect(nt->er, Border);
4683e12c5d1SDavid du Colombier 	nt->er.max.x = editr.max.x;
4697dd7cddfSDavid du Colombier 	draw(screen, nt->er, display->white, nil, ZP);
4703e12c5d1SDavid du Colombier 	for(i=0; i<nf; i++){
4713e12c5d1SDavid du Colombier 		p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
4723e12c5d1SDavid du Colombier 		/* draw portion of bitmap */
4733e12c5d1SDavid du Colombier 		p = Pt(p1.x+1, p1.y);
4743e12c5d1SDavid du Colombier 		if(nt->mag == 1)
4757dd7cddfSDavid du Colombier 			draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)),
4767dd7cddfSDavid du Colombier 				b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y));
4773e12c5d1SDavid du Colombier 		else{
4783e12c5d1SDavid du Colombier 			for(y=b->r.min.y; y<b->r.max.y; y++){
4793e12c5d1SDavid du Colombier 				sy = p.y+(y-b->r.min.y)*nt->mag;
4807dd7cddfSDavid du Colombier 				unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data);
4813e12c5d1SDavid du Colombier 				for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
4823e12c5d1SDavid du Colombier 					sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
4833e12c5d1SDavid du Colombier 					if(sx >= nt->r.max.x)
4843e12c5d1SDavid du Colombier 						break;
4857dd7cddfSDavid du Colombier 					v = bvalue(value(b, x), b->depth);
4867dd7cddfSDavid du Colombier 					if(v == 255)
4877dd7cddfSDavid du Colombier 						continue;
4887dd7cddfSDavid du Colombier 					if(b->chan == GREY8)
4897dd7cddfSDavid du Colombier 						draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
4907dd7cddfSDavid du Colombier 							greyvalues[v], nil, ZP);
4917dd7cddfSDavid du Colombier 					else
4927dd7cddfSDavid du Colombier 						draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
4937dd7cddfSDavid du Colombier 							values[v], nil, ZP);
4943e12c5d1SDavid du Colombier 				}
4953e12c5d1SDavid du Colombier 
4963e12c5d1SDavid du Colombier 			}
4973e12c5d1SDavid du Colombier 		}
4983e12c5d1SDavid du Colombier 		/* line down left */
4997dd7cddfSDavid du Colombier 		if(i == 0)
5007dd7cddfSDavid du Colombier 			col = display->black;
5013e12c5d1SDavid du Colombier 		else
5027dd7cddfSDavid du Colombier 			col = display->white;
5037dd7cddfSDavid du Colombier 		draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP);
5047dd7cddfSDavid du Colombier 		/* line across top */
5057dd7cddfSDavid du Colombier 		draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP);
5067dd7cddfSDavid du Colombier 		p2 = p1;
5077dd7cddfSDavid du Colombier 		if(i == nf-1){
5087dd7cddfSDavid du Colombier 			p2.x += 1 + dx%fdx;
5097dd7cddfSDavid du Colombier 			col = display->black;
5107dd7cddfSDavid du Colombier 		}else{
5113e12c5d1SDavid du Colombier 			p2.x = nt->r.max.x;
5127dd7cddfSDavid du Colombier 			col = display->white;
5137dd7cddfSDavid du Colombier 		}
5143e12c5d1SDavid du Colombier 		/* line down right */
5157dd7cddfSDavid du Colombier 		draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP);
5163e12c5d1SDavid du Colombier 		/* line across bottom */
5173e12c5d1SDavid du Colombier 		if(i == nf-1){
5183e12c5d1SDavid du Colombier 			p1.y += Border+dy;
5197dd7cddfSDavid du Colombier 			draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP);
5203e12c5d1SDavid du Colombier 		}
5213e12c5d1SDavid du Colombier 	}
5223e12c5d1SDavid du Colombier 	nt->tr.min.x = editr.min.x;
5233e12c5d1SDavid du Colombier 	nt->tr.max.x = editr.max.x;
5243e12c5d1SDavid du Colombier 	nt->tr.min.y = nt->er.max.y + Border;
5253e12c5d1SDavid du Colombier 	nt->tr.max.y = nt->tr.min.y + nl;
5263e12c5d1SDavid du Colombier 	nt->er.max.y = nt->tr.max.y + Border;
5273e12c5d1SDavid du Colombier 	text(nt);
5283e12c5d1SDavid du Colombier }
5293e12c5d1SDavid du Colombier 
5303e12c5d1SDavid du Colombier int
5313e12c5d1SDavid du Colombier tohex(int c)
5323e12c5d1SDavid du Colombier {
5333e12c5d1SDavid du Colombier 	if('0'<=c && c<='9')
5343e12c5d1SDavid du Colombier 		return c - '0';
5353e12c5d1SDavid du Colombier 	if('a'<=c && c<='f')
5363e12c5d1SDavid du Colombier 		return 10 + (c - 'a');
5373e12c5d1SDavid du Colombier 	if('A'<=c && c<='F')
5383e12c5d1SDavid du Colombier 		return 10 + (c - 'A');
539*43f733d7SDavid du Colombier 	return 0;
5403e12c5d1SDavid du Colombier }
5413e12c5d1SDavid du Colombier 
5423e12c5d1SDavid du Colombier Thing*
5433e12c5d1SDavid du Colombier tget(char *file)
5443e12c5d1SDavid du Colombier {
5457dd7cddfSDavid du Colombier 	int i, j, fd, face, x, y, c, chan;
5467dd7cddfSDavid du Colombier 	Image *b;
5473e12c5d1SDavid du Colombier 	Subfont *s;
5483e12c5d1SDavid du Colombier 	Thing *t;
5499a747e4fSDavid du Colombier 	Dir *d;
5503e12c5d1SDavid du Colombier 	jmp_buf oerr;
5513e12c5d1SDavid du Colombier 	uchar buf[256];
5523e12c5d1SDavid du Colombier 	char *data;
5533e12c5d1SDavid du Colombier 
5549a747e4fSDavid du Colombier 	buf[0] = '\0';
5559a747e4fSDavid du Colombier 	errstr((char*)buf, sizeof buf);	/* flush pending error message */
5563e12c5d1SDavid du Colombier 	memmove(oerr, err, sizeof err);
5579a747e4fSDavid du Colombier 	d = nil;
5583e12c5d1SDavid du Colombier 	if(setjmp(err)){
5593e12c5d1SDavid du Colombier    Err:
5609a747e4fSDavid du Colombier 		free(d);
5613e12c5d1SDavid du Colombier 		memmove(err, oerr, sizeof err);
5623e12c5d1SDavid du Colombier 		return 0;
5633e12c5d1SDavid du Colombier 	}
5643e12c5d1SDavid du Colombier 	fd = open(file, OREAD);
5653e12c5d1SDavid du Colombier 	if(fd < 0){
5663e12c5d1SDavid du Colombier 		mesg("can't open %s: %r", file);
5673e12c5d1SDavid du Colombier 		goto Err;
5683e12c5d1SDavid du Colombier 	}
5699a747e4fSDavid du Colombier 	d = dirfstat(fd);
5709a747e4fSDavid du Colombier 	if(d == nil){
5713e12c5d1SDavid du Colombier 		mesg("can't stat bitmap file %s: %r", file);
5723e12c5d1SDavid du Colombier 		close(fd);
5733e12c5d1SDavid du Colombier 		goto Err;
5743e12c5d1SDavid du Colombier 	}
5757dd7cddfSDavid du Colombier 	if(read(fd, buf, 11) != 11){
5763e12c5d1SDavid du Colombier 		mesg("can't read %s: %r", file);
5773e12c5d1SDavid du Colombier 		close(fd);
5783e12c5d1SDavid du Colombier 		goto Err;
5793e12c5d1SDavid du Colombier 	}
5803e12c5d1SDavid du Colombier 	seek(fd, 0, 0);
5817dd7cddfSDavid du Colombier 	data = (char*)buf;
5827dd7cddfSDavid du Colombier 	if(*data == '{')
5837dd7cddfSDavid du Colombier 		data++;
5847dd7cddfSDavid du Colombier 	if(memcmp(data, "0x", 2)==0 && data[4]==','){
5857dd7cddfSDavid du Colombier 		/*
5867dd7cddfSDavid du Colombier 		 * cursor file
5877dd7cddfSDavid du Colombier 		 */
5887dd7cddfSDavid du Colombier 		face = CURSOR;
5897dd7cddfSDavid du Colombier 		s = 0;
5909a747e4fSDavid du Colombier 		data = malloc(d->length+1);
5917dd7cddfSDavid du Colombier 		if(data == 0){
5927dd7cddfSDavid du Colombier 			mesg("can't malloc buffer: %r");
5937dd7cddfSDavid du Colombier 			close(fd);
5947dd7cddfSDavid du Colombier 			goto Err;
5957dd7cddfSDavid du Colombier 		}
5969a747e4fSDavid du Colombier 		data[d->length] = 0;
5979a747e4fSDavid du Colombier 		if(read(fd, data, d->length) != d->length){
5987dd7cddfSDavid du Colombier 			mesg("can't read cursor file %s: %r", file);
5997dd7cddfSDavid du Colombier 			close(fd);
6007dd7cddfSDavid du Colombier 			goto Err;
6017dd7cddfSDavid du Colombier 		}
6027dd7cddfSDavid du Colombier 		b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill);
6037dd7cddfSDavid du Colombier 		if(b == 0){
6047dd7cddfSDavid du Colombier 			mesg("image alloc failed file %s: %r", file);
6057dd7cddfSDavid du Colombier 			free(data);
6067dd7cddfSDavid du Colombier 			close(fd);
6077dd7cddfSDavid du Colombier 			goto Err;
6087dd7cddfSDavid du Colombier 		}
6097dd7cddfSDavid du Colombier 		i = 0;
6107dd7cddfSDavid du Colombier 		for(x=0;x<64; ){
6117dd7cddfSDavid du Colombier 			if((c=data[i]) == '\0')
6127dd7cddfSDavid du Colombier 				goto ill;
6137dd7cddfSDavid du Colombier 			if(c=='0' && data[i+1] == 'x'){
6147dd7cddfSDavid du Colombier 				i += 2;
6157dd7cddfSDavid du Colombier 				continue;
6167dd7cddfSDavid du Colombier 			}
6177dd7cddfSDavid du Colombier 			if(strchr(hex, c)){
6187dd7cddfSDavid du Colombier 				buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
6197dd7cddfSDavid du Colombier 				i += 2;
6207dd7cddfSDavid du Colombier 				continue;
6217dd7cddfSDavid du Colombier 			}
6227dd7cddfSDavid du Colombier 			i++;
6237dd7cddfSDavid du Colombier 		}
6247dd7cddfSDavid du Colombier 		loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf);
6257dd7cddfSDavid du Colombier 		free(data);
6267dd7cddfSDavid du Colombier 	}else if(memcmp(buf, "0x", 2)==0){
6273e12c5d1SDavid du Colombier 		/*
6283e12c5d1SDavid du Colombier 		 * face file
6293e12c5d1SDavid du Colombier 		 */
6307dd7cddfSDavid du Colombier 		face = FACE;
6313e12c5d1SDavid du Colombier 		s = 0;
6329a747e4fSDavid du Colombier 		data = malloc(d->length+1);
6333e12c5d1SDavid du Colombier 		if(data == 0){
6343e12c5d1SDavid du Colombier 			mesg("can't malloc buffer: %r");
6353e12c5d1SDavid du Colombier 			close(fd);
6363e12c5d1SDavid du Colombier 			goto Err;
6373e12c5d1SDavid du Colombier 		}
6389a747e4fSDavid du Colombier 		data[d->length] = 0;
6399a747e4fSDavid du Colombier 		if(read(fd, data, d->length) != d->length){
6403e12c5d1SDavid du Colombier 			mesg("can't read bitmap file %s: %r", file);
6413e12c5d1SDavid du Colombier 			close(fd);
6423e12c5d1SDavid du Colombier 			goto Err;
6433e12c5d1SDavid du Colombier 		}
6449a747e4fSDavid du Colombier 		for(y=0,i=0; i<d->length; i++)
6453e12c5d1SDavid du Colombier 			if(data[i] == '\n')
6463e12c5d1SDavid du Colombier 				y++;
6473e12c5d1SDavid du Colombier 		if(y == 0){
6483e12c5d1SDavid du Colombier 	ill:
6493e12c5d1SDavid du Colombier 			mesg("ill-formed face file %s", file);
6503e12c5d1SDavid du Colombier 			close(fd);
6513e12c5d1SDavid du Colombier 			free(data);
6523e12c5d1SDavid du Colombier 			goto Err;
6533e12c5d1SDavid du Colombier 		}
6543e12c5d1SDavid du Colombier 		for(x=0,i=0; (c=data[i])!='\n'; ){
6553e12c5d1SDavid du Colombier 			if(c==',' || c==' ' || c=='\t'){
6563e12c5d1SDavid du Colombier 				i++;
6573e12c5d1SDavid du Colombier 				continue;
6583e12c5d1SDavid du Colombier 			}
6593e12c5d1SDavid du Colombier 			if(c=='0' && data[i+1] == 'x'){
6603e12c5d1SDavid du Colombier 				i += 2;
6613e12c5d1SDavid du Colombier 				continue;
6623e12c5d1SDavid du Colombier 			}
6633e12c5d1SDavid du Colombier 			if(strchr(hex, c)){
6643e12c5d1SDavid du Colombier 				x += 4;
6653e12c5d1SDavid du Colombier 				i++;
6663e12c5d1SDavid du Colombier 				continue;
6673e12c5d1SDavid du Colombier 			}
6683e12c5d1SDavid du Colombier 			goto ill;
6693e12c5d1SDavid du Colombier 		}
6703e12c5d1SDavid du Colombier 		if(x % y)
6713e12c5d1SDavid du Colombier 			goto ill;
6723e12c5d1SDavid du Colombier 		switch(x / y){
6733e12c5d1SDavid du Colombier 		default:
6743e12c5d1SDavid du Colombier 			goto ill;
6753e12c5d1SDavid du Colombier 		case 1:
6767dd7cddfSDavid du Colombier 			chan = GREY1;
6773e12c5d1SDavid du Colombier 			break;
6783e12c5d1SDavid du Colombier 		case 2:
6797dd7cddfSDavid du Colombier 			chan = GREY2;
6803e12c5d1SDavid du Colombier 			break;
6813e12c5d1SDavid du Colombier 		case 4:
6827dd7cddfSDavid du Colombier 			chan = GREY4;
6833e12c5d1SDavid du Colombier 			break;
6843e12c5d1SDavid du Colombier 		case 8:
6857dd7cddfSDavid du Colombier 			chan = CMAP8;
6863e12c5d1SDavid du Colombier 			break;
6873e12c5d1SDavid du Colombier 		}
6887dd7cddfSDavid du Colombier 		b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1);
6893e12c5d1SDavid du Colombier 		if(b == 0){
6907dd7cddfSDavid du Colombier 			mesg("image alloc failed file %s: %r", file);
6913e12c5d1SDavid du Colombier 			free(data);
6923e12c5d1SDavid du Colombier 			close(fd);
6933e12c5d1SDavid du Colombier 			goto Err;
6943e12c5d1SDavid du Colombier 		}
6953e12c5d1SDavid du Colombier 		i = 0;
6963e12c5d1SDavid du Colombier 		for(j=0; j<y; j++){
6973e12c5d1SDavid du Colombier 			for(x=0; (c=data[i])!='\n'; ){
6983e12c5d1SDavid du Colombier 				if(c=='0' && data[i+1] == 'x'){
6993e12c5d1SDavid du Colombier 					i += 2;
7003e12c5d1SDavid du Colombier 					continue;
7013e12c5d1SDavid du Colombier 				}
7023e12c5d1SDavid du Colombier 				if(strchr(hex, c)){
7037dd7cddfSDavid du Colombier 					buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
7043e12c5d1SDavid du Colombier 					i += 2;
7053e12c5d1SDavid du Colombier 					continue;
7063e12c5d1SDavid du Colombier 				}
7077dd7cddfSDavid du Colombier 				i++;
7083e12c5d1SDavid du Colombier 			}
7093e12c5d1SDavid du Colombier 			i++;
7107dd7cddfSDavid du Colombier 			loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
7113e12c5d1SDavid du Colombier 		}
7123e12c5d1SDavid du Colombier 		free(data);
7133e12c5d1SDavid du Colombier 	}else{
7147dd7cddfSDavid du Colombier 		face = NORMAL;
7153e12c5d1SDavid du Colombier 		s = 0;
7167dd7cddfSDavid du Colombier 		b = readimage(display, fd, 0);
7173e12c5d1SDavid du Colombier 		if(b == 0){
7183e12c5d1SDavid du Colombier 			mesg("can't read bitmap file %s: %r", file);
7193e12c5d1SDavid du Colombier 			close(fd);
7203e12c5d1SDavid du Colombier 			goto Err;
7213e12c5d1SDavid du Colombier 		}
7229a747e4fSDavid du Colombier 		if(seek(fd, 0, 1) < d->length)
7237dd7cddfSDavid du Colombier 			s = readsubfonti(display, file, fd, b, 0);
7243e12c5d1SDavid du Colombier 	}
7253e12c5d1SDavid du Colombier 	close(fd);
7263e12c5d1SDavid du Colombier 	t = malloc(sizeof(Thing));
7273e12c5d1SDavid du Colombier 	if(t == 0){
7283e12c5d1SDavid du Colombier    nomem:
7293e12c5d1SDavid du Colombier 		mesg("malloc failed: %r");
7303e12c5d1SDavid du Colombier 		if(s)
7317dd7cddfSDavid du Colombier 			freesubfont(s);
7327dd7cddfSDavid du Colombier 		else
7337dd7cddfSDavid du Colombier 			freeimage(b);
7343e12c5d1SDavid du Colombier 		goto Err;
7353e12c5d1SDavid du Colombier 	}
7363e12c5d1SDavid du Colombier 	t->name = strdup(file);
7373e12c5d1SDavid du Colombier 	if(t->name == 0){
7383e12c5d1SDavid du Colombier 		free(t);
7393e12c5d1SDavid du Colombier 		goto nomem;
7403e12c5d1SDavid du Colombier 	}
7413e12c5d1SDavid du Colombier 	t->b = b;
7423e12c5d1SDavid du Colombier 	t->s = s;
7433e12c5d1SDavid du Colombier 	t->face = face;
7443e12c5d1SDavid du Colombier 	t->mod = 0;
7453e12c5d1SDavid du Colombier 	t->parent = 0;
7463e12c5d1SDavid du Colombier 	t->c = -1;
7473e12c5d1SDavid du Colombier 	t->mag = 1;
7483e12c5d1SDavid du Colombier 	t->off = 0;
7493e12c5d1SDavid du Colombier 	memmove(err, oerr, sizeof err);
7503e12c5d1SDavid du Colombier 	return t;
7513e12c5d1SDavid du Colombier }
7523e12c5d1SDavid du Colombier 
7533e12c5d1SDavid du Colombier int
7543e12c5d1SDavid du Colombier atline(int x, Point p, char *line, char *buf)
7553e12c5d1SDavid du Colombier {
7563e12c5d1SDavid du Colombier 	char *s, *c, *word, *hit;
7573e12c5d1SDavid du Colombier 	int w, wasblank;
7583e12c5d1SDavid du Colombier 	Rune r;
7593e12c5d1SDavid du Colombier 
7603e12c5d1SDavid du Colombier 	wasblank = 1;
7613e12c5d1SDavid du Colombier 	hit = 0;
7623e12c5d1SDavid du Colombier 	word = 0;
7633e12c5d1SDavid du Colombier 	for(s=line; *s; s+=w){
7643e12c5d1SDavid du Colombier 		w = chartorune(&r, s);
7657dd7cddfSDavid du Colombier 		x += runestringnwidth(font, &r, 1);
7663e12c5d1SDavid du Colombier 		if(wasblank && r!=' ')
7673e12c5d1SDavid du Colombier 			word = s;
7683e12c5d1SDavid du Colombier 		wasblank = 0;
7693e12c5d1SDavid du Colombier 		if(r == ' '){
7703e12c5d1SDavid du Colombier 			if(x >= p.x)
7713e12c5d1SDavid du Colombier 				break;
7723e12c5d1SDavid du Colombier 			wasblank = 1;
7733e12c5d1SDavid du Colombier 		}
7743e12c5d1SDavid du Colombier 		if(r == ':')
7753e12c5d1SDavid du Colombier 			hit = word;
7763e12c5d1SDavid du Colombier 	}
7773e12c5d1SDavid du Colombier 	if(x < p.x)
7783e12c5d1SDavid du Colombier 		return 0;
7793e12c5d1SDavid du Colombier 	c = utfrune(hit, ':');
7803e12c5d1SDavid du Colombier 	strncpy(buf, hit, c-hit);
7813e12c5d1SDavid du Colombier 	buf[c-hit] = 0;
7823e12c5d1SDavid du Colombier 	return 1;
7833e12c5d1SDavid du Colombier }
7843e12c5d1SDavid du Colombier 
7853e12c5d1SDavid du Colombier int
7863e12c5d1SDavid du Colombier attext(Thing *t, Point p, char *buf)
7873e12c5d1SDavid du Colombier {
7883e12c5d1SDavid du Colombier 	char l0[256], l1[256];
7893e12c5d1SDavid du Colombier 
7903e12c5d1SDavid du Colombier 	if(!ptinrect(p, t->tr))
7913e12c5d1SDavid du Colombier 		return 0;
7923e12c5d1SDavid du Colombier 	stext(t, l0, l1);
7933e12c5d1SDavid du Colombier 	if(p.y < t->tr.min.y+font->height)
7943e12c5d1SDavid du Colombier 		return atline(t->r.min.x, p, l0, buf);
7953e12c5d1SDavid du Colombier 	else
7963e12c5d1SDavid du Colombier 		return atline(t->r.min.x, p, l1, buf);
7973e12c5d1SDavid du Colombier }
7983e12c5d1SDavid du Colombier 
7993e12c5d1SDavid du Colombier int
8003e12c5d1SDavid du Colombier type(char *buf, char *tag)
8013e12c5d1SDavid du Colombier {
8023e12c5d1SDavid du Colombier 	Rune r;
8033e12c5d1SDavid du Colombier 	char *p;
8043e12c5d1SDavid du Colombier 
8057dd7cddfSDavid du Colombier 	esetcursor(&busy);
8063e12c5d1SDavid du Colombier 	p = buf;
8073e12c5d1SDavid du Colombier 	for(;;){
8083e12c5d1SDavid du Colombier 		*p = 0;
8093e12c5d1SDavid du Colombier 		mesg("%s: %s", tag, buf);
8103e12c5d1SDavid du Colombier 		r = ekbd();
8113e12c5d1SDavid du Colombier 		switch(r){
8123e12c5d1SDavid du Colombier 		case '\n':
8133e12c5d1SDavid du Colombier 			mesg("");
8147dd7cddfSDavid du Colombier 			esetcursor(0);
8153e12c5d1SDavid du Colombier 			return p-buf;
8163e12c5d1SDavid du Colombier 		case 0x15:	/* control-U */
8173e12c5d1SDavid du Colombier 			p = buf;
8183e12c5d1SDavid du Colombier 			break;
8193e12c5d1SDavid du Colombier 		case '\b':
8203e12c5d1SDavid du Colombier 			if(p > buf)
8213e12c5d1SDavid du Colombier 				--p;
8223e12c5d1SDavid du Colombier 			break;
8233e12c5d1SDavid du Colombier 		default:
8243e12c5d1SDavid du Colombier 			p += runetochar(p, &r);
8253e12c5d1SDavid du Colombier 		}
8263e12c5d1SDavid du Colombier 	}
8273e12c5d1SDavid du Colombier 	return 0;	/* shut up compiler */
8283e12c5d1SDavid du Colombier }
8293e12c5d1SDavid du Colombier 
8303e12c5d1SDavid du Colombier void
8313e12c5d1SDavid du Colombier textedit(Thing *t, char *tag)
8323e12c5d1SDavid du Colombier {
8333e12c5d1SDavid du Colombier 	char buf[256];
8343e12c5d1SDavid du Colombier 	char *s;
8357dd7cddfSDavid du Colombier 	Image *b;
8363e12c5d1SDavid du Colombier 	Subfont *f;
8373e12c5d1SDavid du Colombier 	Fontchar *fc, *nfc;
8383e12c5d1SDavid du Colombier 	Rectangle r;
8397dd7cddfSDavid du Colombier 	ulong chan;
8407dd7cddfSDavid du Colombier 	int i, ld, d, w, c, doredraw, fdx, x;
8413e12c5d1SDavid du Colombier 	Thing *nt;
8423e12c5d1SDavid du Colombier 
8433e12c5d1SDavid du Colombier 	buttons(Up);
8443e12c5d1SDavid du Colombier 	if(type(buf, tag) == 0)
8453e12c5d1SDavid du Colombier 		return;
8463e12c5d1SDavid du Colombier 	if(strcmp(tag, "file") == 0){
8473e12c5d1SDavid du Colombier 		for(s=buf; *s; s++)
8483e12c5d1SDavid du Colombier 			if(*s <= ' '){
8493e12c5d1SDavid du Colombier 				mesg("illegal file name");
8503e12c5d1SDavid du Colombier 				return;
8513e12c5d1SDavid du Colombier 			}
8523e12c5d1SDavid du Colombier 		if(strcmp(t->name, buf) != 0){
8533e12c5d1SDavid du Colombier 			if(t->parent)
8543e12c5d1SDavid du Colombier 				t->parent->mod = 1;
8553e12c5d1SDavid du Colombier 			else
8563e12c5d1SDavid du Colombier 				t->mod = 1;
8573e12c5d1SDavid du Colombier 		}
8583e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
8593e12c5d1SDavid du Colombier 			if(t==nt || t->parent==nt || nt->parent==t){
8603e12c5d1SDavid du Colombier 				free(nt->name);
8613e12c5d1SDavid du Colombier 				nt->name = strdup(buf);
8623e12c5d1SDavid du Colombier 				if(nt->name == 0){
8633e12c5d1SDavid du Colombier 					mesg("malloc failed: %r");
8643e12c5d1SDavid du Colombier 					return;
8653e12c5d1SDavid du Colombier 				}
8663e12c5d1SDavid du Colombier 				text(nt);
8673e12c5d1SDavid du Colombier 			}
8683e12c5d1SDavid du Colombier 		return;
8693e12c5d1SDavid du Colombier 	}
8707dd7cddfSDavid du Colombier 	if(strcmp(tag, "depth") == 0){
8717dd7cddfSDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || log2[d]<0){
8723e12c5d1SDavid du Colombier 			mesg("illegal ldepth");
8733e12c5d1SDavid du Colombier 			return;
8743e12c5d1SDavid du Colombier 		}
8757dd7cddfSDavid du Colombier 		if(d == t->b->depth)
8763e12c5d1SDavid du Colombier 			return;
8773e12c5d1SDavid du Colombier 		if(t->parent)
8783e12c5d1SDavid du Colombier 			t->parent->mod = 1;
8793e12c5d1SDavid du Colombier 		else
8803e12c5d1SDavid du Colombier 			t->mod = 1;
8817dd7cddfSDavid du Colombier 		if(d == 8)
8827dd7cddfSDavid du Colombier 			chan = CMAP8;
8837dd7cddfSDavid du Colombier 		else
8847dd7cddfSDavid du Colombier 			chan = CHAN1(CGrey, d);
8853e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next){
8863e12c5d1SDavid du Colombier 			if(nt!=t && nt!=t->parent && nt->parent!=t)
8873e12c5d1SDavid du Colombier 				continue;
8887dd7cddfSDavid du Colombier 			b = allocimage(display, nt->b->r, chan, 0, 0);
8893e12c5d1SDavid du Colombier 			if(b == 0){
8903e12c5d1SDavid du Colombier 	nobmem:
8917dd7cddfSDavid du Colombier 				mesg("image alloc failed: %r");
8923e12c5d1SDavid du Colombier 				return;
8933e12c5d1SDavid du Colombier 			}
8947dd7cddfSDavid du Colombier 			draw(b, b->r, nt->b, nil, nt->b->r.min);
8957dd7cddfSDavid du Colombier 			freeimage(nt->b);
8963e12c5d1SDavid du Colombier 			nt->b = b;
8973e12c5d1SDavid du Colombier 			if(nt->s){
8987dd7cddfSDavid du Colombier 				b = allocimage(display, nt->b->r, chan, 0, -1);
8993e12c5d1SDavid du Colombier 				if(b == 0)
9003e12c5d1SDavid du Colombier 					goto nobmem;
9017dd7cddfSDavid du Colombier 				draw(b, b->r, nt->b, nil, nt->b->r.min);
9027dd7cddfSDavid du Colombier 				f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
9033e12c5d1SDavid du Colombier 				if(f == 0){
9043e12c5d1SDavid du Colombier 	nofmem:
9057dd7cddfSDavid du Colombier 					freeimage(b);
9063e12c5d1SDavid du Colombier 					mesg("can't make subfont: %r");
9073e12c5d1SDavid du Colombier 					return;
9083e12c5d1SDavid du Colombier 				}
9097dd7cddfSDavid du Colombier 				nt->s->info = 0;	/* prevent it being freed */
9107dd7cddfSDavid du Colombier 				nt->s->bits = 0;
9117dd7cddfSDavid du Colombier 				freesubfont(nt->s);
9123e12c5d1SDavid du Colombier 				nt->s = f;
9133e12c5d1SDavid du Colombier 			}
9147dd7cddfSDavid du Colombier 			drawthing(nt, 0);
9153e12c5d1SDavid du Colombier 		}
9163e12c5d1SDavid du Colombier 		return;
9173e12c5d1SDavid du Colombier 	}
9183e12c5d1SDavid du Colombier 	if(strcmp(tag, "mag") == 0){
9193e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
9203e12c5d1SDavid du Colombier 			mesg("illegal magnification");
9213e12c5d1SDavid du Colombier 			return;
9223e12c5d1SDavid du Colombier 		}
9233e12c5d1SDavid du Colombier 		if(t->mag == ld)
9243e12c5d1SDavid du Colombier 			return;
9253e12c5d1SDavid du Colombier 		t->mag = ld;
9263e12c5d1SDavid du Colombier 		redraw(t);
9273e12c5d1SDavid du Colombier 		return;
9283e12c5d1SDavid du Colombier 	}
9297dd7cddfSDavid du Colombier 	if(strcmp(tag, "r") == 0){
9307dd7cddfSDavid du Colombier 		if(t->s){
9317dd7cddfSDavid du Colombier 			mesg("can't change rectangle of subfont\n");
9327dd7cddfSDavid du Colombier 			return;
9337dd7cddfSDavid du Colombier 		}
9347dd7cddfSDavid du Colombier 		s = buf;
9357dd7cddfSDavid du Colombier 		r.min.x = strtoul(s, &s, 0);
9367dd7cddfSDavid du Colombier 		r.min.y = strtoul(s, &s, 0);
9377dd7cddfSDavid du Colombier 		r.max.x = strtoul(s, &s, 0);
9387dd7cddfSDavid du Colombier 		r.max.y = strtoul(s, &s, 0);
9397dd7cddfSDavid du Colombier 		if(Dx(r)<=0 || Dy(r)<=0){
9407dd7cddfSDavid du Colombier 			mesg("illegal rectangle");
9417dd7cddfSDavid du Colombier 			return;
9427dd7cddfSDavid du Colombier 		}
9437dd7cddfSDavid du Colombier 		if(t->parent)
9447dd7cddfSDavid du Colombier 			t = t->parent;
9457dd7cddfSDavid du Colombier 		for(nt=thing; nt; nt=nt->next){
9467dd7cddfSDavid du Colombier 			if(nt->parent==t && !rectinrect(nt->b->r, r))
9477dd7cddfSDavid du Colombier 				tclose1(nt);
9487dd7cddfSDavid du Colombier 		}
9497dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
9507dd7cddfSDavid du Colombier 		if(b == 0)
9517dd7cddfSDavid du Colombier 			goto nobmem;
9527dd7cddfSDavid du Colombier 		draw(b, r, t->b, nil, r.min);
9537dd7cddfSDavid du Colombier 		freeimage(t->b);
9547dd7cddfSDavid du Colombier 		t->b = b;
9557dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
9567dd7cddfSDavid du Colombier 		if(b == 0)
9577dd7cddfSDavid du Colombier 			goto nobmem;
9587dd7cddfSDavid du Colombier 		redraw(t);
9597dd7cddfSDavid du Colombier 		t->mod = 1;
9607dd7cddfSDavid du Colombier 		return;
9617dd7cddfSDavid du Colombier 	}
9623e12c5d1SDavid du Colombier 	if(strcmp(tag, "ascent") == 0){
9633e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
9643e12c5d1SDavid du Colombier 			mesg("illegal ascent");
9653e12c5d1SDavid du Colombier 			return;
9663e12c5d1SDavid du Colombier 		}
9673e12c5d1SDavid du Colombier 		if(t->s->ascent == ld)
9683e12c5d1SDavid du Colombier 			return;
9693e12c5d1SDavid du Colombier 		t->s->ascent = ld;
9703e12c5d1SDavid du Colombier 		text(t);
9713e12c5d1SDavid du Colombier 		t->mod = 1;
9723e12c5d1SDavid du Colombier 		return;
9733e12c5d1SDavid du Colombier 	}
9743e12c5d1SDavid du Colombier 	if(strcmp(tag, "height") == 0){
9753e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
9763e12c5d1SDavid du Colombier 			mesg("illegal height");
9773e12c5d1SDavid du Colombier 			return;
9783e12c5d1SDavid du Colombier 		}
9793e12c5d1SDavid du Colombier 		if(t->s->height == ld)
9803e12c5d1SDavid du Colombier 			return;
9813e12c5d1SDavid du Colombier 		t->s->height = ld;
9823e12c5d1SDavid du Colombier 		text(t);
9833e12c5d1SDavid du Colombier 		t->mod = 1;
9843e12c5d1SDavid du Colombier 		return;
9853e12c5d1SDavid du Colombier 	}
9863e12c5d1SDavid du Colombier 	if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
9873e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
9883e12c5d1SDavid du Colombier 			mesg("illegal value");
9893e12c5d1SDavid du Colombier 			return;
9903e12c5d1SDavid du Colombier 		}
9913e12c5d1SDavid du Colombier 		fc = &t->parent->s->info[t->c];
9923e12c5d1SDavid du Colombier 		if(strcmp(tag, "left")==0){
9933e12c5d1SDavid du Colombier 			if(fc->left == ld)
9943e12c5d1SDavid du Colombier 				return;
9953e12c5d1SDavid du Colombier 			fc->left = ld;
9963e12c5d1SDavid du Colombier 		}else{
9973e12c5d1SDavid du Colombier 			if(fc->width == ld)
9983e12c5d1SDavid du Colombier 				return;
9993e12c5d1SDavid du Colombier 			fc->width = ld;
10003e12c5d1SDavid du Colombier 		}
10013e12c5d1SDavid du Colombier 		text(t);
10023e12c5d1SDavid du Colombier 		t->parent->mod = 1;
10033e12c5d1SDavid du Colombier 		return;
10043e12c5d1SDavid du Colombier 	}
10053e12c5d1SDavid du Colombier 	if(strcmp(tag, "offset(hex)") == 0){
10063e12c5d1SDavid du Colombier 		if(!strchr(hex, buf[0])){
10073e12c5d1SDavid du Colombier 	illoff:
10083e12c5d1SDavid du Colombier 			mesg("illegal offset");
10093e12c5d1SDavid du Colombier 			return;
10103e12c5d1SDavid du Colombier 		}
10113e12c5d1SDavid du Colombier 		s = 0;
10123e12c5d1SDavid du Colombier 		ld = strtoul(buf, &s, 16);
10133e12c5d1SDavid du Colombier 		if(*s)
10143e12c5d1SDavid du Colombier 			goto illoff;
10153e12c5d1SDavid du Colombier 		t->off = ld;
10163e12c5d1SDavid du Colombier 		text(t);
10173e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
10183e12c5d1SDavid du Colombier 			if(nt->parent == t)
10193e12c5d1SDavid du Colombier 				text(nt);
10203e12c5d1SDavid du Colombier 		return;
10213e12c5d1SDavid du Colombier 	}
10223e12c5d1SDavid du Colombier 	if(strcmp(tag, "n") == 0){
10233e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
10243e12c5d1SDavid du Colombier 			mesg("illegal n");
10253e12c5d1SDavid du Colombier 			return;
10263e12c5d1SDavid du Colombier 		}
10273e12c5d1SDavid du Colombier 		f = t->s;
10283e12c5d1SDavid du Colombier 		if(w == f->n)
10293e12c5d1SDavid du Colombier 			return;
10303e12c5d1SDavid du Colombier 		doredraw = 0;
10313e12c5d1SDavid du Colombier 	again:
10323e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
10333e12c5d1SDavid du Colombier 			if(nt->parent == t){
10343e12c5d1SDavid du Colombier 				doredraw = 1;
10353e12c5d1SDavid du Colombier 				tclose1(nt);
10363e12c5d1SDavid du Colombier 				goto again;
10373e12c5d1SDavid du Colombier 			}
10383e12c5d1SDavid du Colombier 		r = t->b->r;
10393e12c5d1SDavid du Colombier 		if(w < f->n)
10403e12c5d1SDavid du Colombier 			r.max.x = f->info[w].x;
10417dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
10423e12c5d1SDavid du Colombier 		if(b == 0)
10433e12c5d1SDavid du Colombier 			goto nobmem;
10447dd7cddfSDavid du Colombier 		draw(b, b->r, t->b, nil, r.min);
10453e12c5d1SDavid du Colombier 		fdx = Dx(editr) - 2*Border;
10463e12c5d1SDavid du Colombier 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
10473e12c5d1SDavid du Colombier 			doredraw = 1;
10487dd7cddfSDavid du Colombier 		freeimage(t->b);
10493e12c5d1SDavid du Colombier 		t->b = b;
10507dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
10513e12c5d1SDavid du Colombier 		if(b == 0)
10523e12c5d1SDavid du Colombier 			goto nobmem;
10537dd7cddfSDavid du Colombier 		draw(b, b->r, t->b, nil, r.min);
10543e12c5d1SDavid du Colombier 		nfc = malloc((w+1)*sizeof(Fontchar));
10553e12c5d1SDavid du Colombier 		if(nfc == 0){
10563e12c5d1SDavid du Colombier 			mesg("malloc failed");
10577dd7cddfSDavid du Colombier 			freeimage(b);
10583e12c5d1SDavid du Colombier 			return;
10593e12c5d1SDavid du Colombier 		}
10603e12c5d1SDavid du Colombier 		fc = f->info;
10613e12c5d1SDavid du Colombier 		for(i=0; i<=w && i<=f->n; i++)
10623e12c5d1SDavid du Colombier 			nfc[i] = fc[i];
10633e12c5d1SDavid du Colombier 		if(w+1 < i)
10643e12c5d1SDavid du Colombier 			memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
10653e12c5d1SDavid du Colombier 		x = fc[f->n].x;
10663e12c5d1SDavid du Colombier 		for(; i<=w; i++)
10673e12c5d1SDavid du Colombier 			nfc[i].x = x;
10687dd7cddfSDavid du Colombier 		f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
10693e12c5d1SDavid du Colombier 		if(f == 0)
10703e12c5d1SDavid du Colombier 			goto nofmem;
10717dd7cddfSDavid du Colombier 		t->s->bits = nil;	/* don't free it */
10727dd7cddfSDavid du Colombier 		freesubfont(t->s);
10733e12c5d1SDavid du Colombier 		f->info = nfc;
10743e12c5d1SDavid du Colombier 		t->s = f;
10753e12c5d1SDavid du Colombier 		if(doredraw)
10763e12c5d1SDavid du Colombier 			redraw(thing);
10773e12c5d1SDavid du Colombier 		else
10787dd7cddfSDavid du Colombier 			drawthing(t, 0);
10793e12c5d1SDavid du Colombier 		t->mod = 1;
10803e12c5d1SDavid du Colombier 		return;
10813e12c5d1SDavid du Colombier 	}
10823e12c5d1SDavid du Colombier 	if(strcmp(tag, "iwidth") == 0){
10833e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
10843e12c5d1SDavid du Colombier 			mesg("illegal iwidth");
10853e12c5d1SDavid du Colombier 			return;
10863e12c5d1SDavid du Colombier 		}
10873e12c5d1SDavid du Colombier 		w -= Dx(t->b->r);
10883e12c5d1SDavid du Colombier 		if(w == 0)
10893e12c5d1SDavid du Colombier 			return;
10903e12c5d1SDavid du Colombier 		r = t->parent->b->r;
10913e12c5d1SDavid du Colombier 		r.max.x += w;
10923e12c5d1SDavid du Colombier 		c = t->c;
10933e12c5d1SDavid du Colombier 		t = t->parent;
10943e12c5d1SDavid du Colombier 		f = t->s;
10957dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
10963e12c5d1SDavid du Colombier 		if(b == 0)
10973e12c5d1SDavid du Colombier 			goto nobmem;
10983e12c5d1SDavid du Colombier 		fc = &f->info[c];
10997dd7cddfSDavid du Colombier 		draw(b, Rect(b->r.min.x, b->r.min.y,
11007dd7cddfSDavid du Colombier 				b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
11017dd7cddfSDavid du Colombier 				t->b, nil, t->b->r.min);
11027dd7cddfSDavid du Colombier 		draw(b, Rect(fc[1].x+w, b->r.min.y, w+t->b->r.max.x, b->r.min.y+Dy(t->b->r)),
11037dd7cddfSDavid du Colombier 			t->b, nil, Pt(fc[1].x, t->b->r.min.y));
11043e12c5d1SDavid du Colombier 		fdx = Dx(editr) - 2*Border;
11053e12c5d1SDavid du Colombier 		doredraw = 0;
11063e12c5d1SDavid du Colombier 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
11073e12c5d1SDavid du Colombier 			doredraw = 1;
11087dd7cddfSDavid du Colombier 		freeimage(t->b);
11093e12c5d1SDavid du Colombier 		t->b = b;
11107dd7cddfSDavid du Colombier 		b = allocimage(display, r, t->b->chan, 0, 0);
11113e12c5d1SDavid du Colombier 		if(b == 0)
11123e12c5d1SDavid du Colombier 			goto nobmem;
11137dd7cddfSDavid du Colombier 		draw(b, b->r, t->b, nil, t->b->r.min);
11143e12c5d1SDavid du Colombier 		fc = &f->info[c+1];
11153e12c5d1SDavid du Colombier 		for(i=c+1; i<=f->n; i++, fc++)
11163e12c5d1SDavid du Colombier 			fc->x += w;
11177dd7cddfSDavid du Colombier 		f = allocsubfont(t->name, f->n, f->height, f->ascent,
11187dd7cddfSDavid du Colombier 			f->info, b);
11193e12c5d1SDavid du Colombier 		if(f == 0)
11203e12c5d1SDavid du Colombier 			goto nofmem;
11213e12c5d1SDavid du Colombier 		/* t->s and f share info; free carefully */
11223e12c5d1SDavid du Colombier 		fc = f->info;
11237dd7cddfSDavid du Colombier 		t->s->bits = nil;
11243e12c5d1SDavid du Colombier 		t->s->info = 0;
11257dd7cddfSDavid du Colombier 		freesubfont(t->s);
11263e12c5d1SDavid du Colombier 		f->info = fc;
11273e12c5d1SDavid du Colombier 		t->s = f;
11283e12c5d1SDavid du Colombier 		if(doredraw)
11293e12c5d1SDavid du Colombier 			redraw(t);
11303e12c5d1SDavid du Colombier 		else
11317dd7cddfSDavid du Colombier 			drawthing(t, 0);
11323e12c5d1SDavid du Colombier 		/* redraw all affected chars */
11333e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next){
11343e12c5d1SDavid du Colombier 			if(nt->parent!=t || nt->c<c)
11353e12c5d1SDavid du Colombier 				continue;
11363e12c5d1SDavid du Colombier 			fc = &f->info[nt->c];
11373e12c5d1SDavid du Colombier 			r.min.x = fc[0].x;
11383e12c5d1SDavid du Colombier 			r.min.y = nt->b->r.min.y;
11393e12c5d1SDavid du Colombier 			r.max.x = fc[1].x;
11403e12c5d1SDavid du Colombier 			r.max.y = nt->b->r.max.y;
11417dd7cddfSDavid du Colombier 			b = allocimage(display, r, nt->b->chan, 0, 0);
11423e12c5d1SDavid du Colombier 			if(b == 0)
11433e12c5d1SDavid du Colombier 				goto nobmem;
11447dd7cddfSDavid du Colombier 			draw(b, r, t->b, nil, r.min);
11453e12c5d1SDavid du Colombier 			doredraw = 0;
11463e12c5d1SDavid du Colombier 			if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
11473e12c5d1SDavid du Colombier 				doredraw = 1;
11487dd7cddfSDavid du Colombier 			freeimage(nt->b);
11493e12c5d1SDavid du Colombier 			nt->b = b;
11503e12c5d1SDavid du Colombier 			if(c != nt->c)
11513e12c5d1SDavid du Colombier 				text(nt);
11523e12c5d1SDavid du Colombier 			else{
11533e12c5d1SDavid du Colombier 				if(doredraw)
11543e12c5d1SDavid du Colombier 					redraw(nt);
11553e12c5d1SDavid du Colombier 				else
11567dd7cddfSDavid du Colombier 					drawthing(nt, 0);
11573e12c5d1SDavid du Colombier 			}
11583e12c5d1SDavid du Colombier 		}
11593e12c5d1SDavid du Colombier 		t->mod = 1;
11603e12c5d1SDavid du Colombier 		return;
11613e12c5d1SDavid du Colombier 	}
11623e12c5d1SDavid du Colombier 	mesg("cannot edit %s in file %s", tag, t->name);
11633e12c5d1SDavid du Colombier }
11643e12c5d1SDavid du Colombier 
11653e12c5d1SDavid du Colombier void
11663e12c5d1SDavid du Colombier cntledit(char *tag)
11673e12c5d1SDavid du Colombier {
11683e12c5d1SDavid du Colombier 	char buf[256];
116922a127bbSDavid du Colombier 	long l;
11703e12c5d1SDavid du Colombier 
11713e12c5d1SDavid du Colombier 	buttons(Up);
11723e12c5d1SDavid du Colombier 	if(type(buf, tag) == 0)
11733e12c5d1SDavid du Colombier 		return;
11743e12c5d1SDavid du Colombier 	if(strcmp(tag, "mag") == 0){
11753e12c5d1SDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
11763e12c5d1SDavid du Colombier 			mesg("illegal magnification");
11773e12c5d1SDavid du Colombier 			return;
11783e12c5d1SDavid du Colombier 		}
11793e12c5d1SDavid du Colombier 		mag = l;
11803e12c5d1SDavid du Colombier 		cntl();
11813e12c5d1SDavid du Colombier 		return;
11823e12c5d1SDavid du Colombier 	}
11833e12c5d1SDavid du Colombier 	if(strcmp(tag, "but1")==0
11847dd7cddfSDavid du Colombier 	|| strcmp(tag, "but2")==0){
11857dd7cddfSDavid du Colombier 		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<0 || l>255){
11867dd7cddfSDavid du Colombier 			mesg("illegal value");
11873e12c5d1SDavid du Colombier 			return;
11883e12c5d1SDavid du Colombier 		}
11893e12c5d1SDavid du Colombier 		if(strcmp(tag, "but1") == 0)
11907dd7cddfSDavid du Colombier 			but1val = l;
11913e12c5d1SDavid du Colombier 		else if(strcmp(tag, "but2") == 0)
11927dd7cddfSDavid du Colombier 			but2val = l;
11937dd7cddfSDavid du Colombier 		cntl();
11947dd7cddfSDavid du Colombier 		return;
11957dd7cddfSDavid du Colombier 	}
11967dd7cddfSDavid du Colombier 	if(strcmp(tag, "invert-on-copy")==0){
11977dd7cddfSDavid du Colombier 		if(buf[0]=='y' || buf[0]=='1')
11987dd7cddfSDavid du Colombier 			invert = 1;
11997dd7cddfSDavid du Colombier 		else if(buf[0]=='n' || buf[0]=='0')
12007dd7cddfSDavid du Colombier 			invert = 0;
12017dd7cddfSDavid du Colombier 		else{
12027dd7cddfSDavid du Colombier 			mesg("illegal value");
12037dd7cddfSDavid du Colombier 			return;
12047dd7cddfSDavid du Colombier 		}
12053e12c5d1SDavid du Colombier 		cntl();
12063e12c5d1SDavid du Colombier 		return;
12073e12c5d1SDavid du Colombier 	}
12083e12c5d1SDavid du Colombier 	mesg("cannot edit %s", tag);
12093e12c5d1SDavid du Colombier }
12103e12c5d1SDavid du Colombier 
12113e12c5d1SDavid du Colombier void
12123e12c5d1SDavid du Colombier buttons(int ud)
12133e12c5d1SDavid du Colombier {
12143e12c5d1SDavid du Colombier 	while((mouse.buttons==0) != ud)
12153e12c5d1SDavid du Colombier 		mouse = emouse();
12163e12c5d1SDavid du Colombier }
12173e12c5d1SDavid du Colombier 
12187dd7cddfSDavid du Colombier Point
12197dd7cddfSDavid du Colombier screenpt(Thing *t, Point realp)
12203e12c5d1SDavid du Colombier {
12217dd7cddfSDavid du Colombier 	int fdx, n;
12223e12c5d1SDavid du Colombier 	Point p;
12233e12c5d1SDavid du Colombier 
12247dd7cddfSDavid du Colombier 	fdx = Dx(editr)-2*Border;
12257dd7cddfSDavid du Colombier 	if(t->mag > 1)
12267dd7cddfSDavid du Colombier 		fdx -= fdx%t->mag;
12277dd7cddfSDavid du Colombier 	p = mulpt(subpt(realp, t->b->r.min), t->mag);
12287dd7cddfSDavid du Colombier 	if(fdx < Dx(t->b->r)*t->mag){
12297dd7cddfSDavid du Colombier 		n = p.x/fdx;
12307dd7cddfSDavid du Colombier 		p.y += n * (Dy(t->b->r)*t->mag+Border);
12317dd7cddfSDavid du Colombier 		p.x -= n * fdx;
12327dd7cddfSDavid du Colombier 	}
12337dd7cddfSDavid du Colombier 	p = addpt(p, t->r.min);
12347dd7cddfSDavid du Colombier 	return p;
12357dd7cddfSDavid du Colombier }
12367dd7cddfSDavid du Colombier 
12377dd7cddfSDavid du Colombier Point
12387dd7cddfSDavid du Colombier realpt(Thing *t, Point screenp)
12397dd7cddfSDavid du Colombier {
12407dd7cddfSDavid du Colombier 	int fdx, n, dy;
12417dd7cddfSDavid du Colombier 	Point p;
12427dd7cddfSDavid du Colombier 
12437dd7cddfSDavid du Colombier 	fdx = (Dx(editr)-2*Border);
12447dd7cddfSDavid du Colombier 	if(t->mag > 1)
12457dd7cddfSDavid du Colombier 		fdx -= fdx%t->mag;
12467dd7cddfSDavid du Colombier 	p.y = screenp.y-t->r.min.y;
12477dd7cddfSDavid du Colombier 	p.x = 0;
12487dd7cddfSDavid du Colombier 	if(fdx < Dx(t->b->r)*t->mag){
12497dd7cddfSDavid du Colombier 		dy = Dy(t->b->r)*t->mag+Border;
12507dd7cddfSDavid du Colombier 		n = (p.y/dy);
12517dd7cddfSDavid du Colombier 		p.x = n * fdx;
12527dd7cddfSDavid du Colombier 		p.y -= n * dy;
12537dd7cddfSDavid du Colombier 	}
12547dd7cddfSDavid du Colombier 	p.x += screenp.x-t->r.min.x;
12557dd7cddfSDavid du Colombier 	p = addpt(divpt(p, t->mag), t->b->r.min);
12567dd7cddfSDavid du Colombier 	return p;
12577dd7cddfSDavid du Colombier }
12587dd7cddfSDavid du Colombier 
12597dd7cddfSDavid du Colombier int
12607dd7cddfSDavid du Colombier sweep(int but, Rectangle *r)
12617dd7cddfSDavid du Colombier {
12627dd7cddfSDavid du Colombier 	Thing *t;
12637dd7cddfSDavid du Colombier 	Point p, q, lastq;
12647dd7cddfSDavid du Colombier 
12657dd7cddfSDavid du Colombier 	esetcursor(&sweep0);
12663e12c5d1SDavid du Colombier 	buttons(Down);
12677dd7cddfSDavid du Colombier 	if(mouse.buttons != (1<<(but-1))){
12683e12c5d1SDavid du Colombier 		buttons(Up);
12697dd7cddfSDavid du Colombier 		esetcursor(0);
12703e12c5d1SDavid du Colombier 		return 0;
12713e12c5d1SDavid du Colombier 	}
12723e12c5d1SDavid du Colombier 	p = mouse.xy;
12737dd7cddfSDavid du Colombier 	for(t=thing; t; t=t->next)
12747dd7cddfSDavid du Colombier 		if(ptinrect(p, t->r))
12757dd7cddfSDavid du Colombier 			break;
12767dd7cddfSDavid du Colombier 	if(t)
12777dd7cddfSDavid du Colombier 		p = screenpt(t, realpt(t, p));
12783e12c5d1SDavid du Colombier 	r->min = p;
12793e12c5d1SDavid du Colombier 	r->max = p;
12807dd7cddfSDavid du Colombier 	esetcursor(&box);
12817dd7cddfSDavid du Colombier 	lastq = ZP;
12827dd7cddfSDavid du Colombier 	while(mouse.buttons == (1<<(but-1))){
12837dd7cddfSDavid du Colombier 		edrawgetrect(insetrect(*r, -Borderwidth), 1);
12843e12c5d1SDavid du Colombier 		mouse = emouse();
12857dd7cddfSDavid du Colombier 		edrawgetrect(insetrect(*r, -Borderwidth), 0);
12867dd7cddfSDavid du Colombier 		q = mouse.xy;
12877dd7cddfSDavid du Colombier 		if(t)
12887dd7cddfSDavid du Colombier 			q = screenpt(t, realpt(t, q));
12897dd7cddfSDavid du Colombier 		if(eqpt(q, lastq))
12907dd7cddfSDavid du Colombier 			continue;
12917dd7cddfSDavid du Colombier 		*r = canonrect(Rpt(p, q));
12927dd7cddfSDavid du Colombier 		lastq = q;
12933e12c5d1SDavid du Colombier 	}
12947dd7cddfSDavid du Colombier 	esetcursor(0);
12953e12c5d1SDavid du Colombier 	if(mouse.buttons){
12963e12c5d1SDavid du Colombier 		buttons(Up);
12973e12c5d1SDavid du Colombier 		return 0;
12983e12c5d1SDavid du Colombier 	}
12993e12c5d1SDavid du Colombier 	return 1;
13003e12c5d1SDavid du Colombier }
13013e12c5d1SDavid du Colombier 
13023e12c5d1SDavid du Colombier void
13033e12c5d1SDavid du Colombier openedit(Thing *t, Point pt, int c)
13043e12c5d1SDavid du Colombier {
13053e12c5d1SDavid du Colombier 	int x, y;
13063e12c5d1SDavid du Colombier 	Point p;
13073e12c5d1SDavid du Colombier 	Rectangle r;
13083e12c5d1SDavid du Colombier 	Rectangle br;
13093e12c5d1SDavid du Colombier 	Fontchar *fc;
13103e12c5d1SDavid du Colombier 	Thing *nt;
13113e12c5d1SDavid du Colombier 
13127dd7cddfSDavid du Colombier 	if(t->b->depth > 8){
13137dd7cddfSDavid du Colombier 		mesg("image has depth %d; can't handle >8", t->b->depth);
13147dd7cddfSDavid du Colombier 		return;
13157dd7cddfSDavid du Colombier 	}
13163e12c5d1SDavid du Colombier 	br = t->b->r;
13173e12c5d1SDavid du Colombier 	if(t->s == 0){
13183e12c5d1SDavid du Colombier 		c = -1;
13193e12c5d1SDavid du Colombier 		/* if big enough to bother, sweep box */
13203e12c5d1SDavid du Colombier 		if(Dx(br)<=16 && Dy(br)<=16)
13213e12c5d1SDavid du Colombier 			r = br;
13223e12c5d1SDavid du Colombier 		else{
13233e12c5d1SDavid du Colombier 			if(!sweep(1, &r))
13243e12c5d1SDavid du Colombier 				return;
13257dd7cddfSDavid du Colombier 			r = rectaddpt(r, subpt(br.min, t->r.min));
13263e12c5d1SDavid du Colombier 			if(!rectclip(&r, br))
13273e12c5d1SDavid du Colombier 				return;
13283e12c5d1SDavid du Colombier 			if(Dx(br) <= 8){
13293e12c5d1SDavid du Colombier 				r.min.x = br.min.x;
13303e12c5d1SDavid du Colombier 				r.max.x = br.max.x;
13313e12c5d1SDavid du Colombier 			}else if(Dx(r) < 4){
13323e12c5d1SDavid du Colombier 	    toosmall:
13333e12c5d1SDavid du Colombier 				mesg("rectangle too small");
13343e12c5d1SDavid du Colombier 				return;
13353e12c5d1SDavid du Colombier 			}
13363e12c5d1SDavid du Colombier 			if(Dy(br) <= 8){
13373e12c5d1SDavid du Colombier 				r.min.y = br.min.y;
13383e12c5d1SDavid du Colombier 				r.max.y = br.max.y;
13393e12c5d1SDavid du Colombier 			}else if(Dy(r) < 4)
13403e12c5d1SDavid du Colombier 				goto toosmall;
13413e12c5d1SDavid du Colombier 		}
13423e12c5d1SDavid du Colombier 	}else if(c >= 0){
13433e12c5d1SDavid du Colombier 		fc = &t->s->info[c];
13443e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
13453e12c5d1SDavid du Colombier 		r.min.y = br.min.y;
13463e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
13473e12c5d1SDavid du Colombier 		r.max.y = br.min.y + Dy(br);
13483e12c5d1SDavid du Colombier 	}else{
13493e12c5d1SDavid du Colombier 		/* just point at character */
13503e12c5d1SDavid du Colombier 		fc = t->s->info;
13517dd7cddfSDavid du Colombier 		p = addpt(pt, subpt(br.min, t->r.min));
13523e12c5d1SDavid du Colombier 		x = br.min.x;
13533e12c5d1SDavid du Colombier 		y = br.min.y;
13543e12c5d1SDavid du Colombier 		for(c=0; c<t->s->n; c++,fc++){
13553e12c5d1SDavid du Colombier 	    again:
13563e12c5d1SDavid du Colombier 			r.min.x = x;
13573e12c5d1SDavid du Colombier 			r.min.y = y;
13583e12c5d1SDavid du Colombier 			r.max.x = x + fc[1].x - fc[0].x;
13593e12c5d1SDavid du Colombier 			r.max.y = y + Dy(br);
13603e12c5d1SDavid du Colombier 			if(ptinrect(p, r))
13613e12c5d1SDavid du Colombier 				goto found;
13623e12c5d1SDavid du Colombier 			if(r.max.x >= br.min.x+Dx(t->r)){
13633e12c5d1SDavid du Colombier 				x -= Dx(t->r);
13643e12c5d1SDavid du Colombier 				y += t->s->height;
13653e12c5d1SDavid du Colombier 				if(fc[1].x > fc[0].x)
13663e12c5d1SDavid du Colombier 					goto again;
13673e12c5d1SDavid du Colombier 			}
13683e12c5d1SDavid du Colombier 			x += fc[1].x - fc[0].x;
13693e12c5d1SDavid du Colombier 		}
13703e12c5d1SDavid du Colombier 		return;
13713e12c5d1SDavid du Colombier 	   found:
13723e12c5d1SDavid du Colombier 		r = br;
13733e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
13743e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
13753e12c5d1SDavid du Colombier 	}
13763e12c5d1SDavid du Colombier 	nt = malloc(sizeof(Thing));
13773e12c5d1SDavid du Colombier 	if(nt == 0){
13783e12c5d1SDavid du Colombier    nomem:
13793e12c5d1SDavid du Colombier 		mesg("can't allocate: %r");
13803e12c5d1SDavid du Colombier 		return;
13813e12c5d1SDavid du Colombier 	}
13823e12c5d1SDavid du Colombier 	memset(nt, 0, sizeof(Thing));
13833e12c5d1SDavid du Colombier 	nt->c = c;
13847dd7cddfSDavid du Colombier 	nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
13853e12c5d1SDavid du Colombier 	if(nt->b == 0){
13863e12c5d1SDavid du Colombier 		free(nt);
13873e12c5d1SDavid du Colombier 		goto nomem;
13883e12c5d1SDavid du Colombier 	}
13897dd7cddfSDavid du Colombier 	draw(nt->b, r, t->b, nil, r.min);
13903e12c5d1SDavid du Colombier 	nt->name = strdup(t->name);
13913e12c5d1SDavid du Colombier 	if(nt->name == 0){
13927dd7cddfSDavid du Colombier 		freeimage(nt->b);
13933e12c5d1SDavid du Colombier 		free(nt);
13943e12c5d1SDavid du Colombier 		goto nomem;
13953e12c5d1SDavid du Colombier 	}
13963e12c5d1SDavid du Colombier 	nt->parent = t;
13973e12c5d1SDavid du Colombier 	nt->mag = mag;
13987dd7cddfSDavid du Colombier 	drawthing(nt, 1);
13993e12c5d1SDavid du Colombier }
14003e12c5d1SDavid du Colombier 
14013e12c5d1SDavid du Colombier void
14023e12c5d1SDavid du Colombier ckinfo(Thing *t, Rectangle mod)
14033e12c5d1SDavid du Colombier {
14043e12c5d1SDavid du Colombier 	int i, j, k, top, bot, n, zero;
14053e12c5d1SDavid du Colombier 	Fontchar *fc;
14063e12c5d1SDavid du Colombier 	Rectangle r;
14077dd7cddfSDavid du Colombier 	Image *b;
14083e12c5d1SDavid du Colombier 	Thing *nt;
14093e12c5d1SDavid du Colombier 
14103e12c5d1SDavid du Colombier 	if(t->parent)
14113e12c5d1SDavid du Colombier 		t = t->parent;
14123e12c5d1SDavid du Colombier 	if(t->s==0 || Dy(t->b->r)==0)
14133e12c5d1SDavid du Colombier 		return;
14143e12c5d1SDavid du Colombier 	b = 0;
14153e12c5d1SDavid du Colombier 	/* check bounding boxes */
14163e12c5d1SDavid du Colombier 	fc = &t->s->info[0];
14173e12c5d1SDavid du Colombier 	r.min.y = t->b->r.min.y;
14183e12c5d1SDavid du Colombier 	r.max.y = t->b->r.max.y;
14193e12c5d1SDavid du Colombier 	for(i=0; i<t->s->n; i++, fc++){
14203e12c5d1SDavid du Colombier 		r.min.x = fc[0].x;
14213e12c5d1SDavid du Colombier 		r.max.x = fc[1].x;
14223e12c5d1SDavid du Colombier 		if(!rectXrect(mod, r))
14233e12c5d1SDavid du Colombier 			continue;
14243e12c5d1SDavid du Colombier 		if(b==0 || Dx(b->r)<Dx(r)){
14253e12c5d1SDavid du Colombier 			if(b)
14267dd7cddfSDavid du Colombier 				freeimage(b);
14277dd7cddfSDavid du Colombier 			b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
14283e12c5d1SDavid du Colombier 			if(b == 0){
14297dd7cddfSDavid du Colombier 				mesg("can't alloc image");
14303e12c5d1SDavid du Colombier 				break;
14313e12c5d1SDavid du Colombier 			}
14323e12c5d1SDavid du Colombier 		}
14337dd7cddfSDavid du Colombier 		draw(b, b->r, display->white, nil, ZP);
14347dd7cddfSDavid du Colombier 		draw(b, b->r, t->b, nil, r.min);
14353e12c5d1SDavid du Colombier 		top = 100000;
14363e12c5d1SDavid du Colombier 		bot = 0;
14377dd7cddfSDavid du Colombier 		n = 2+((Dx(r)/8)*t->b->depth);
14383e12c5d1SDavid du Colombier 		for(j=0; j<b->r.max.y; j++){
14393e12c5d1SDavid du Colombier 			memset(data, 0, n);
14407dd7cddfSDavid du Colombier 			unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
14413e12c5d1SDavid du Colombier 			zero = 1;
14423e12c5d1SDavid du Colombier 			for(k=0; k<n; k++)
14433e12c5d1SDavid du Colombier 				if(data[k]){
14443e12c5d1SDavid du Colombier 					zero = 0;
14453e12c5d1SDavid du Colombier 					break;
14463e12c5d1SDavid du Colombier 				}
14473e12c5d1SDavid du Colombier 			if(!zero){
14483e12c5d1SDavid du Colombier 				if(top > j)
14493e12c5d1SDavid du Colombier 					top = j;
14503e12c5d1SDavid du Colombier 				bot = j+1;
14513e12c5d1SDavid du Colombier 			}
14523e12c5d1SDavid du Colombier 		}
14533e12c5d1SDavid du Colombier 		if(top > j)
14543e12c5d1SDavid du Colombier 			top = 0;
14553e12c5d1SDavid du Colombier 		if(top!=fc->top || bot!=fc->bottom){
14563e12c5d1SDavid du Colombier 			fc->top = top;
14573e12c5d1SDavid du Colombier 			fc->bottom = bot;
14583e12c5d1SDavid du Colombier 			for(nt=thing; nt; nt=nt->next)
14593e12c5d1SDavid du Colombier 				if(nt->parent==t && nt->c==i)
14603e12c5d1SDavid du Colombier 					text(nt);
14613e12c5d1SDavid du Colombier 		}
14623e12c5d1SDavid du Colombier 	}
14633e12c5d1SDavid du Colombier 	if(b)
14647dd7cddfSDavid du Colombier 		freeimage(b);
14653e12c5d1SDavid du Colombier }
14663e12c5d1SDavid du Colombier 
14673e12c5d1SDavid du Colombier void
14683e12c5d1SDavid du Colombier twidpix(Thing *t, Point p, int set)
14693e12c5d1SDavid du Colombier {
14707dd7cddfSDavid du Colombier 	Image *b, *v;
14717dd7cddfSDavid du Colombier 	int c;
14723e12c5d1SDavid du Colombier 
14733e12c5d1SDavid du Colombier 	b = t->b;
14743e12c5d1SDavid du Colombier 	if(!ptinrect(p, b->r))
14753e12c5d1SDavid du Colombier 		return;
14767dd7cddfSDavid du Colombier 	if(set)
14777dd7cddfSDavid du Colombier 		c = but1val;
14787dd7cddfSDavid du Colombier 	else
14797dd7cddfSDavid du Colombier 		c = but2val;
14807dd7cddfSDavid du Colombier 	if(b->chan == GREY8)
14817dd7cddfSDavid du Colombier 		v = greyvalues[c];
14827dd7cddfSDavid du Colombier 	else
14837dd7cddfSDavid du Colombier 		v = values[c];
14847dd7cddfSDavid du Colombier 	draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
14853e12c5d1SDavid du Colombier 	p = screenpt(t, p);
14867dd7cddfSDavid du Colombier 	draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
14873e12c5d1SDavid du Colombier }
14883e12c5d1SDavid du Colombier 
14893e12c5d1SDavid du Colombier void
14903e12c5d1SDavid du Colombier twiddle(Thing *t)
14913e12c5d1SDavid du Colombier {
14923e12c5d1SDavid du Colombier 	int set;
14933e12c5d1SDavid du Colombier 	Point p, lastp;
14947dd7cddfSDavid du Colombier 	Image *b;
14953e12c5d1SDavid du Colombier 	Thing *nt;
14963e12c5d1SDavid du Colombier 	Rectangle mod;
14973e12c5d1SDavid du Colombier 
14983e12c5d1SDavid du Colombier 	if(mouse.buttons!=1 && mouse.buttons!=2){
14993e12c5d1SDavid du Colombier 		buttons(Up);
15003e12c5d1SDavid du Colombier 		return;
15013e12c5d1SDavid du Colombier 	}
15023e12c5d1SDavid du Colombier 	set = mouse.buttons==1;
15033e12c5d1SDavid du Colombier 	b = t->b;
15047dd7cddfSDavid du Colombier 	lastp = addpt(b->r.min, Pt(-1, -1));
15057dd7cddfSDavid du Colombier 	mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
15063e12c5d1SDavid du Colombier 	while(mouse.buttons){
15073e12c5d1SDavid du Colombier 		p = realpt(t, mouse.xy);
15083e12c5d1SDavid du Colombier 		if(!eqpt(p, lastp)){
15093e12c5d1SDavid du Colombier 			lastp = p;
15103e12c5d1SDavid du Colombier 			if(ptinrect(p, b->r)){
15113e12c5d1SDavid du Colombier 				for(nt=thing; nt; nt=nt->next)
15123e12c5d1SDavid du Colombier 					if(nt->parent==t->parent || nt==t->parent)
15133e12c5d1SDavid du Colombier 						twidpix(nt, p, set);
15143e12c5d1SDavid du Colombier 				if(t->parent)
15153e12c5d1SDavid du Colombier 					t->parent->mod = 1;
15163e12c5d1SDavid du Colombier 				else
15173e12c5d1SDavid du Colombier 					t->mod = 1;
15183e12c5d1SDavid du Colombier 				if(p.x < mod.min.x)
15193e12c5d1SDavid du Colombier 					mod.min.x = p.x;
15203e12c5d1SDavid du Colombier 				if(p.y < mod.min.y)
15213e12c5d1SDavid du Colombier 					mod.min.y = p.y;
15223e12c5d1SDavid du Colombier 				if(p.x >= mod.max.x)
15233e12c5d1SDavid du Colombier 					mod.max.x = p.x+1;
15243e12c5d1SDavid du Colombier 				if(p.y >= mod.max.y)
15253e12c5d1SDavid du Colombier 					mod.max.y = p.y+1;
15263e12c5d1SDavid du Colombier 			}
15273e12c5d1SDavid du Colombier 		}
15283e12c5d1SDavid du Colombier 		mouse = emouse();
15293e12c5d1SDavid du Colombier 	}
15303e12c5d1SDavid du Colombier 	ckinfo(t, mod);
15313e12c5d1SDavid du Colombier }
15323e12c5d1SDavid du Colombier 
15333e12c5d1SDavid du Colombier void
15343e12c5d1SDavid du Colombier select(void)
15353e12c5d1SDavid du Colombier {
15363e12c5d1SDavid du Colombier 	Thing *t;
15373e12c5d1SDavid du Colombier 	char line[128], buf[128];
15383e12c5d1SDavid du Colombier 	Point p;
15393e12c5d1SDavid du Colombier 
15403e12c5d1SDavid du Colombier 	if(ptinrect(mouse.xy, cntlr)){
15413e12c5d1SDavid du Colombier 		scntl(line);
15423e12c5d1SDavid du Colombier 		if(atline(cntlr.min.x, mouse.xy, line, buf)){
15433e12c5d1SDavid du Colombier 			if(mouse.buttons == 1)
15443e12c5d1SDavid du Colombier 				cntledit(buf);
15453e12c5d1SDavid du Colombier 			else
15463e12c5d1SDavid du Colombier 				buttons(Up);
15473e12c5d1SDavid du Colombier 			return;
15483e12c5d1SDavid du Colombier 		}
15493e12c5d1SDavid du Colombier 		return;
15503e12c5d1SDavid du Colombier 	}
15513e12c5d1SDavid du Colombier 	for(t=thing; t; t=t->next){
15523e12c5d1SDavid du Colombier 		if(attext(t, mouse.xy, buf)){
15533e12c5d1SDavid du Colombier 			if(mouse.buttons == 1)
15543e12c5d1SDavid du Colombier 				textedit(t, buf);
15553e12c5d1SDavid du Colombier 			else
15563e12c5d1SDavid du Colombier 				buttons(Up);
15573e12c5d1SDavid du Colombier 			return;
15583e12c5d1SDavid du Colombier 		}
15593e12c5d1SDavid du Colombier 		if(ptinrect(mouse.xy, t->r)){
15603e12c5d1SDavid du Colombier 			if(t->parent == 0){
15613e12c5d1SDavid du Colombier 				if(mouse.buttons == 1){
15623e12c5d1SDavid du Colombier 					p = mouse.xy;
15633e12c5d1SDavid du Colombier 					buttons(Up);
15643e12c5d1SDavid du Colombier 					openedit(t, p, -1);
15653e12c5d1SDavid du Colombier 				}else
15663e12c5d1SDavid du Colombier 					buttons(Up);
15673e12c5d1SDavid du Colombier 				return;
15683e12c5d1SDavid du Colombier 			}
15693e12c5d1SDavid du Colombier 			twiddle(t);
15703e12c5d1SDavid du Colombier 			return;
15713e12c5d1SDavid du Colombier 		}
15723e12c5d1SDavid du Colombier 	}
15733e12c5d1SDavid du Colombier }
15743e12c5d1SDavid du Colombier 
15753e12c5d1SDavid du Colombier void
15763e12c5d1SDavid du Colombier twrite(Thing *t)
15773e12c5d1SDavid du Colombier {
15783e12c5d1SDavid du Colombier 	int i, j, x, y, fd, ws, ld;
15793e12c5d1SDavid du Colombier 	Biobuf buf;
15803e12c5d1SDavid du Colombier 	Rectangle r;
15813e12c5d1SDavid du Colombier 
15823e12c5d1SDavid du Colombier 	if(t->parent)
15833e12c5d1SDavid du Colombier 		t = t->parent;
15847dd7cddfSDavid du Colombier 	esetcursor(&busy);
15853e12c5d1SDavid du Colombier 	fd = create(t->name, OWRITE, 0666);
15863e12c5d1SDavid du Colombier 	if(fd < 0){
15873e12c5d1SDavid du Colombier 		mesg("can't write %s: %r", t->name);
15883e12c5d1SDavid du Colombier 		return;
15893e12c5d1SDavid du Colombier 	}
15909a747e4fSDavid du Colombier 	if(t->face && t->b->depth <= 4){
15913e12c5d1SDavid du Colombier 		r = t->b->r;
15927dd7cddfSDavid du Colombier 		ld = log2[t->b->depth];
15933e12c5d1SDavid du Colombier 		/* This heuristic reflects peculiarly different formats */
15943e12c5d1SDavid du Colombier 		ws = 4;
15957dd7cddfSDavid du Colombier 		if(t->face == 2)	/* cursor file */
15963e12c5d1SDavid du Colombier 			ws = 1;
15973e12c5d1SDavid du Colombier 		else if(Dx(r)<32 || ld==0)
15983e12c5d1SDavid du Colombier 			ws = 2;
15993e12c5d1SDavid du Colombier 		Binit(&buf, fd, OWRITE);
16007dd7cddfSDavid du Colombier 		if(t->face == CURSOR)
16017dd7cddfSDavid du Colombier 			Bprint(&buf, "{");
16023e12c5d1SDavid du Colombier 		for(y=r.min.y; y<r.max.y; y++){
16037dd7cddfSDavid du Colombier 			unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
16043e12c5d1SDavid du Colombier 			j = 0;
16053e12c5d1SDavid du Colombier 			for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
16063e12c5d1SDavid du Colombier 				Bprint(&buf, "0x");
16073e12c5d1SDavid du Colombier 				for(i=0; i<ws; i++)
16083e12c5d1SDavid du Colombier 					Bprint(&buf, "%.2x", data[i+j]);
16093e12c5d1SDavid du Colombier 				Bprint(&buf, ", ");
16103e12c5d1SDavid du Colombier 			}
16117dd7cddfSDavid du Colombier 			if(t->face == CURSOR){
16127dd7cddfSDavid du Colombier 				switch(y){
16137dd7cddfSDavid du Colombier 				case 3: case 7: case 11: case 19: case 23: case 27:
16147dd7cddfSDavid du Colombier 					Bprint(&buf, "\n ");
16157dd7cddfSDavid du Colombier 					break;
16167dd7cddfSDavid du Colombier 				case 15:
16177dd7cddfSDavid du Colombier 					Bprint(&buf, "},\n{");
16187dd7cddfSDavid du Colombier 					break;
16197dd7cddfSDavid du Colombier 				case 31:
16207dd7cddfSDavid du Colombier 					Bprint(&buf, "}\n");
16217dd7cddfSDavid du Colombier 					break;
16227dd7cddfSDavid du Colombier 				}
16237dd7cddfSDavid du Colombier 			}else
16243e12c5d1SDavid du Colombier 				Bprint(&buf, "\n");
16253e12c5d1SDavid du Colombier 		}
1626219b2ee8SDavid du Colombier 		Bterm(&buf);
16277dd7cddfSDavid du Colombier 	}else
16287dd7cddfSDavid du Colombier 		if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
16297dd7cddfSDavid du Colombier 			close(fd);
16307dd7cddfSDavid du Colombier 			mesg("can't write %s: %r", t->name);
16313e12c5d1SDavid du Colombier 		}
16323e12c5d1SDavid du Colombier 	t->mod = 0;
16333e12c5d1SDavid du Colombier 	close(fd);
16343e12c5d1SDavid du Colombier 	mesg("wrote %s", t->name);
16353e12c5d1SDavid du Colombier }
16363e12c5d1SDavid du Colombier 
16373e12c5d1SDavid du Colombier void
16387dd7cddfSDavid du Colombier tpixels(void)
16397dd7cddfSDavid du Colombier {
16407dd7cddfSDavid du Colombier 	Thing *t;
16417dd7cddfSDavid du Colombier 	Point p, lastp;
16427dd7cddfSDavid du Colombier 
16437dd7cddfSDavid du Colombier 	esetcursor(&pixel);
16447dd7cddfSDavid du Colombier 	for(;;){
16457dd7cddfSDavid du Colombier 		buttons(Down);
16467dd7cddfSDavid du Colombier 		if(mouse.buttons != 4)
16477dd7cddfSDavid du Colombier 			break;
16487dd7cddfSDavid du Colombier 		for(t=thing; t; t=t->next){
16497dd7cddfSDavid du Colombier 			lastp = Pt(-1, -1);
16507dd7cddfSDavid du Colombier 			if(ptinrect(mouse.xy, t->r)){
16517dd7cddfSDavid du Colombier 				while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
16527dd7cddfSDavid du Colombier 					p = realpt(t, mouse.xy);
16537dd7cddfSDavid du Colombier 					if(!eqpt(p, lastp)){
16547dd7cddfSDavid du Colombier 						if(p.y != lastp.y)
16557dd7cddfSDavid du Colombier 							unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
16567dd7cddfSDavid du Colombier 						mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
16577dd7cddfSDavid du Colombier 						lastp = p;
16587dd7cddfSDavid du Colombier 					}
16597dd7cddfSDavid du Colombier 					mouse = emouse();
16607dd7cddfSDavid du Colombier 				}
16617dd7cddfSDavid du Colombier 				goto Continue;
16627dd7cddfSDavid du Colombier 			}
16637dd7cddfSDavid du Colombier 		}
16647dd7cddfSDavid du Colombier 		mouse = emouse();
16657dd7cddfSDavid du Colombier     Continue:;
16667dd7cddfSDavid du Colombier 	}
16677dd7cddfSDavid du Colombier 	buttons(Up);
16687dd7cddfSDavid du Colombier 	esetcursor(0);
16697dd7cddfSDavid du Colombier }
16707dd7cddfSDavid du Colombier 
16717dd7cddfSDavid du Colombier void
16723e12c5d1SDavid du Colombier tclose1(Thing *t)
16733e12c5d1SDavid du Colombier {
16743e12c5d1SDavid du Colombier 	Thing *nt;
16753e12c5d1SDavid du Colombier 
16763e12c5d1SDavid du Colombier 	if(t == thing)
16773e12c5d1SDavid du Colombier 		thing = t->next;
16783e12c5d1SDavid du Colombier 	else{
16793e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
16803e12c5d1SDavid du Colombier 			;
16813e12c5d1SDavid du Colombier 		nt->next = t->next;
16823e12c5d1SDavid du Colombier 	}
16833e12c5d1SDavid du Colombier 	do
16843e12c5d1SDavid du Colombier 		for(nt=thing; nt; nt=nt->next)
16853e12c5d1SDavid du Colombier 			if(nt->parent == t){
16863e12c5d1SDavid du Colombier 				tclose1(nt);
16873e12c5d1SDavid du Colombier 				break;
16883e12c5d1SDavid du Colombier 			}
16893e12c5d1SDavid du Colombier 	while(nt);
16903e12c5d1SDavid du Colombier 	if(t->s)
16917dd7cddfSDavid du Colombier 		freesubfont(t->s);
16927dd7cddfSDavid du Colombier 	else
16937dd7cddfSDavid du Colombier 		freeimage(t->b);
16943e12c5d1SDavid du Colombier 	free(t->name);
16953e12c5d1SDavid du Colombier 	free(t);
16963e12c5d1SDavid du Colombier }
16973e12c5d1SDavid du Colombier 
16983e12c5d1SDavid du Colombier void
16993e12c5d1SDavid du Colombier tclose(Thing *t)
17003e12c5d1SDavid du Colombier {
17013e12c5d1SDavid du Colombier 	Thing *ct;
17023e12c5d1SDavid du Colombier 
17033e12c5d1SDavid du Colombier 	if(t->mod){
17043e12c5d1SDavid du Colombier 		mesg("%s modified", t->name);
17053e12c5d1SDavid du Colombier 		t->mod = 0;
17063e12c5d1SDavid du Colombier 		return;
17073e12c5d1SDavid du Colombier 	}
17083e12c5d1SDavid du Colombier 	/* fiddle to save redrawing unmoved things */
17093e12c5d1SDavid du Colombier 	if(t == thing)
17103e12c5d1SDavid du Colombier 		ct = 0;
17113e12c5d1SDavid du Colombier 	else
17123e12c5d1SDavid du Colombier 		for(ct=thing; ct; ct=ct->next)
17133e12c5d1SDavid du Colombier 			if(ct->next==t || ct->next->parent==t)
17143e12c5d1SDavid du Colombier 				break;
17153e12c5d1SDavid du Colombier 	tclose1(t);
17163e12c5d1SDavid du Colombier 	if(ct)
17173e12c5d1SDavid du Colombier 		ct = ct->next;
17183e12c5d1SDavid du Colombier 	else
17193e12c5d1SDavid du Colombier 		ct = thing;
17203e12c5d1SDavid du Colombier 	redraw(ct);
17213e12c5d1SDavid du Colombier }
17223e12c5d1SDavid du Colombier 
17233e12c5d1SDavid du Colombier void
17243e12c5d1SDavid du Colombier tread(Thing *t)
17253e12c5d1SDavid du Colombier {
17263e12c5d1SDavid du Colombier 	Thing *nt, *new;
17273e12c5d1SDavid du Colombier 	Fontchar *i;
17283e12c5d1SDavid du Colombier 	Rectangle r;
17293e12c5d1SDavid du Colombier 	int nclosed;
17303e12c5d1SDavid du Colombier 
17313e12c5d1SDavid du Colombier 	if(t->parent)
17323e12c5d1SDavid du Colombier 		t = t->parent;
17333e12c5d1SDavid du Colombier 	new = tget(t->name);
17343e12c5d1SDavid du Colombier 	if(new == 0)
17353e12c5d1SDavid du Colombier 		return;
17363e12c5d1SDavid du Colombier 	nclosed = 0;
17373e12c5d1SDavid du Colombier     again:
17383e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
17393e12c5d1SDavid du Colombier 		if(nt->parent == t){
17403e12c5d1SDavid du Colombier 			if(!rectinrect(nt->b->r, new->b->r)
17417dd7cddfSDavid du Colombier 			|| new->b->depth!=nt->b->depth){
17423e12c5d1SDavid du Colombier     closeit:
17433e12c5d1SDavid du Colombier 				nclosed++;
17443e12c5d1SDavid du Colombier 				nt->parent = 0;
17453e12c5d1SDavid du Colombier 				tclose1(nt);
17463e12c5d1SDavid du Colombier 				goto again;
17473e12c5d1SDavid du Colombier 			}
17483e12c5d1SDavid du Colombier 			if((t->s==0) != (new->s==0))
17493e12c5d1SDavid du Colombier 				goto closeit;
17503e12c5d1SDavid du Colombier 			if((t->face==0) != (new->face==0))
17513e12c5d1SDavid du Colombier 				goto closeit;
17523e12c5d1SDavid du Colombier 			if(t->s){	/* check same char */
17533e12c5d1SDavid du Colombier 				if(nt->c >= new->s->n)
17543e12c5d1SDavid du Colombier 					goto closeit;
17553e12c5d1SDavid du Colombier 				i = &new->s->info[nt->c];
17563e12c5d1SDavid du Colombier 				r.min.x = i[0].x;
17573e12c5d1SDavid du Colombier 				r.max.x = i[1].x;
17583e12c5d1SDavid du Colombier 				r.min.y = new->b->r.min.y;
17593e12c5d1SDavid du Colombier 				r.max.y = new->b->r.max.y;
17603e12c5d1SDavid du Colombier 				if(!eqrect(r, nt->b->r))
17613e12c5d1SDavid du Colombier 					goto closeit;
17623e12c5d1SDavid du Colombier 			}
17633e12c5d1SDavid du Colombier 			nt->parent = new;
17647dd7cddfSDavid du Colombier 			draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
17653e12c5d1SDavid du Colombier 		}
17663e12c5d1SDavid du Colombier 	new->next = t->next;
17673e12c5d1SDavid du Colombier 	if(t == thing)
17683e12c5d1SDavid du Colombier 		thing = new;
17693e12c5d1SDavid du Colombier 	else{
17703e12c5d1SDavid du Colombier 		for(nt=thing; nt->next!=t; nt=nt->next)
17713e12c5d1SDavid du Colombier 			;
17723e12c5d1SDavid du Colombier 		nt->next = new;
17733e12c5d1SDavid du Colombier 	}
17743e12c5d1SDavid du Colombier 	if(t->s)
17757dd7cddfSDavid du Colombier 		freesubfont(t->s);
17767dd7cddfSDavid du Colombier 	else
17777dd7cddfSDavid du Colombier 		freeimage(t->b);
17783e12c5d1SDavid du Colombier 	free(t->name);
17793e12c5d1SDavid du Colombier 	free(t);
17803e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
17813e12c5d1SDavid du Colombier 		if(nt==new || nt->parent==new)
17823e12c5d1SDavid du Colombier 			if(nclosed == 0)
17837dd7cddfSDavid du Colombier 				drawthing(nt, 0);	/* can draw in place */
17843e12c5d1SDavid du Colombier 			else{
17853e12c5d1SDavid du Colombier 				redraw(nt);	/* must redraw all below */
17863e12c5d1SDavid du Colombier 				break;
17873e12c5d1SDavid du Colombier 			}
17883e12c5d1SDavid du Colombier }
17893e12c5d1SDavid du Colombier 
17903e12c5d1SDavid du Colombier void
17913e12c5d1SDavid du Colombier tchar(Thing *t)
17923e12c5d1SDavid du Colombier {
1793219b2ee8SDavid du Colombier 	char buf[256], *p;
17943e12c5d1SDavid du Colombier 	Rune r;
1795219b2ee8SDavid du Colombier 	ulong c, d;
17963e12c5d1SDavid du Colombier 
17973e12c5d1SDavid du Colombier 	if(t->s == 0){
17987dd7cddfSDavid du Colombier 		t = t->parent;
17997dd7cddfSDavid du Colombier 		if(t==0 || t->s==0){
18003e12c5d1SDavid du Colombier 			mesg("not a subfont");
18013e12c5d1SDavid du Colombier 			return;
18023e12c5d1SDavid du Colombier 		}
18037dd7cddfSDavid du Colombier 	}
1804219b2ee8SDavid du Colombier 	if(type(buf, "char (hex or character or hex-hex)") == 0)
18053e12c5d1SDavid du Colombier 		return;
18063e12c5d1SDavid du Colombier 	if(utflen(buf) == 1){
18073e12c5d1SDavid du Colombier 		chartorune(&r, buf);
18083e12c5d1SDavid du Colombier 		c = r;
1809219b2ee8SDavid du Colombier 		d = r;
18103e12c5d1SDavid du Colombier 	}else{
18113e12c5d1SDavid du Colombier 		if(!strchr(hex, buf[0])){
18123e12c5d1SDavid du Colombier 			mesg("illegal hex character");
18133e12c5d1SDavid du Colombier 			return;
18143e12c5d1SDavid du Colombier 		}
18153e12c5d1SDavid du Colombier 		c = strtoul(buf, 0, 16);
1816219b2ee8SDavid du Colombier 		d = c;
1817219b2ee8SDavid du Colombier 		p = utfrune(buf, '-');
1818219b2ee8SDavid du Colombier 		if(p){
1819219b2ee8SDavid du Colombier 			d = strtoul(p+1, 0, 16);
1820219b2ee8SDavid du Colombier 			if(d < c){
1821219b2ee8SDavid du Colombier 				mesg("invalid range");
1822219b2ee8SDavid du Colombier 				return;
1823219b2ee8SDavid du Colombier 			}
1824219b2ee8SDavid du Colombier 		}
18253e12c5d1SDavid du Colombier 	}
18263e12c5d1SDavid du Colombier 	c -= t->off;
1827219b2ee8SDavid du Colombier 	d -= t->off;
1828219b2ee8SDavid du Colombier 	while(c <= d){
18293e12c5d1SDavid du Colombier 		if(c<0 || c>=t->s->n){
18303e12c5d1SDavid du Colombier 			mesg("0x%lux not in font %s", c+t->off, t->name);
18313e12c5d1SDavid du Colombier 			return;
18323e12c5d1SDavid du Colombier 		}
18333e12c5d1SDavid du Colombier 		openedit(t, Pt(0, 0), c);
1834219b2ee8SDavid du Colombier 		c++;
1835219b2ee8SDavid du Colombier 	}
18363e12c5d1SDavid du Colombier }
18373e12c5d1SDavid du Colombier 
18383e12c5d1SDavid du Colombier void
18393e12c5d1SDavid du Colombier apply(void (*f)(Thing*))
18403e12c5d1SDavid du Colombier {
18413e12c5d1SDavid du Colombier 	Thing *t;
18423e12c5d1SDavid du Colombier 
18437dd7cddfSDavid du Colombier 	esetcursor(&sight);
18443e12c5d1SDavid du Colombier 	buttons(Down);
18453e12c5d1SDavid du Colombier 	if(mouse.buttons == 4)
18463e12c5d1SDavid du Colombier 		for(t=thing; t; t=t->next)
18473e12c5d1SDavid du Colombier 			if(ptinrect(mouse.xy, t->er)){
18483e12c5d1SDavid du Colombier 				buttons(Up);
18493e12c5d1SDavid du Colombier 				f(t);
18503e12c5d1SDavid du Colombier 				break;
18513e12c5d1SDavid du Colombier 			}
18523e12c5d1SDavid du Colombier 	buttons(Up);
18537dd7cddfSDavid du Colombier 	esetcursor(0);
18547dd7cddfSDavid du Colombier }
18557dd7cddfSDavid du Colombier 
18567dd7cddfSDavid du Colombier int
18577dd7cddfSDavid du Colombier complement(Image *t)
18587dd7cddfSDavid du Colombier {
18597dd7cddfSDavid du Colombier 	int i, n;
18607dd7cddfSDavid du Colombier 	uchar *buf;
18617dd7cddfSDavid du Colombier 
18627dd7cddfSDavid du Colombier 	n = Dy(t->r)*bytesperline(t->r, t->depth);
18637dd7cddfSDavid du Colombier 	buf = malloc(n);
18647dd7cddfSDavid du Colombier 	if(buf == 0)
18657dd7cddfSDavid du Colombier 		return 0;
18667dd7cddfSDavid du Colombier 	unloadimage(t, t->r, buf, n);
18677dd7cddfSDavid du Colombier 	for(i=0; i<n; i++)
18687dd7cddfSDavid du Colombier 		buf[i] = ~buf[i];
18697dd7cddfSDavid du Colombier 	loadimage(t, t->r, buf, n);
18707dd7cddfSDavid du Colombier 	free(buf);
18717dd7cddfSDavid du Colombier 	return 1;
18723e12c5d1SDavid du Colombier }
18733e12c5d1SDavid du Colombier 
18743e12c5d1SDavid du Colombier void
18753e12c5d1SDavid du Colombier copy(void)
18763e12c5d1SDavid du Colombier {
18773e12c5d1SDavid du Colombier 	Thing *st, *dt, *nt;
18783e12c5d1SDavid du Colombier 	Rectangle sr, dr, fr;
18797dd7cddfSDavid du Colombier 	Image *tmp;
18803e12c5d1SDavid du Colombier 	Point p1, p2;
18813e12c5d1SDavid du Colombier 	int but, up;
18823e12c5d1SDavid du Colombier 
18837dd7cddfSDavid du Colombier 	if(!sweep(3, &sr))
18843e12c5d1SDavid du Colombier 		return;
18853e12c5d1SDavid du Colombier 	for(st=thing; st; st=st->next)
18863e12c5d1SDavid du Colombier 		if(rectXrect(sr, st->r))
18873e12c5d1SDavid du Colombier 			break;
18883e12c5d1SDavid du Colombier 	if(st == 0)
18893e12c5d1SDavid du Colombier 		return;
18903e12c5d1SDavid du Colombier 	/* click gives full rectangle */
18913e12c5d1SDavid du Colombier 	if(Dx(sr)<4 && Dy(sr)<4)
18923e12c5d1SDavid du Colombier 		sr = st->r;
18933e12c5d1SDavid du Colombier 	rectclip(&sr, st->r);
18943e12c5d1SDavid du Colombier 	p1 = realpt(st, sr.min);
18953e12c5d1SDavid du Colombier 	p2 = realpt(st, Pt(sr.min.x, sr.max.y));
189680ee5cbfSDavid du Colombier 	up = 0;
18973e12c5d1SDavid du Colombier 	if(p1.x != p2.x){	/* swept across a fold */
18983e12c5d1SDavid du Colombier    onafold:
18993e12c5d1SDavid du Colombier 		mesg("sweep spans a fold");
190080ee5cbfSDavid du Colombier 		goto Return;
19013e12c5d1SDavid du Colombier 	}
19023e12c5d1SDavid du Colombier 	p2 = realpt(st, sr.max);
19033e12c5d1SDavid du Colombier 	sr.min = p1;
19043e12c5d1SDavid du Colombier 	sr.max = p2;
19053e12c5d1SDavid du Colombier 	fr.min = screenpt(st, sr.min);
19063e12c5d1SDavid du Colombier 	fr.max = screenpt(st, sr.max);
19077dd7cddfSDavid du Colombier 	p1 = subpt(p2, p1);	/* diagonal */
19087dd7cddfSDavid du Colombier 	if(p1.x==0 || p1.y==0)
19093e12c5d1SDavid du Colombier 		return;
19107dd7cddfSDavid du Colombier 	border(screen, fr, -1, values[Blue], ZP);
19117dd7cddfSDavid du Colombier 	esetcursor(&box);
19123e12c5d1SDavid du Colombier 	for(; mouse.buttons==0; mouse=emouse()){
19133e12c5d1SDavid du Colombier 		for(dt=thing; dt; dt=dt->next)
19143e12c5d1SDavid du Colombier 			if(ptinrect(mouse.xy, dt->er))
19153e12c5d1SDavid du Colombier 				break;
19163e12c5d1SDavid du Colombier 		if(up)
19177dd7cddfSDavid du Colombier 			edrawgetrect(insetrect(dr, -Borderwidth), 0);
19183e12c5d1SDavid du Colombier 		up = 0;
19193e12c5d1SDavid du Colombier 		if(dt == 0)
19203e12c5d1SDavid du Colombier 			continue;
19213e12c5d1SDavid du Colombier 		dr.max = screenpt(dt, realpt(dt, mouse.xy));
19227dd7cddfSDavid du Colombier 		dr.min = subpt(dr.max, mulpt(p1, dt->mag));
19233e12c5d1SDavid du Colombier 		if(!rectXrect(dr, dt->r))
19243e12c5d1SDavid du Colombier 			continue;
19257dd7cddfSDavid du Colombier 		edrawgetrect(insetrect(dr, -Borderwidth), 1);
19263e12c5d1SDavid du Colombier 		up = 1;
19273e12c5d1SDavid du Colombier 	}
19283e12c5d1SDavid du Colombier 	/* if up==1, we had a hit */
19297dd7cddfSDavid du Colombier 	esetcursor(0);
19303e12c5d1SDavid du Colombier 	if(up)
19317dd7cddfSDavid du Colombier 		edrawgetrect(insetrect(dr, -Borderwidth), 0);
19323e12c5d1SDavid du Colombier 	but = mouse.buttons;
19333e12c5d1SDavid du Colombier 	buttons(Up);
19343e12c5d1SDavid du Colombier 	if(!up || but!=4)
19357dd7cddfSDavid du Colombier 		goto Return;
19363e12c5d1SDavid du Colombier 	dt = 0;
19373e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
19383e12c5d1SDavid du Colombier 		if(rectXrect(dr, nt->r)){
19393e12c5d1SDavid du Colombier 			if(dt){
19403e12c5d1SDavid du Colombier 				mesg("ambiguous sweep");
19413e12c5d1SDavid du Colombier 				return;
19423e12c5d1SDavid du Colombier 			}
19433e12c5d1SDavid du Colombier 			dt = nt;
19443e12c5d1SDavid du Colombier 		}
19453e12c5d1SDavid du Colombier 	if(dt == 0)
19467dd7cddfSDavid du Colombier 		goto Return;
19473e12c5d1SDavid du Colombier 	p1 = realpt(dt, dr.min);
19483e12c5d1SDavid du Colombier 	p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
19493e12c5d1SDavid du Colombier 	if(p1.x != p2.x)
19503e12c5d1SDavid du Colombier 		goto onafold;
19513e12c5d1SDavid du Colombier 	p2 = realpt(dt, dr.max);
19523e12c5d1SDavid du Colombier 	dr.min = p1;
19533e12c5d1SDavid du Colombier 	dr.max = p2;
19547dd7cddfSDavid du Colombier 
19557dd7cddfSDavid du Colombier 	if(invert){
19567dd7cddfSDavid du Colombier 		tmp = allocimage(display, dr, dt->b->chan, 0, 255);
19577dd7cddfSDavid du Colombier 		if(tmp == 0){
19587dd7cddfSDavid du Colombier     nomem:
19597dd7cddfSDavid du Colombier 			mesg("can't allocate temporary");
19607dd7cddfSDavid du Colombier 			goto Return;
19617dd7cddfSDavid du Colombier 		}
19627dd7cddfSDavid du Colombier 		draw(tmp, dr, st->b, nil, sr.min);
19637dd7cddfSDavid du Colombier 		if(!complement(tmp))
19647dd7cddfSDavid du Colombier 			goto nomem;
19657dd7cddfSDavid du Colombier 		draw(dt->b, dr, tmp, nil, dr.min);
19667dd7cddfSDavid du Colombier 		freeimage(tmp);
19677dd7cddfSDavid du Colombier 	}else
19687dd7cddfSDavid du Colombier 		draw(dt->b, dr, st->b, nil, sr.min);
19693e12c5d1SDavid du Colombier 	if(dt->parent){
19707dd7cddfSDavid du Colombier 		draw(dt->parent->b, dr, dt->b, nil, dr.min);
19713e12c5d1SDavid du Colombier 		dt = dt->parent;
19723e12c5d1SDavid du Colombier 	}
19737dd7cddfSDavid du Colombier 	drawthing(dt, 0);
19743e12c5d1SDavid du Colombier 	for(nt=thing; nt; nt=nt->next)
19753e12c5d1SDavid du Colombier 		if(nt->parent==dt && rectXrect(dr, nt->b->r)){
19767dd7cddfSDavid du Colombier 			draw(nt->b, dr, dt->b, nil, dr.min);
19777dd7cddfSDavid du Colombier 			drawthing(nt, 0);
19783e12c5d1SDavid du Colombier 		}
19793e12c5d1SDavid du Colombier 	ckinfo(dt, dr);
19803e12c5d1SDavid du Colombier 	dt->mod = 1;
19817dd7cddfSDavid du Colombier 
19827dd7cddfSDavid du Colombier Return:
19837dd7cddfSDavid du Colombier 	/* clear blue box */
19847dd7cddfSDavid du Colombier 	drawthing(st, 0);
19853e12c5d1SDavid du Colombier }
19863e12c5d1SDavid du Colombier 
19873e12c5d1SDavid du Colombier void
19883e12c5d1SDavid du Colombier menu(void)
19893e12c5d1SDavid du Colombier {
19903e12c5d1SDavid du Colombier 	Thing *t;
19913e12c5d1SDavid du Colombier 	char *mod;
19923e12c5d1SDavid du Colombier 	int sel;
19933e12c5d1SDavid du Colombier 	char buf[256];
19943e12c5d1SDavid du Colombier 
19957dd7cddfSDavid du Colombier 	sel = emenuhit(3, &mouse, &menu3);
19963e12c5d1SDavid du Colombier 	switch(sel){
19973e12c5d1SDavid du Colombier 	case Mopen:
19983e12c5d1SDavid du Colombier 		if(type(buf, "file")){
19993e12c5d1SDavid du Colombier 			t = tget(buf);
20003e12c5d1SDavid du Colombier 			if(t)
20017dd7cddfSDavid du Colombier 				drawthing(t, 1);
20023e12c5d1SDavid du Colombier 		}
20033e12c5d1SDavid du Colombier 		break;
20043e12c5d1SDavid du Colombier 	case Mwrite:
20053e12c5d1SDavid du Colombier 		apply(twrite);
20063e12c5d1SDavid du Colombier 		break;
20073e12c5d1SDavid du Colombier 	case Mread:
20083e12c5d1SDavid du Colombier 		apply(tread);
20093e12c5d1SDavid du Colombier 		break;
20103e12c5d1SDavid du Colombier 	case Mchar:
20113e12c5d1SDavid du Colombier 		apply(tchar);
20123e12c5d1SDavid du Colombier 		break;
20133e12c5d1SDavid du Colombier 	case Mcopy:
20143e12c5d1SDavid du Colombier 		copy();
20153e12c5d1SDavid du Colombier 		break;
20167dd7cddfSDavid du Colombier 	case Mpixels:
20177dd7cddfSDavid du Colombier 		tpixels();
20187dd7cddfSDavid du Colombier 		break;
20193e12c5d1SDavid du Colombier 	case Mclose:
20203e12c5d1SDavid du Colombier 		apply(tclose);
20213e12c5d1SDavid du Colombier 		break;
20223e12c5d1SDavid du Colombier 	case Mexit:
20233e12c5d1SDavid du Colombier 		mod = 0;
20243e12c5d1SDavid du Colombier 		for(t=thing; t; t=t->next)
20253e12c5d1SDavid du Colombier 			if(t->mod){
20263e12c5d1SDavid du Colombier 				mod = t->name;
20273e12c5d1SDavid du Colombier 				t->mod = 0;
20283e12c5d1SDavid du Colombier 			}
20293e12c5d1SDavid du Colombier 		if(mod){
20303e12c5d1SDavid du Colombier 			mesg("%s modified", mod);
20313e12c5d1SDavid du Colombier 			break;
20323e12c5d1SDavid du Colombier 		}
20337dd7cddfSDavid du Colombier 		esetcursor(&skull);
20343e12c5d1SDavid du Colombier 		buttons(Down);
20353e12c5d1SDavid du Colombier 		if(mouse.buttons == 4){
20363e12c5d1SDavid du Colombier 			buttons(Up);
20373e12c5d1SDavid du Colombier 			exits(0);
20383e12c5d1SDavid du Colombier 		}
20393e12c5d1SDavid du Colombier 		buttons(Up);
20407dd7cddfSDavid du Colombier 		esetcursor(0);
20413e12c5d1SDavid du Colombier 		break;
20423e12c5d1SDavid du Colombier 	}
20433e12c5d1SDavid du Colombier }
2044