xref: /plan9-contrib/sys/src/cmd/tweak.c (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
1 #include <u.h>
2 #include <libc.h>
3 #include <libg.h>
4 #include <bio.h>
5 
6 /*
7  * This program keeps the system's and the application's bitmap and
8  * subfont structures consistent, but that is really not necessary;
9  * in fact it isn't even necessary to hold the subfonts in the system
10  * at all.  Doing it this way makes it easy to do I/O on subfonts.
11  * Even so, edits to individual character's Fontchars are not passed
12  * to the system.
13  */
14 
15 typedef struct	Thing	Thing;
16 
17 struct Thing
18 {
19 	Bitmap	*b;
20 	Subfont *s;
21 	char	*name;		/* file name */
22 	int	face;		/* is 48x48 face file or cursor file*/
23 	Rectangle r;		/* drawing region */
24 	Rectangle tr;		/* text region */
25 	Rectangle er;		/* entire region */
26 	long	c;		/* character number in subfont */
27 	int	mod;		/* modified */
28 	int	mag;		/* magnification */
29 	Rune	off;		/* offset for subfont indices */
30 	Thing	*parent;	/* thing of which i'm an edit */
31 	Thing	*next;
32 };
33 
34 enum
35 {
36 	Border	= 1,
37 	Up	= 1,
38 	Down	= 0,
39 	Mag	= 4,
40 	Maxmag	= 10,
41 };
42 
43 enum
44 {
45 	Mopen,
46 	Mread,
47 	Mwrite,
48 	Mcopy,
49 	Mchar,
50 	Mclose,
51 	Mexit,
52 };
53 
54 
55 char	*menu3str[] = {
56 	[Mopen]		"open",
57 	[Mread]		"read",
58 	[Mwrite]	"write",
59 	[Mcopy]		"copy",
60 	[Mchar]		"char",
61 	[Mclose]	"close",
62 	[Mexit]		"exit",
63 	0,
64 };
65 
66 Menu	menu3 = {
67 	menu3str
68 };
69 
70 Cursor sweep0 = {
71 	{-7, -7},
72 	{0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0,
73 	 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF,
74 	 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0,
75 	 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0},
76 	{0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
77 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE,
78 	 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
79 	 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00}
80 };
81 
82 Cursor box = {
83 	{-7, -7},
84 	{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
85 	 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F,
86 	 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF,
87 	 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
88 	{0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE,
89 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
90 	 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E,
91 	 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00}
92 };
93 
94 Cursor sight = {
95 	{-7, -7},
96 	{0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
97 	 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
98 	 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
99 	 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,},
100 	{0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
101 	 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
102 	 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
103 	 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,}
104 };
105 
106 Cursor busy = {
107 	{-7, -7},
108 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 	 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7,
110 	 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe,
111 	 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,},
112 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 	 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82,
114 	 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe,
115 	 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,}
116 };
117 
118 Cursor skull = {
119 	{-7,-7},
120 	{0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7,
121 	 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8,
122 	 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff,
123 	 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,},
124 	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03,
125 	 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0,
126 	 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27,
127 	 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
128 };
129 
130 char	*fns[] = {
131 	"0",      "Zero",		0,
132 	"~(S|D)", "~(D|S)", "DnorS",	0,
133 	"~S&D",   "D&~S",   "DandnotS",	0,
134 	"~S",     "notS",		0,
135 	"S&~D",   "~D&S",   "notDandS",	0,
136 	"~D",     "notD",		0,
137 	"S^D",    "D^S",    "DxorS",	0,
138 	"~(S&D)", "~(D&S)", "DnandS",	0,
139 	"S&D",    "D&S",    "DandS",	0,
140 	"~(S^D)", "~(D^S)", "DxnorS",	0,
141 	"D",				0,
142 	"~S|D",   "D|~S",   "DornotS",	0,
143 	"S",				0,
144 	"S|~D",   "~D|S",   "notDorS",	0,
145 	"S|D",    "D|S",    "DorS",	0,
146 	"F",				0,
147 	0
148 };
149 
150 Rectangle	cntlr;		/* control region */
151 Rectangle	editr;		/* editing region */
152 Rectangle	textr;		/* text region */
153 Thing		*thing;
154 Mouse		mouse;
155 char		hex[] = "0123456789abcdefABCDEF";
156 jmp_buf		err;
157 char		*file;
158 int		mag;
159 uint		copyfn = S;
160 uint		but1fn = S;
161 uint		but2fn = 0;
162 Bitmap		*values;
163 ulong		val = ~0;
164 uchar		data[8192];
165 
166 Thing*	tget(char*);
167 void	mesg(char*, ...);
168 void	draw(Thing*, int);
169 void	select(void);
170 void	menu(void);
171 void	error(char*);
172 void	buttons(int);
173 char	*fntostr(uint);
174 int	strtofn(char*);
175 void	drawall(void);
176 void	tclose1(Thing*);
177 
178 void
179 main(int argc, char *argv[])
180 {
181 	int i, j;
182 	Event e;
183 	Thing *t;
184 
185 	mag = Mag;
186 	binit(error, 0, "tweak");
187 	values = balloc(Rect(0, 0, 256*Maxmag, Maxmag), screen.ldepth);
188 	if(values == 0)
189 		berror("can't balloc");
190 	for(i=0; i<256; i++)
191 		for(j=0; j<Maxmag; j++)
192 			segment(values, Pt(i*Maxmag, j), Pt((i+1)*Maxmag, j), i, S);
193 	einit(Emouse|Ekeyboard);
194 	ereshaped(screen.r);
195 	i = 1;
196 	setjmp(err);
197 	for(; i<argc; i++){
198 		file = argv[i];
199 		t = tget(argv[i]);
200 		if(t)
201 			draw(t, 1);
202 		bflush();
203 	}
204 	file = 0;
205 	setjmp(err);
206 	for(;;)
207 		switch(event(&e)){
208 		case Ekeyboard:
209 			break;
210 		case Emouse:
211 			mouse = e.mouse;
212 			if(mouse.buttons & 3){
213 				select();
214 				break;
215 			}
216 			if(mouse.buttons & 4)
217 				menu();
218 		}
219 }
220 
221 void
222 error(char *s)
223 {
224 	if(file)
225 		mesg("can't read %s: %s: %r", file, s);
226 	else
227 		mesg("/dev/bitblt error: %s", s);
228 	if(err[0])
229 		longjmp(err, 1);
230 	exits(s);
231 }
232 
233 void
234 redraw(Thing *t)
235 {
236 	Thing *nt;
237 	Point p;
238 
239 	if(thing==0 || thing==t)
240 		bitblt(&screen, editr.min, &screen, editr, 0);
241 	if(thing == 0)
242 		return;
243 	if(thing != t){
244 		for(nt=thing; nt->next!=t; nt=nt->next)
245 			;
246 		bitblt(&screen, Pt(screen.r.min.x, nt->er.max.y), &screen,
247 			Rect(screen.r.min.x, nt->er.max.y, editr.max.x, editr.max.y), 0);
248 	}
249 	for(nt=t; nt; nt=nt->next){
250 		draw(nt, 0);
251 		if(nt->next == 0){
252 			p = Pt(editr.min.x, nt->er.max.y);
253 			bitblt(&screen, p, &screen, Rpt(p, editr.max), 0);
254 		}
255 	}
256 	mesg("");
257 }
258 
259 void
260 ereshaped(Rectangle r)
261 {
262 	USED(r);
263 
264 	screen.r = bscreenrect(&screen.clipr);
265 	cntlr = inset(screen.clipr, 1);
266 	editr = cntlr;
267 	textr = editr;
268 	textr.min.y = textr.max.y - font->height;
269 	cntlr.max.y = cntlr.min.y + font->height;
270 	editr.min.y = cntlr.max.y+1;
271 	editr.max.y = textr.min.y-1;
272 	bitblt(&screen, screen.clipr.min, &screen, screen.clipr, 0);
273 	segment(&screen, Pt(editr.min.x, editr.max.y),
274 			 Pt(editr.max.x+1, editr.max.y), ~0, F);
275 	clipr(&screen, editr);
276 	drawall();
277 }
278 
279 void
280 mesgstr(Point p, int line, char *s)
281 {
282 	Rectangle c, r;
283 
284 	r.min = p;
285 	r.min.y += line*font->height;
286 	r.max.y = r.min.y+font->height;
287 	r.max.x = editr.max.x;
288 	c = screen.clipr;
289 	clipr(&screen, r);
290 	bitblt(&screen, r.min, &screen, r, 0);
291 	r.min.x++;
292 	p = string(&screen, r.min, font, s, S);
293 	r.max.x = p.x + 1;
294 	r.min.x--;
295 	bitblt(&screen, r.min, &screen, r, ~D);
296 	clipr(&screen, c);
297 	bflush();
298 }
299 
300 void
301 mesg(char *fmt, ...)
302 {
303 	char buf[1024];
304 
305 	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
306 	mesgstr(textr.min, 0, buf);
307 }
308 
309 void
310 tmesg(Thing *t, int line, char *fmt, ...)
311 {
312 	char buf[1024];
313 
314 	doprint(buf, buf+sizeof(buf), fmt, (&fmt+1));
315 	mesgstr(t->tr.min, line, buf);
316 }
317 
318 
319 void
320 scntl(char *l)
321 {
322 	char vals[16];
323 
324 	if(val & 0x80000000)
325 		sprint(vals, "~%lux", ~val);
326 	else
327 		sprint(vals, "%lux", val);
328 	sprint(l, "mag: %d val(hex): %s but1: %s but2: %s copy: %s",
329 		mag, vals, fntostr(but1fn), fntostr(but2fn), fntostr(copyfn));
330 }
331 
332 void
333 cntl(void)
334 {
335 	char buf[256];
336 
337 	scntl(buf);
338 	mesgstr(cntlr.min, 0, buf);
339 }
340 
341 void
342 stext(Thing *t, char *l0, char *l1)
343 {
344 	Fontchar *fc;
345 	char buf[256];
346 
347 	l1[0] = 0;
348 	sprint(buf, "ldepth:%d r:%d %d  %d %d ",
349 		t->b->ldepth, t->b->r.min.x, t->b->r.min.y,
350 		t->b->r.max.x, t->b->r.max.y);
351 	if(t->parent)
352 		sprint(buf+strlen(buf), "mag: %d ", t->mag);
353 	sprint(l0, "%s file: %s", buf, t->name);
354 	if(t->c >= 0){
355 		fc = &t->parent->s->info[t->c];
356 		sprint(l1, "c(hex): %x c(char): %C x: %d "
357 			   "top: %d bottom: %d left: %d width: %d iwidth: %d",
358 			t->c+t->parent->off, t->c+t->parent->off,
359 			fc->x, fc->top, fc->bottom, fc->left,
360 			fc->width, Dx(t->b->r));
361 	}else if(t->s)
362 		sprint(l1, "offset(hex): %ux n:%d  height:%d  ascent:%d",
363 			t->off, t->s->n, t->s->height, t->s->ascent);
364 }
365 
366 void
367 text(Thing *t)
368 {
369 	char l0[256], l1[256];
370 
371 	stext(t, l0, l1);
372 	tmesg(t, 0, l0);
373 	if(l1[0])
374 		tmesg(t, 1, l1);
375 }
376 
377 void
378 drawall(void)
379 {
380 	Thing *t;
381 
382 	cntl();
383 	for(t=thing; t; t=t->next)
384 		draw(t, 0);
385 }
386 
387 int
388 value(Bitmap *b, int x)
389 {
390 	int v, l, w;
391 	uchar mask;
392 
393 	l = b->ldepth;
394 	if(l > 3){
395 		mesg("ldepth too large");
396 		return 0;
397 	}
398 	w = 1<<l;
399 	mask = (2<<w)-1;		/* ones at right end of word */
400 	x -= b->r.min.x&~(7>>l);	/* adjust x relative to first pixel */
401 	v = data[x>>(3-l)];
402 	v >>= ((7>>l)<<l) - ((x&(7>>l))<<l);	/* pixel at right end of word */
403 	v &= mask;			/* pixel at right end of word */
404 	return v;
405 }
406 
407 int
408 bvalue(int v, int l)
409 {
410 	v &= (1<<(1<<l))-1;
411 	if(l > screen.ldepth)
412 		v >>= (1<<l) - (1<<screen.ldepth);
413 	else
414 		while(l < screen.ldepth){
415 			v |= v << (1<<l);
416 			l++;
417 		}
418 	return v*Maxmag;
419 }
420 
421 void
422 draw(Thing *nt, int link)
423 {
424 	int nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
425 	Thing *t;
426 	Subfont *s;
427 	Bitmap *b;
428 	Point p, p1, p2;
429 
430 	if(link){
431 		nt->next = 0;
432 		if(thing == 0){
433 			thing = nt;
434 			y = editr.min.y;
435 		}else{
436 			for(t=thing; t->next; t=t->next)
437 				;
438 			t->next = nt;
439 			y = t->er.max.y;
440 		}
441 	}else{
442 		if(thing == nt)
443 			y = editr.min.y;
444 		else{
445 			for(t=thing; t->next!=nt; t=t->next)
446 				;
447 			y = t->er.max.y;
448 		}
449 	}
450 	s = nt->s;
451 	b = nt->b;
452 	nl = font->height;
453 	if(s || nt->c>=0)
454 		nl += font->height;
455 	fdx = Dx(editr) - 2*Border;
456 	dx = Dx(b->r);
457 	dy = Dy(b->r);
458 	if(nt->mag > 1){
459 		dx *= nt->mag;
460 		dy *= nt->mag;
461 		fdx -= fdx%nt->mag;
462 	}
463 	nf = 1 + dx/fdx;
464 	nt->er.min.y = y;
465 	nt->er.min.x = editr.min.x;
466 	nt->er.max.x = nt->er.min.x + Border + dx + Border;
467 	if(nt->er.max.x > editr.max.x)
468 		nt->er.max.x = editr.max.x;
469 	nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
470 	nt->r = inset(nt->er, Border);
471 	nt->er.max.x = editr.max.x;
472 	bitblt(&screen, nt->er.min, &screen, nt->er, 0);
473 	for(i=0; i<nf; i++){
474 		p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
475 		/* draw portion of bitmap */
476 		p = Pt(p1.x+1, p1.y);
477 		if(nt->mag == 1)
478 			bitblt(&screen, p, b,
479 				Rect(b->r.min.x+i*fdx, b->r.min.y,
480 				     b->r.max.x+(i+1)*fdx, b->r.max.y), S);
481 		else{
482 			for(y=b->r.min.y; y<b->r.max.y; y++){
483 				sy = p.y+(y-b->r.min.y)*nt->mag;
484 				rdbitmap(b, y, y+1, data);
485 				for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
486 					sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
487 					if(sx >= nt->r.max.x)
488 						break;
489 					v = bvalue(value(b, x), b->ldepth);
490 					if(v)
491 						bitblt(&screen, Pt(sx, sy),
492 							values,
493 							Rect(v, 0, v+nt->mag, nt->mag), S);
494 				}
495 
496 			}
497 		}
498 		/* line down left */
499 		segment(&screen, p1, Pt(p1.x, p1.y+dy+Border+1), i==0? ~0 : 0, S);
500 		/* line across top */
501 		segment(&screen, Pt(p1.x, p1.y-1), Pt(nt->r.max.x+Border, p1.y-1), ~0, F);
502 		p2 = p1;
503 		if(i == nf-1)
504 			p2.x += 1 + dx%fdx;
505 		else
506 			p2.x = nt->r.max.x;
507 		/* line down right */
508 		segment(&screen, p2, Pt(p2.x, p2.y+dy+1), i==nf-1? ~0 : 0, S);
509 		/* line across bottom */
510 		if(i == nf-1){
511 			p1.y += Border+dy;
512 			segment(&screen, Pt(p1.x, p1.y-1), Pt(p2.x, p1.y-1), ~0, F);
513 		}
514 	}
515 	nt->tr.min.x = editr.min.x;
516 	nt->tr.max.x = editr.max.x;
517 	nt->tr.min.y = nt->er.max.y + Border;
518 	nt->tr.max.y = nt->tr.min.y + nl;
519 	nt->er.max.y = nt->tr.max.y + Border;
520 	text(nt);
521 }
522 
523 int
524 tohex(int c)
525 {
526 	if('0'<=c && c<='9')
527 		return c - '0';
528 	if('a'<=c && c<='f')
529 		return 10 + (c - 'a');
530 	if('A'<=c && c<='F')
531 		return 10 + (c - 'A');
532 }
533 
534 Thing*
535 tget(char *file)
536 {
537 	int i, j, fd, face, x, y, c, ld;
538 	Bitmap *b, *tb;
539 	Subfont *s;
540 	Thing *t;
541 	Dir d;
542 	jmp_buf oerr;
543 	uchar buf[256];
544 	char *data;
545 
546 	errstr((char*)buf);	/* flush pending error message */
547 	memmove(oerr, err, sizeof err);
548 	if(setjmp(err)){
549    Err:
550 		memmove(err, oerr, sizeof err);
551 		return 0;
552 	}
553 	fd = open(file, OREAD);
554 	if(fd < 0){
555 		mesg("can't open %s: %r", file);
556 		goto Err;
557 	}
558 	if(dirfstat(fd, &d) < 0){
559 		mesg("can't stat bitmap file %s: %r", file);
560 		close(fd);
561 		goto Err;
562 	}
563 	if(read(fd, buf, 2) != 2){
564 		mesg("can't read %s: %r", file);
565 		close(fd);
566 		goto Err;
567 	}
568 	seek(fd, 0, 0);
569 	if(buf[0]=='0' && buf[1]=='x'){
570 		/*
571 		 * face file
572 		 */
573 		face = 1;
574 		s = 0;
575 		data = malloc(d.length+1);
576 		if(data == 0){
577 			mesg("can't malloc buffer: %r");
578 			close(fd);
579 			goto Err;
580 		}
581 		data[d.length] = 0;
582 		if(read(fd, data, d.length) != d.length){
583 			mesg("can't read bitmap file %s: %r", file);
584 			close(fd);
585 			goto Err;
586 		}
587 		for(y=0,i=0; i<d.length; i++)
588 			if(data[i] == '\n')
589 				y++;
590 		if(y == 0){
591 	ill:
592 			mesg("ill-formed face file %s", file);
593 			close(fd);
594 			free(data);
595 			goto Err;
596 		}
597 		for(x=0,i=0; (c=data[i])!='\n'; ){
598 			if(c==',' || c==' ' || c=='\t'){
599 				i++;
600 				continue;
601 			}
602 			if(c=='0' && data[i+1] == 'x'){
603 				i += 2;
604 				continue;
605 			}
606 			if(strchr(hex, c)){
607 				x += 4;
608 				i++;
609 				continue;
610 			}
611 			goto ill;
612 		}
613 		if(x % y)
614 			goto ill;
615 		switch(x / y){
616 		default:
617 			goto ill;
618 		case 1:
619 			ld = 0;
620 			break;
621 		case 2:
622 			ld = 1;
623 			break;
624 		case 4:
625 			ld = 2;
626 			break;
627 		case 8:
628 			ld = 3;
629 			break;
630 		}
631 		b = balloc(Rect(0, 0, y, y), ld);
632 		if(b == 0){
633 			mesg("balloc failed file %s: %r", file);
634 			free(data);
635 			close(fd);
636 			goto Err;
637 		}
638 		i = 0;
639 		for(j=0; j<y; j++){
640 			for(x=0; (c=data[i])!='\n'; ){
641 				if(strchr(" \t,{}", c)){
642 					i++;
643 					continue;
644 				}
645 				if(c=='0' && data[i+1] == 'x'){
646 					i += 2;
647 					continue;
648 				}
649 				if(strchr(hex, c)){
650 					buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
651 					i += 2;
652 					continue;
653 				}
654 			}
655 			i++;
656 			wrbitmap(b, j, j+1, buf);
657 		}
658 		free(data);
659 	}else{
660 		/*
661 		 * plain bitmap file
662 		 */
663 		face = 0;
664 		s = 0;
665 		b = rdbitmapfile(fd);
666 		if(b == 0){
667 			mesg("can't read bitmap file %s: %r", file);
668 			close(fd);
669 			goto Err;
670 		}
671 		if(seek(fd, 0, 1) < d.length){
672 			/* rdsubfontfile drops bitmap; must make a copy */
673 			tb = balloc(b->r, b->ldepth);
674 			if(tb == 0){
675 				mesg("can't balloc bitmap for file %s: %r", file);
676 				close(fd);
677 				goto Err;
678 			}
679 			bitblt(tb, tb->r.min, b, b->r, S);
680 			s = rdsubfontfile(fd, tb);
681 		}
682 	}
683 	close(fd);
684 	t = malloc(sizeof(Thing));
685 	if(t == 0){
686    nomem:
687 		mesg("malloc failed: %r");
688 		if(s)
689 			subffree(s);
690 		bfree(b);
691 		goto Err;
692 	}
693 	t->name = strdup(file);
694 	if(t->name == 0){
695 		free(t);
696 		goto nomem;
697 	}
698 	t->b = b;
699 	t->s = s;
700 	t->face = face;
701 	t->mod = 0;
702 	t->parent = 0;
703 	t->c = -1;
704 	t->mag = 1;
705 	t->off = 0;
706 	memmove(err, oerr, sizeof err);
707 	return t;
708 }
709 
710 int
711 atline(int x, Point p, char *line, char *buf)
712 {
713 	char *s, *c, *word, *hit;
714 	int w, wasblank;
715 	Rune r;
716 
717 	wasblank = 1;
718 	hit = 0;
719 	word = 0;
720 	for(s=line; *s; s+=w){
721 		w = chartorune(&r, s);
722 		x += charwidth(font, r);
723 		if(wasblank && r!=' ')
724 			word = s;
725 		wasblank = 0;
726 		if(r == ' '){
727 			if(x >= p.x)
728 				break;
729 			wasblank = 1;
730 		}
731 		if(r == ':')
732 			hit = word;
733 	}
734 	if(x < p.x)
735 		return 0;
736 	c = utfrune(hit, ':');
737 	strncpy(buf, hit, c-hit);
738 	buf[c-hit] = 0;
739 	return 1;
740 }
741 
742 int
743 attext(Thing *t, Point p, char *buf)
744 {
745 	char l0[256], l1[256];
746 
747 	if(!ptinrect(p, t->tr))
748 		return 0;
749 	stext(t, l0, l1);
750 	if(p.y < t->tr.min.y+font->height)
751 		return atline(t->r.min.x, p, l0, buf);
752 	else
753 		return atline(t->r.min.x, p, l1, buf);
754 }
755 
756 int
757 type(char *buf, char *tag)
758 {
759 	Rune r;
760 	char *p;
761 
762 	cursorswitch(&busy);
763 	p = buf;
764 	for(;;){
765 		*p = 0;
766 		mesg("%s: %s", tag, buf);
767 		r = ekbd();
768 		switch(r){
769 		case '\n':
770 			mesg("");
771 			cursorswitch(0);
772 			return p-buf;
773 		case 0x15:	/* control-U */
774 			p = buf;
775 			break;
776 		case '\b':
777 			if(p > buf)
778 				--p;
779 			break;
780 		default:
781 			p += runetochar(p, &r);
782 		}
783 	}
784 	return 0;	/* shut up compiler */
785 }
786 
787 void
788 textedit(Thing *t, char *tag)
789 {
790 	char buf[256];
791 	char *s;
792 	Bitmap *b;
793 	Subfont *f;
794 	Fontchar *fc, *nfc;
795 	Rectangle r;
796 	int i, ld, w, c, doredraw, fdx, x;
797 	Thing *nt;
798 
799 	buttons(Up);
800 	if(type(buf, tag) == 0)
801 		return;
802 	if(strcmp(tag, "file") == 0){
803 		for(s=buf; *s; s++)
804 			if(*s <= ' '){
805 				mesg("illegal file name");
806 				return;
807 			}
808 		if(strcmp(t->name, buf) != 0){
809 			if(t->parent)
810 				t->parent->mod = 1;
811 			else
812 				t->mod = 1;
813 		}
814 		for(nt=thing; nt; nt=nt->next)
815 			if(t==nt || t->parent==nt || nt->parent==t){
816 				free(nt->name);
817 				nt->name = strdup(buf);
818 				if(nt->name == 0){
819 	nomem:
820 					mesg("malloc failed: %r");
821 					return;
822 				}
823 				text(nt);
824 			}
825 		return;
826 	}
827 	if(strcmp(tag, "ldepth") == 0){
828 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>3){
829 			mesg("illegal ldepth");
830 			return;
831 		}
832 		if(ld == t->b->ldepth)
833 			return;
834 		if(t->parent)
835 			t->parent->mod = 1;
836 		else
837 			t->mod = 1;
838 		for(nt=thing; nt; nt=nt->next){
839 			if(nt!=t && nt!=t->parent && nt->parent!=t)
840 				continue;
841 			b = balloc(nt->b->r, ld);
842 			if(b == 0){
843 	nobmem:
844 				mesg("balloc failed: %r");
845 				return;
846 			}
847 			bitblt(b, b->r.min, nt->b, nt->b->r, S);
848 			bfree(nt->b);
849 			nt->b = b;
850 			if(nt->s){
851 				b = balloc(nt->b->r, ld);
852 				if(b == 0)
853 					goto nobmem;
854 				bitblt(b, b->r.min, nt->b, nt->b->r, S);
855 				f = subfalloc(nt->s->n, nt->s->height, nt->s->ascent,
856 					nt->s->info, b, ~0, ~0);
857 				if(f == 0){
858 	nofmem:
859 					mesg("can't make subfont: %r");
860 					return;
861 				}
862 				subffree(nt->s);
863 				nt->s = f;
864 			}
865 			draw(nt, 0);
866 		}
867 		return;
868 	}
869 	if(strcmp(tag, "mag") == 0){
870 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
871 			mesg("illegal magnification");
872 			return;
873 		}
874 		if(t->mag == ld)
875 			return;
876 		t->mag = ld;
877 		redraw(t);
878 		return;
879 	}
880 	if(strcmp(tag, "ascent") == 0){
881 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
882 			mesg("illegal ascent");
883 			return;
884 		}
885 		if(t->s->ascent == ld)
886 			return;
887 		t->s->ascent = ld;
888 		text(t);
889 		t->mod = 1;
890 		return;
891 	}
892 	if(strcmp(tag, "height") == 0){
893 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
894 			mesg("illegal height");
895 			return;
896 		}
897 		if(t->s->height == ld)
898 			return;
899 		t->s->height = ld;
900 		text(t);
901 		t->mod = 1;
902 		return;
903 	}
904 	if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
905 		if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
906 			mesg("illegal value");
907 			return;
908 		}
909 		fc = &t->parent->s->info[t->c];
910 		if(strcmp(tag, "left")==0){
911 			if(fc->left == ld)
912 				return;
913 			fc->left = ld;
914 		}else{
915 			if(fc->width == ld)
916 				return;
917 			fc->width = ld;
918 		}
919 		text(t);
920 		t->parent->mod = 1;
921 		return;
922 	}
923 	if(strcmp(tag, "offset(hex)") == 0){
924 		if(!strchr(hex, buf[0])){
925 	illoff:
926 			mesg("illegal offset");
927 			return;
928 		}
929 		s = 0;
930 		ld = strtoul(buf, &s, 16);
931 		if(*s)
932 			goto illoff;
933 		t->off = ld;
934 		text(t);
935 		for(nt=thing; nt; nt=nt->next)
936 			if(nt->parent == t)
937 				text(nt);
938 		return;
939 	}
940 	if(strcmp(tag, "n") == 0){
941 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
942 			mesg("illegal n");
943 			return;
944 		}
945 		f = t->s;
946 		if(w == f->n)
947 			return;
948 		doredraw = 0;
949 	again:
950 		for(nt=thing; nt; nt=nt->next)
951 			if(nt->parent == t){
952 				doredraw = 1;
953 				tclose1(nt);
954 				goto again;
955 			}
956 		r = t->b->r;
957 		if(w < f->n)
958 			r.max.x = f->info[w].x;
959 		b = balloc(r, t->b->ldepth);
960 		if(b == 0)
961 			goto nobmem;
962 		bitblt(b, b->r.min, t->b, r, S);
963 		fdx = Dx(editr) - 2*Border;
964 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
965 			doredraw = 1;
966 		bfree(t->b);
967 		t->b = b;
968 		b = balloc(r, t->b->ldepth);
969 		if(b == 0)
970 			goto nobmem;
971 		bitblt(b, b->r.min, t->b, r, S);
972 		nfc = malloc((w+1)*sizeof(Fontchar));
973 		if(nfc == 0){
974 			mesg("malloc failed");
975 			bfree(b);
976 			return;
977 		}
978 		fc = f->info;
979 		for(i=0; i<=w && i<=f->n; i++)
980 			nfc[i] = fc[i];
981 		if(w+1 < i)
982 			memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
983 		x = fc[f->n].x;
984 		for(; i<=w; i++)
985 			nfc[i].x = x;
986 		f = subfalloc(w, f->height, f->ascent, nfc, b, ~0, ~0);
987 		if(f == 0)
988 			goto nofmem;
989 		subffree(t->s);
990 		f->info = nfc;
991 		t->s = f;
992 		if(doredraw)
993 			redraw(thing);
994 		else
995 			draw(t, 0);
996 		t->mod = 1;
997 		return;
998 	}
999 	if(strcmp(tag, "iwidth") == 0){
1000 		if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1001 			mesg("illegal iwidth");
1002 			return;
1003 		}
1004 		w -= Dx(t->b->r);
1005 		if(w == 0)
1006 			return;
1007 		r = t->parent->b->r;
1008 		r.max.x += w;
1009 		c = t->c;
1010 		t = t->parent;
1011 		f = t->s;
1012 		b = balloc(r, t->b->ldepth);
1013 		if(b == 0)
1014 			goto nobmem;
1015 		fc = &f->info[c];
1016 		bitblt(b, b->r.min, t->b,
1017 			Rect(t->b->r.min.x, t->b->r.min.y,
1018 			fc[1].x, t->b->r.max.y), S);
1019 		bitblt(b, Pt(fc[1].x+w, b->r.min.y), t->b,
1020 			Rect(fc[1].x, t->b->r.min.y,
1021 			t->b->r.max.x, t->b->r.max.y), S);
1022 		fdx = Dx(editr) - 2*Border;
1023 		doredraw = 0;
1024 		if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1025 			doredraw = 1;
1026 		bfree(t->b);
1027 		t->b = b;
1028 		b = balloc(r, t->b->ldepth);
1029 		if(b == 0)
1030 			goto nobmem;
1031 		bitblt(b, b->r.min, t->b, t->b->r, S);
1032 		fc = &f->info[c+1];
1033 		for(i=c+1; i<=f->n; i++, fc++)
1034 			fc->x += w;
1035 		f = subfalloc(f->n, f->height, f->ascent,
1036 			f->info, b, ~0, ~0);
1037 		if(f == 0)
1038 			goto nofmem;
1039 		/* t->s and f share info; free carefully */
1040 		fc = f->info;
1041 		t->s->info = 0;
1042 		subffree(t->s);
1043 		f->info = fc;
1044 		t->s = f;
1045 		if(doredraw)
1046 			redraw(t);
1047 		else
1048 			draw(t, 0);
1049 		/* redraw all affected chars */
1050 		for(nt=thing; nt; nt=nt->next){
1051 			if(nt->parent!=t || nt->c<c)
1052 				continue;
1053 			fc = &f->info[nt->c];
1054 			r.min.x = fc[0].x;
1055 			r.min.y = nt->b->r.min.y;
1056 			r.max.x = fc[1].x;
1057 			r.max.y = nt->b->r.max.y;
1058 			b = balloc(r, nt->b->ldepth);
1059 			if(b == 0)
1060 				goto nobmem;
1061 			bitblt(b, r.min, t->b, r, S);
1062 			doredraw = 0;
1063 			if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
1064 				doredraw = 1;
1065 			bfree(nt->b);
1066 			nt->b = b;
1067 			if(c != nt->c)
1068 				text(nt);
1069 			else{
1070 				if(doredraw)
1071 					redraw(nt);
1072 				else
1073 					draw(nt, 0);
1074 			}
1075 		}
1076 		t->mod = 1;
1077 		return;
1078 	}
1079 	mesg("cannot edit %s in file %s", tag, t->name);
1080 }
1081 
1082 void
1083 cntledit(char *tag)
1084 {
1085 	char buf[256];
1086 	char *p, *q;
1087 	ulong l;
1088 	int i, inv;
1089 
1090 	buttons(Up);
1091 	if(type(buf, tag) == 0)
1092 		return;
1093 	if(strcmp(tag, "mag") == 0){
1094 		if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
1095 			mesg("illegal magnification");
1096 			return;
1097 		}
1098 		mag = l;
1099 		cntl();
1100 		return;
1101 	}
1102 	if(strcmp(tag, "val(hex)") == 0){
1103 		inv = 0;
1104 		p = buf;
1105 		if(*p == '~'){
1106 			p++;
1107 			inv = 1;
1108 		}
1109 		l = strtoul(p, &q, 16);
1110 		if(l==0 && q==p){
1111 			mesg("illegal magnification");
1112 			return;
1113 		}
1114 		if(inv)
1115 			l = ~l;
1116 		val = l;
1117 		cntl();
1118 		return;
1119 	}
1120 	if(strcmp(tag, "but1")==0
1121 	|| strcmp(tag, "but2")==0
1122 	|| strcmp(tag, "copy")==0){
1123 		i = strtofn(buf);
1124 		if(i < 0){
1125 			mesg("unknown function");
1126 			return;
1127 		}
1128 		if(strcmp(tag, "but1") == 0)
1129 			but1fn = i;
1130 		else if(strcmp(tag, "but2") == 0)
1131 			but2fn = i;
1132 		else
1133 			copyfn = i;
1134 		cntl();
1135 		return;
1136 	}
1137 	mesg("cannot edit %s", tag);
1138 }
1139 
1140 void
1141 buttons(int ud)
1142 {
1143 	while((mouse.buttons==0) != ud)
1144 		mouse = emouse();
1145 }
1146 
1147 int
1148 sweep(int butmask, Rectangle *r)
1149 {
1150 	Point p;
1151 
1152 	cursorswitch(&sweep0);
1153 	buttons(Down);
1154 	if(mouse.buttons != butmask){
1155 		buttons(Up);
1156 		cursorswitch(0);
1157 		return 0;
1158 	}
1159 	p = mouse.xy;
1160 	cursorswitch(&box);
1161 	r->min = p;
1162 	r->max = p;
1163 	while(mouse.buttons == butmask){
1164 		border(&screen, *r, -2, ~D);
1165 		bflush();
1166 		mouse = emouse();
1167 		border(&screen, *r, -2, ~D);
1168 		*r = rcanon(Rpt(p, mouse.xy));
1169 	}
1170 	cursorswitch(0);
1171 	if(mouse.buttons){
1172 		buttons(Up);
1173 		return 0;
1174 	}
1175 	return 1;
1176 }
1177 
1178 void
1179 openedit(Thing *t, Point pt, int c)
1180 {
1181 	int x, y;
1182 	Point p;
1183 	Rectangle r;
1184 	Rectangle br;
1185 	Fontchar *fc;
1186 	Thing *nt;
1187 
1188 	br = t->b->r;
1189 	if(t->s == 0){
1190 		c = -1;
1191 		/* if big enough to bother, sweep box */
1192 		if(Dx(br)<=16 && Dy(br)<=16)
1193 			r = br;
1194 		else{
1195 			if(!sweep(1, &r))
1196 				return;
1197 			r = raddp(r, sub(br.min, t->r.min));
1198 			if(!rectclip(&r, br))
1199 				return;
1200 			if(Dx(br) <= 8){
1201 				r.min.x = br.min.x;
1202 				r.max.x = br.max.x;
1203 			}else if(Dx(r) < 4){
1204 	    toosmall:
1205 				mesg("rectangle too small");
1206 				return;
1207 			}
1208 			if(Dy(br) <= 8){
1209 				r.min.y = br.min.y;
1210 				r.max.y = br.max.y;
1211 			}else if(Dy(r) < 4)
1212 				goto toosmall;
1213 		}
1214 	}else if(c >= 0){
1215 		fc = &t->s->info[c];
1216 		r.min.x = fc[0].x;
1217 		r.min.y = br.min.y;
1218 		r.max.x = fc[1].x;
1219 		r.max.y = br.min.y + Dy(br);
1220 	}else{
1221 		/* just point at character */
1222 		fc = t->s->info;
1223 		p = add(pt, sub(br.min, t->r.min));
1224 		x = br.min.x;
1225 		y = br.min.y;
1226 		for(c=0; c<t->s->n; c++,fc++){
1227 	    again:
1228 			r.min.x = x;
1229 			r.min.y = y;
1230 			r.max.x = x + fc[1].x - fc[0].x;
1231 			r.max.y = y + Dy(br);
1232 			if(ptinrect(p, r))
1233 				goto found;
1234 			if(r.max.x >= br.min.x+Dx(t->r)){
1235 				x -= Dx(t->r);
1236 				y += t->s->height;
1237 				if(fc[1].x > fc[0].x)
1238 					goto again;
1239 			}
1240 			x += fc[1].x - fc[0].x;
1241 		}
1242 		return;
1243 	   found:
1244 		r = br;
1245 		r.min.x = fc[0].x;
1246 		r.max.x = fc[1].x;
1247 	}
1248 	nt = malloc(sizeof(Thing));
1249 	if(nt == 0){
1250    nomem:
1251 		mesg("can't allocate: %r");
1252 		return;
1253 	}
1254 	memset(nt, 0, sizeof(Thing));
1255 	nt->c = c;
1256 	nt->b = balloc(r, t->b->ldepth);
1257 	if(nt->b == 0){
1258 		free(nt);
1259 		goto nomem;
1260 	}
1261 	bitblt(nt->b, r.min, t->b, r, S);
1262 	nt->name = strdup(t->name);
1263 	if(nt->name == 0){
1264 		bfree(nt->b);
1265 		free(nt);
1266 		goto nomem;
1267 	}
1268 	nt->parent = t;
1269 	nt->mag = mag;
1270 	draw(nt, 1);
1271 }
1272 
1273 void
1274 ckinfo(Thing *t, Rectangle mod)
1275 {
1276 	int i, j, k, top, bot, n, zero;
1277 	Fontchar *fc;
1278 	Rectangle r;
1279 	Bitmap *b;
1280 	Thing *nt;
1281 
1282 	if(t->parent)
1283 		t = t->parent;
1284 	if(t->s==0 || Dy(t->b->r)==0)
1285 		return;
1286 	b = 0;
1287 	/* check bounding boxes */
1288 	fc = &t->s->info[0];
1289 	r.min.y = t->b->r.min.y;
1290 	r.max.y = t->b->r.max.y;
1291 	for(i=0; i<t->s->n; i++, fc++){
1292 		r.min.x = fc[0].x;
1293 		r.max.x = fc[1].x;
1294 		if(!rectXrect(mod, r))
1295 			continue;
1296 		if(b==0 || Dx(b->r)<Dx(r)){
1297 			if(b)
1298 				bfree(b);
1299 			b = balloc(rsubp(r, r.min), t->b->ldepth);
1300 			if(b == 0){
1301 				mesg("can't balloc");
1302 				break;
1303 			}
1304 		}
1305 		bitblt(b, b->r.min, b, b->r, 0);
1306 		bitblt(b, b->r.min, t->b, r, S);
1307 		top = 100000;
1308 		bot = 0;
1309 		n = 2+(Dx(r)/8<<t->b->ldepth);
1310 		for(j=0; j<b->r.max.y; j++){
1311 			memset(data, 0, n);
1312 			rdbitmap(b, j, j+1, data);
1313 			zero = 1;
1314 			for(k=0; k<n; k++)
1315 				if(data[k]){
1316 					zero = 0;
1317 					break;
1318 				}
1319 			if(!zero){
1320 				if(top > j)
1321 					top = j;
1322 				bot = j+1;
1323 			}
1324 		}
1325 		if(top > j)
1326 			top = 0;
1327 		if(top!=fc->top || bot!=fc->bottom){
1328 			fc->top = top;
1329 			fc->bottom = bot;
1330 			for(nt=thing; nt; nt=nt->next)
1331 				if(nt->parent==t && nt->c==i)
1332 					text(nt);
1333 		}
1334 	}
1335 	if(b)
1336 		bfree(b);
1337 }
1338 
1339 Point
1340 screenpt(Thing *t, Point realp)
1341 {
1342 	int fdx, n;
1343 	Point p;
1344 
1345 	fdx = Dx(editr)-2*Border;
1346 	if(t->mag > 1)
1347 		fdx -= fdx%t->mag;
1348 	p = mul(sub(realp, t->b->r.min), t->mag);
1349 	if(fdx < Dx(t->b->r)*t->mag){
1350 		n = p.x/fdx;
1351 		p.y += n * (Dy(t->b->r)*t->mag+Border);
1352 		p.x -= n * fdx;
1353 	}
1354 	p = add(p, t->r.min);
1355 	return p;
1356 }
1357 
1358 Point
1359 realpt(Thing *t, Point screenp)
1360 {
1361 	int fdx, n, dy;
1362 	Point p;
1363 
1364 	fdx = (Dx(editr)-2*Border);
1365 	if(t->mag > 1)
1366 		fdx -= fdx%t->mag;
1367 	p.y = screenp.y-t->r.min.y;
1368 	p.x = 0;
1369 	if(fdx < Dx(t->b->r)*t->mag){
1370 		dy = Dy(t->b->r)*t->mag+Border;
1371 		n = (p.y/dy);
1372 		p.x = n * fdx;
1373 		p.y -= n * dy;
1374 	}
1375 	p.x += screenp.x-t->r.min.x;
1376 	p = add(div(p, t->mag), t->b->r.min);
1377 	return p;
1378 }
1379 
1380 void
1381 twidpix(Thing *t, Point p, int set)
1382 {
1383 	Bitmap *b;
1384 	int v, c;
1385 
1386 	c = but1fn;
1387 	if(!set)
1388 		c = but2fn;
1389 	b = t->b;
1390 	if(!ptinrect(p, b->r))
1391 		return;
1392 	point(b, p, val, c);
1393 	v = bvalue(val, b->ldepth);
1394 	p = screenpt(t, p);
1395 	bitblt(&screen, p, values, Rect(v, 0, v+t->mag, t->mag), c);
1396 }
1397 
1398 void
1399 twiddle(Thing *t)
1400 {
1401 	int set;
1402 	Point p, lastp;
1403 	Bitmap *b;
1404 	Thing *nt;
1405 	Rectangle mod;
1406 
1407 	if(mouse.buttons!=1 && mouse.buttons!=2){
1408 		buttons(Up);
1409 		return;
1410 	}
1411 	set = mouse.buttons==1;
1412 	b = t->b;
1413 	lastp = add(b->r.min, Pt(-1, -1));
1414 	mod = Rpt(add(b->r.max, Pt(1, 1)), lastp);
1415 	while(mouse.buttons){
1416 		p = realpt(t, mouse.xy);
1417 		if(!eqpt(p, lastp)){
1418 			lastp = p;
1419 			if(ptinrect(p, b->r)){
1420 				for(nt=thing; nt; nt=nt->next)
1421 					if(nt->parent==t->parent || nt==t->parent)
1422 						twidpix(nt, p, set);
1423 				if(t->parent)
1424 					t->parent->mod = 1;
1425 				else
1426 					t->mod = 1;
1427 				if(p.x < mod.min.x)
1428 					mod.min.x = p.x;
1429 				if(p.y < mod.min.y)
1430 					mod.min.y = p.y;
1431 				if(p.x >= mod.max.x)
1432 					mod.max.x = p.x+1;
1433 				if(p.y >= mod.max.y)
1434 					mod.max.y = p.y+1;
1435 			}
1436 		}
1437 		mouse = emouse();
1438 	}
1439 	ckinfo(t, mod);
1440 }
1441 
1442 void
1443 select(void)
1444 {
1445 	Thing *t;
1446 	char line[128], buf[128];
1447 	Point p;
1448 
1449 	if(ptinrect(mouse.xy, cntlr)){
1450 		scntl(line);
1451 		if(atline(cntlr.min.x, mouse.xy, line, buf)){
1452 			if(mouse.buttons == 1)
1453 				cntledit(buf);
1454 			else
1455 				buttons(Up);
1456 			return;
1457 		}
1458 		return;
1459 	}
1460 	for(t=thing; t; t=t->next){
1461 		if(attext(t, mouse.xy, buf)){
1462 			if(mouse.buttons == 1)
1463 				textedit(t, buf);
1464 			else
1465 				buttons(Up);
1466 			return;
1467 		}
1468 		if(ptinrect(mouse.xy, t->r)){
1469 			if(t->parent == 0){
1470 				if(mouse.buttons == 1){
1471 					p = mouse.xy;
1472 					buttons(Up);
1473 					openedit(t, p, -1);
1474 				}else
1475 					buttons(Up);
1476 				return;
1477 			}
1478 			twiddle(t);
1479 			return;
1480 		}
1481 	}
1482 }
1483 
1484 void
1485 twrite(Thing *t)
1486 {
1487 	int i, j, x, y, fd, ws, ld;
1488 	Biobuf buf;
1489 	Rectangle r;
1490 
1491 	if(t->parent)
1492 		t = t->parent;
1493 	cursorswitch(&busy);
1494 	fd = create(t->name, OWRITE, 0666);
1495 	if(fd < 0){
1496 		mesg("can't write %s: %r", t->name);
1497 		return;
1498 	}
1499 	if(t->face){
1500 		r = t->b->r;
1501 		ld = t->b->ldepth;
1502 		/* This heuristic reflects peculiarly different formats */
1503 		ws = 4;
1504 		if(Dx(r) == 16)	/* probably cursor file */
1505 			ws = 1;
1506 		else if(Dx(r)<32 || ld==0)
1507 			ws = 2;
1508 		Binit(&buf, fd, OWRITE);
1509 		for(y=r.min.y; y<r.max.y; y++){
1510 			rdbitmap(t->b, y, y+1, data);
1511 			j = 0;
1512 			for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
1513 				Bprint(&buf, "0x");
1514 				for(i=0; i<ws; i++)
1515 					Bprint(&buf, "%.2x", data[i+j]);
1516 				Bprint(&buf, ", ");
1517 			}
1518 			Bprint(&buf, "\n");
1519 		}
1520 		Bterm(&buf);
1521 	}else{
1522 		wrbitmapfile(fd, t->b);
1523 		if(t->s)
1524 			wrsubfontfile(fd, t->s);
1525 	}
1526 	t->mod = 0;
1527 	close(fd);
1528 	mesg("wrote %s", t->name);
1529 }
1530 
1531 void
1532 tclose1(Thing *t)
1533 {
1534 	Thing *nt;
1535 
1536 	if(t == thing)
1537 		thing = t->next;
1538 	else{
1539 		for(nt=thing; nt->next!=t; nt=nt->next)
1540 			;
1541 		nt->next = t->next;
1542 	}
1543 	do
1544 		for(nt=thing; nt; nt=nt->next)
1545 			if(nt->parent == t){
1546 				tclose1(nt);
1547 				break;
1548 			}
1549 	while(nt);
1550 	if(t->s)
1551 		subffree(t->s);
1552 	bfree(t->b);
1553 	free(t->name);
1554 	free(t);
1555 }
1556 
1557 void
1558 tclose(Thing *t)
1559 {
1560 	Thing *ct;
1561 
1562 	if(t->mod){
1563 		mesg("%s modified", t->name);
1564 		t->mod = 0;
1565 		return;
1566 	}
1567 	/* fiddle to save redrawing unmoved things */
1568 	if(t == thing)
1569 		ct = 0;
1570 	else
1571 		for(ct=thing; ct; ct=ct->next)
1572 			if(ct->next==t || ct->next->parent==t)
1573 				break;
1574 	tclose1(t);
1575 	if(ct)
1576 		ct = ct->next;
1577 	else
1578 		ct = thing;
1579 	redraw(ct);
1580 }
1581 
1582 void
1583 tread(Thing *t)
1584 {
1585 	Thing *nt, *new;
1586 	Fontchar *i;
1587 	Rectangle r;
1588 	int nclosed;
1589 
1590 	if(t->parent)
1591 		t = t->parent;
1592 	new = tget(t->name);
1593 	if(new == 0)
1594 		return;
1595 	nclosed = 0;
1596     again:
1597 	for(nt=thing; nt; nt=nt->next)
1598 		if(nt->parent == t){
1599 			if(!rectinrect(nt->b->r, new->b->r)
1600 			|| new->b->ldepth!=nt->b->ldepth){
1601     closeit:
1602 				nclosed++;
1603 				nt->parent = 0;
1604 				tclose1(nt);
1605 				goto again;
1606 			}
1607 			if((t->s==0) != (new->s==0))
1608 				goto closeit;
1609 			if((t->face==0) != (new->face==0))
1610 				goto closeit;
1611 			if(t->s){	/* check same char */
1612 				if(nt->c >= new->s->n)
1613 					goto closeit;
1614 				i = &new->s->info[nt->c];
1615 				r.min.x = i[0].x;
1616 				r.max.x = i[1].x;
1617 				r.min.y = new->b->r.min.y;
1618 				r.max.y = new->b->r.max.y;
1619 				if(!eqrect(r, nt->b->r))
1620 					goto closeit;
1621 			}
1622 			nt->parent = new;
1623 			bitblt(nt->b, nt->b->r.min, new->b,
1624 				nt->b->r, S);
1625 		}
1626 	new->next = t->next;
1627 	if(t == thing)
1628 		thing = new;
1629 	else{
1630 		for(nt=thing; nt->next!=t; nt=nt->next)
1631 			;
1632 		nt->next = new;
1633 	}
1634 	if(t->s)
1635 		subffree(t->s);
1636 	bfree(t->b);
1637 	free(t->name);
1638 	free(t);
1639 	for(nt=thing; nt; nt=nt->next)
1640 		if(nt==new || nt->parent==new)
1641 			if(nclosed == 0)
1642 				draw(nt, 0);	/* can draw in place */
1643 			else{
1644 				redraw(nt);	/* must redraw all below */
1645 				break;
1646 			}
1647 }
1648 
1649 void
1650 tchar(Thing *t)
1651 {
1652 	char buf[256], *p;
1653 	Rune r;
1654 	ulong c, d;
1655 
1656 	if(t->s == 0){
1657 		mesg("not a subfont");
1658 		return;
1659 	}
1660 	if(type(buf, "char (hex or character or hex-hex)") == 0)
1661 		return;
1662 	if(utflen(buf) == 1){
1663 		chartorune(&r, buf);
1664 		c = r;
1665 		d = r;
1666 	}else{
1667 		if(!strchr(hex, buf[0])){
1668 			mesg("illegal hex character");
1669 			return;
1670 		}
1671 		c = strtoul(buf, 0, 16);
1672 		d = c;
1673 		p = utfrune(buf, '-');
1674 		if(p){
1675 			d = strtoul(p+1, 0, 16);
1676 			if(d < c){
1677 				mesg("invalid range");
1678 				return;
1679 			}
1680 		}
1681 	}
1682 	c -= t->off;
1683 	d -= t->off;
1684 	while(c <= d){
1685 		if(c<0 || c>=t->s->n){
1686 			mesg("0x%lux not in font %s", c+t->off, t->name);
1687 			return;
1688 		}
1689 		openedit(t, Pt(0, 0), c);
1690 		c++;
1691 	}
1692 }
1693 
1694 void
1695 apply(void (*f)(Thing*))
1696 {
1697 	Thing *t;
1698 
1699 	cursorswitch(&sight);
1700 	buttons(Down);
1701 	if(mouse.buttons == 4)
1702 		for(t=thing; t; t=t->next)
1703 			if(ptinrect(mouse.xy, t->er)){
1704 				buttons(Up);
1705 				f(t);
1706 				break;
1707 			}
1708 	buttons(Up);
1709 	cursorswitch(0);
1710 }
1711 
1712 void
1713 copy(void)
1714 {
1715 	Thing *st, *dt, *nt;
1716 	Rectangle sr, dr, fr;
1717 	Point p1, p2;
1718 	int but, up;
1719 
1720 	if(!sweep(4, &sr))
1721 		return;
1722 	for(st=thing; st; st=st->next)
1723 		if(rectXrect(sr, st->r))
1724 			break;
1725 	if(st == 0)
1726 		return;
1727 	/* click gives full rectangle */
1728 	if(Dx(sr)<4 && Dy(sr)<4)
1729 		sr = st->r;
1730 	rectclip(&sr, st->r);
1731 	p1 = realpt(st, sr.min);
1732 	p2 = realpt(st, Pt(sr.min.x, sr.max.y));
1733 	if(p1.x != p2.x){	/* swept across a fold */
1734    onafold:
1735 		mesg("sweep spans a fold");
1736 		return;
1737 	}
1738 	p2 = realpt(st, sr.max);
1739 	sr.min = p1;
1740 	sr.max = p2;
1741 	fr.min = screenpt(st, sr.min);
1742 	fr.max = screenpt(st, sr.max);
1743 	border(&screen, fr, -3, ~D);
1744 	p1 = sub(p2, p1);	/* diagonal */
1745 	if(p1.x==0 || p1.y==0){
1746     Return:
1747 		border(&screen, fr, -3, ~D);
1748 		return;
1749 	}
1750 	cursorswitch(&box);
1751 	up = 0;
1752 	for(; mouse.buttons==0; mouse=emouse()){
1753 		for(dt=thing; dt; dt=dt->next)
1754 			if(ptinrect(mouse.xy, dt->er))
1755 				break;
1756 		if(up)
1757 			border(&screen, dr, -2, ~D);
1758 		up = 0;
1759 		if(dt == 0)
1760 			continue;
1761 		dr.max = screenpt(dt, realpt(dt, mouse.xy));
1762 		dr.min = sub(dr.max, mul(p1, dt->mag));
1763 		if(!rectXrect(dr, dt->r))
1764 			continue;
1765 		border(&screen, dr, -2, ~D);
1766 		up = 1;
1767 	}
1768 	/* if up==1, we had a hit */
1769 	cursorswitch(0);
1770 	if(up)
1771 		border(&screen, dr, -2, ~D);
1772 	but = mouse.buttons;
1773 	border(&screen, fr, -3, ~D);
1774 	buttons(Up);
1775 	if(!up || but!=4)
1776 		return;
1777 	dt = 0;
1778 	for(nt=thing; nt; nt=nt->next)
1779 		if(rectXrect(dr, nt->r)){
1780 			if(dt){
1781 				mesg("ambiguous sweep");
1782 				return;
1783 			}
1784 			dt = nt;
1785 		}
1786 	if(dt == 0)
1787 		return;
1788 	p1 = realpt(dt, dr.min);
1789 	p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
1790 	if(p1.x != p2.x)
1791 		goto onafold;
1792 	p2 = realpt(dt, dr.max);
1793 	dr.min = p1;
1794 	dr.max = p2;
1795 	bitblt(dt->b, dr.min, st->b, sr, copyfn);
1796 	if(dt->parent){
1797 		bitblt(dt->parent->b, dr.min, dt->b, dr, S);
1798 		dt = dt->parent;
1799 	}
1800 	draw(dt, 0);
1801 	for(nt=thing; nt; nt=nt->next)
1802 		if(nt->parent==dt && rectXrect(dr, nt->b->r)){
1803 			bitblt(nt->b, dr.min, dt->b, dr, S);
1804 			draw(nt, 0);
1805 		}
1806 	ckinfo(dt, dr);
1807 	dt->mod = 1;
1808 }
1809 
1810 void
1811 menu(void)
1812 {
1813 	Thing *t;
1814 	char *mod;
1815 	int sel;
1816 	char buf[256];
1817 
1818 	sel = menuhit(3, &mouse, &menu3);
1819 	switch(sel){
1820 	case Mopen:
1821 		if(type(buf, "file")){
1822 			t = tget(buf);
1823 			if(t)
1824 				draw(t, 1);
1825 		}
1826 		break;
1827 	case Mwrite:
1828 		apply(twrite);
1829 		break;
1830 	case Mread:
1831 		apply(tread);
1832 		break;
1833 	case Mchar:
1834 		apply(tchar);
1835 		break;
1836 	case Mcopy:
1837 		copy();
1838 		break;
1839 	case Mclose:
1840 		apply(tclose);
1841 		break;
1842 	case Mexit:
1843 		mod = 0;
1844 		for(t=thing; t; t=t->next)
1845 			if(t->mod){
1846 				mod = t->name;
1847 				t->mod = 0;
1848 			}
1849 		if(mod){
1850 			mesg("%s modified", mod);
1851 			break;
1852 		}
1853 		cursorswitch(&skull);
1854 		buttons(Down);
1855 		if(mouse.buttons == 4){
1856 			buttons(Up);
1857 			exits(0);
1858 		}
1859 		buttons(Up);
1860 		cursorswitch(0);
1861 		break;
1862 	}
1863 }
1864 
1865 char*
1866 fntostr(uint f)
1867 {
1868 	char **s;
1869 	int i;
1870 
1871 	f &= F;
1872 	s = fns;
1873 	for(i=0; i<f; i++)
1874 		do; while(*s++);
1875 	return *s;
1876 }
1877 
1878 int
1879 strtofn(char *str)
1880 {
1881 	char **s;
1882 	int i;
1883 
1884 	s = fns;
1885 	i = 0;
1886 	while(i <= F){
1887 		if(*s == 0)
1888 			i++;
1889 		else
1890 			if(strcmp(str, *s) == 0)
1891 				return i;
1892 		s++;
1893 	}
1894 	return -1;
1895 }
1896