Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_fixcpp/sys/src/cmd/tweak.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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