Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <cursor.h>
5 #include <event.h>
6 #include <bio.h>
8 typedef struct Thing Thing;
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 };
27 enum
28 {
29 Border = 1,
30 Up = 1,
31 Down = 0,
32 Mag = 4,
33 Maxmag = 10,
34 };
36 enum
37 {
38 NORMAL =0,
39 FACE =1,
40 CURSOR =2
41 };
43 enum
44 {
45 Mopen,
46 Mread,
47 Mwrite,
48 Mcopy,
49 Mchar,
50 Mpixels,
51 Mclose,
52 Mexit,
53 };
55 enum
56 {
57 Blue = 54,
58 };
60 char *menu3str[] = {
61 "open",
62 "read",
63 "write",
64 "copy",
65 "char",
66 "pixels",
67 "close",
68 "exit",
69 0,
70 };
72 Menu menu3 = {
73 menu3str
74 };
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 };
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 };
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 };
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 };
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 };
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 };
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];
164 Thing* tget(char*);
165 void mesg(char*, ...);
166 void drawthing(Thing*, int);
167 void xselect(void);
168 void menu(void);
169 void error(Display*, char*);
170 void buttons(int);
171 void drawall(void);
172 void tclose1(Thing*);
174 void
175 usage(void)
177 fprint(2, "usage: tweak [-W winsize] file...\n");
178 exits("usage");
181 void
182 main(volatile int argc, char **volatile argv)
184 volatile int i;
185 Event e;
186 Thing *t;
188 ARGBEGIN{
189 case 'W':
190 winsize = EARGF(usage());
191 break;
192 default:
193 usage();
194 }ARGEND
195 mag = Mag;
196 if(initdraw(error, 0, "tweak") < 0){
197 fprint(2, "tweak: initdraw failed: %r\n");
198 exits("initdraw");
200 for(i=0; i<256; i++){
201 values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i));
202 greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF);
203 if(values[i] == 0 || greyvalues[i] == 0)
204 drawerror(display, "can't allocate image");
206 einit(Emouse|Ekeyboard);
207 eresized(0);
208 i = 0;
209 setjmp(err);
210 for(; i<argc; i++){
211 file = argv[i];
212 t = tget(argv[i]);
213 if(t)
214 drawthing(t, 1);
215 flushimage(display, 1);
217 file = 0;
218 setjmp(err);
219 for(;;)
220 switch(event(&e)){
221 case Ekeyboard:
222 break;
223 case Emouse:
224 mouse = e.mouse;
225 if(mouse.buttons & 3){
226 xselect();
227 break;
229 if(mouse.buttons & 4)
230 menu();
234 int
235 xlog2(int n)
237 int i;
239 for(i=0; (1<<i) <= n; i++)
240 if((1<<i) == n)
241 return i;
242 fprint(2, "log2 %d = 0\n", n);
243 return 0;
246 void
247 error(Display *d, char *s)
249 USED(d);
251 if(file)
252 mesg("can't read %s: %s: %r", file, s);
253 else
254 mesg("/dev/bitblt error: %s", s);
255 if(err[0])
256 longjmp(err, 1);
257 exits(s);
260 void
261 redraw(Thing *t)
263 Thing *nt;
264 Point p;
266 if(thing==0 || thing==t)
267 draw(screen, editr, display->white, nil, ZP);
268 if(thing == 0)
269 return;
270 if(thing != t){
271 for(nt=thing; nt->next!=t; nt=nt->next)
273 draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y),
274 display->white, nil, ZP);
276 for(nt=t; nt; nt=nt->next){
277 drawthing(nt, 0);
278 if(nt->next == 0){
279 p = Pt(editr.min.x, nt->er.max.y);
280 draw(screen, Rpt(p, editr.max), display->white, nil, ZP);
283 mesg("");
286 void
287 eresized(int new)
289 if(new && getwindow(display, Refnone) < 0)
290 error(display, "can't reattach to window");
291 cntlr = insetrect(screen->clipr, 1);
292 editr = cntlr;
293 textr = editr;
294 textr.min.y = textr.max.y - font->height;
295 cntlr.max.y = cntlr.min.y + font->height;
296 editr.min.y = cntlr.max.y+1;
297 editr.max.y = textr.min.y-1;
298 draw(screen, screen->clipr, display->white, nil, ZP);
299 draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP);
300 replclipr(screen, 0, editr);
301 drawall();
304 void
305 mesgstr(Point p, int line, char *s)
307 Rectangle c, r;
309 r.min = p;
310 r.min.y += line*font->height;
311 r.max.y = r.min.y+font->height;
312 r.max.x = editr.max.x;
313 c = screen->clipr;
314 replclipr(screen, 0, r);
315 draw(screen, r, values[0xDD], nil, ZP);
316 r.min.x++;
317 string(screen, r.min, display->black, ZP, font, s);
318 replclipr(screen, 0, c);
319 flushimage(display, 1);
322 void
323 mesg(char *fmt, ...)
325 char buf[1024];
326 va_list arg;
328 va_start(arg, fmt);
329 vseprint(buf, buf+sizeof(buf), fmt, arg);
330 va_end(arg);
331 mesgstr(textr.min, 0, buf);
334 void
335 tmesg(Thing *t, int line, char *fmt, ...)
337 char buf[1024];
338 va_list arg;
340 va_start(arg, fmt);
341 vseprint(buf, buf+sizeof(buf), fmt, arg);
342 va_end(arg);
343 mesgstr(t->tr.min, line, buf);
347 void
348 scntl(char *l)
350 sprint(l, "mag: %d but1: %d but2: %d invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]);
353 void
354 cntl(void)
356 char buf[256];
358 scntl(buf);
359 mesgstr(cntlr.min, 0, buf);
362 void
363 stext(Thing *t, char *l0, char *l1)
365 Fontchar *fc;
366 char buf[256];
368 l1[0] = 0;
369 sprint(buf, "depth:%d r:%d %d %d %d ",
370 t->b->depth, t->b->r.min.x, t->b->r.min.y,
371 t->b->r.max.x, t->b->r.max.y);
372 if(t->parent)
373 sprint(buf+strlen(buf), "mag: %d ", t->mag);
374 sprint(l0, "%s file: %s", buf, t->name);
375 if(t->c >= 0){
376 fc = &t->parent->s->info[t->c];
377 sprint(l1, "c(hex): %x c(char): %C x: %d "
378 "top: %d bottom: %d left: %d width: %d iwidth: %d",
379 (int)(t->c+t->parent->off), (int)(t->c+t->parent->off),
380 fc->x, fc->top, fc->bottom, fc->left,
381 fc->width, Dx(t->b->r));
382 }else if(t->s)
383 sprint(l1, "offset(hex): %ux n:%d height:%d ascent:%d",
384 t->off, t->s->n, t->s->height, t->s->ascent);
387 void
388 text(Thing *t)
390 char l0[256], l1[256];
392 stext(t, l0, l1);
393 tmesg(t, 0, l0);
394 if(l1[0])
395 tmesg(t, 1, l1);
398 void
399 drawall(void)
401 Thing *t;
403 cntl();
404 for(t=thing; t; t=t->next)
405 drawthing(t, 0);
408 int
409 value(Image *b, int x)
411 int v, l, w;
412 uchar mask;
414 w = b->depth;
415 if(w > 8){
416 mesg("ldepth too large");
417 return 0;
419 l = xlog2(w);
420 mask = (1<<w)-1; /* ones at right end of word */
421 x -= b->r.min.x&~(7>>l); /* adjust x relative to first pixel */
422 v = data[x>>(3-l)];
423 v >>= ((7>>l)<<l) - ((x&(7>>l))<<l); /* pixel at right end of word */
424 v &= mask; /* pixel at right end of word */
425 return v;
428 int
429 bvalue(int v, int d)
431 v &= (1<<d)-1;
432 if(d > screen->depth)
433 v >>= d - screen->depth;
434 else
435 while(d < screen->depth && d < 8){
436 v |= v << d;
437 d <<= 1;
439 if(v<0 || v>255){
440 mesg("internal error: bad color");
441 return Blue;
443 return v;
446 void
447 drawthing(Thing *nt, int link)
449 int n, nl, nf, i, x, y, sx, sy, fdx, dx, dy, v;
450 Thing *t;
451 Subfont *s;
452 Image *b, *col;
453 Point p, p1, p2;
455 if(link){
456 nt->next = 0;
457 if(thing == 0){
458 thing = nt;
459 y = editr.min.y;
460 }else{
461 for(t=thing; t->next; t=t->next)
463 t->next = nt;
464 y = t->er.max.y;
466 }else{
467 if(thing == nt)
468 y = editr.min.y;
469 else{
470 for(t=thing; t->next!=nt; t=t->next)
472 y = t->er.max.y;
475 s = nt->s;
476 b = nt->b;
477 nl = font->height;
478 if(s || nt->c>=0)
479 nl += font->height;
480 fdx = Dx(editr) - 2*Border;
481 dx = Dx(b->r);
482 dy = Dy(b->r);
483 if(nt->mag > 1){
484 dx *= nt->mag;
485 dy *= nt->mag;
486 fdx -= fdx%nt->mag;
488 nf = 1 + dx/fdx;
489 nt->er.min.y = y;
490 nt->er.min.x = editr.min.x;
491 nt->er.max.x = nt->er.min.x + Border + dx + Border;
492 if(nt->er.max.x > editr.max.x)
493 nt->er.max.x = editr.max.x;
494 nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border);
495 nt->r = insetrect(nt->er, Border);
496 nt->er.max.x = editr.max.x;
497 draw(screen, nt->er, display->white, nil, ZP);
498 for(i=0; i<nf; i++){
499 p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy));
500 /* draw portion of bitmap */
501 p = Pt(p1.x+1, p1.y);
502 if(nt->mag == 1)
503 draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)),
504 b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y));
505 else{
506 for(y=b->r.min.y; y<b->r.max.y; y++){
507 sy = p.y+(y-b->r.min.y)*nt->mag;
508 if((n=unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data)) < 0)
509 fprint(2, "unloadimage: %r\n");
510 for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){
511 sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag;
512 if(sx >= nt->r.max.x)
513 break;
514 v = bvalue(value(b, x), b->depth);
515 if(v == 255)
516 continue;
517 if(b->chan == GREY8)
518 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
519 greyvalues[v], nil, ZP);
520 else
521 draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag),
522 values[v], nil, ZP);
527 /* line down left */
528 if(i == 0)
529 col = display->black;
530 else
531 col = display->white;
532 draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP);
533 /* line across top */
534 draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP);
535 p2 = p1;
536 if(i == nf-1){
537 p2.x += 1 + dx%fdx;
538 col = display->black;
539 }else{
540 p2.x = nt->r.max.x;
541 col = display->white;
543 /* line down right */
544 draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP);
545 /* line across bottom */
546 if(i == nf-1){
547 p1.y += Border+dy;
548 draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP);
551 nt->tr.min.x = editr.min.x;
552 nt->tr.max.x = editr.max.x;
553 nt->tr.min.y = nt->er.max.y + Border;
554 nt->tr.max.y = nt->tr.min.y + nl;
555 nt->er.max.y = nt->tr.max.y + Border;
556 text(nt);
559 int
560 tohex(int c)
562 if('0'<=c && c<='9')
563 return c - '0';
564 if('a'<=c && c<='f')
565 return 10 + (c - 'a');
566 if('A'<=c && c<='F')
567 return 10 + (c - 'A');
568 return 0;
571 Thing*
572 tget(char *file)
574 int i, j, fd, face, x, y, c, chan;
575 Image *b;
576 Subfont *s;
577 Thing *t;
578 Dir *volatile d;
579 jmp_buf oerr;
580 uchar buf[256];
581 char *data;
583 buf[0] = '\0';
584 errstr((char*)buf, sizeof buf); /* flush pending error message */
585 memmove(oerr, err, sizeof err);
586 d = nil;
587 if(setjmp(err)){
588 Err:
589 free(d);
590 memmove(err, oerr, sizeof err);
591 return 0;
593 fd = open(file, OREAD);
594 if(fd < 0){
595 mesg("can't open %s: %r", file);
596 goto Err;
598 d = dirfstat(fd);
599 if(d == nil){
600 mesg("can't stat bitmap file %s: %r", file);
601 close(fd);
602 goto Err;
604 if(read(fd, buf, 11) != 11){
605 mesg("can't read %s: %r", file);
606 close(fd);
607 goto Err;
609 seek(fd, 0, 0);
610 data = (char*)buf;
611 if(*data == '{')
612 data++;
613 if(memcmp(data, "0x", 2)==0 && data[4]==','){
614 /*
615 * cursor file
616 */
617 face = CURSOR;
618 s = 0;
619 data = malloc(d->length+1);
620 if(data == 0){
621 mesg("can't malloc buffer: %r");
622 close(fd);
623 goto Err;
625 data[d->length] = 0;
626 if(read(fd, data, d->length) != d->length){
627 mesg("can't read cursor file %s: %r", file);
628 close(fd);
629 goto Err;
631 b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill);
632 if(b == 0){
633 mesg("image alloc failed file %s: %r", file);
634 free(data);
635 close(fd);
636 goto Err;
638 i = 0;
639 for(x=0;x<64; ){
640 if((c=data[i]) == '\0')
641 goto ill;
642 if(c=='0' && data[i+1] == 'x'){
643 i += 2;
644 continue;
646 if(strchr(hex, c)){
647 buf[x++] = (tohex(c)<<4) | tohex(data[i+1]);
648 i += 2;
649 continue;
651 i++;
653 loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf);
654 free(data);
655 }else if(memcmp(buf, "0x", 2)==0){
656 /*
657 * face file
658 */
659 face = FACE;
660 s = 0;
661 data = malloc(d->length+1);
662 if(data == 0){
663 mesg("can't malloc buffer: %r");
664 close(fd);
665 goto Err;
667 data[d->length] = 0;
668 if(read(fd, data, d->length) != d->length){
669 mesg("can't read bitmap file %s: %r", file);
670 close(fd);
671 goto Err;
673 for(y=0,i=0; i<d->length; i++)
674 if(data[i] == '\n')
675 y++;
676 if(y == 0){
677 ill:
678 mesg("ill-formed face file %s", file);
679 close(fd);
680 free(data);
681 goto Err;
683 for(x=0,i=0; (c=data[i])!='\n'; ){
684 if(c==',' || c==' ' || c=='\t'){
685 i++;
686 continue;
688 if(c=='0' && data[i+1] == 'x'){
689 i += 2;
690 continue;
692 if(strchr(hex, c)){
693 x += 4;
694 i++;
695 continue;
697 goto ill;
699 if(x % y)
700 goto ill;
701 switch(x / y){
702 default:
703 goto ill;
704 case 1:
705 chan = GREY1;
706 break;
707 case 2:
708 chan = GREY2;
709 break;
710 case 4:
711 chan = GREY4;
712 break;
713 case 8:
714 chan = CMAP8;
715 break;
717 b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1);
718 if(b == 0){
719 mesg("image alloc failed file %s: %r", file);
720 free(data);
721 close(fd);
722 goto Err;
724 i = 0;
725 for(j=0; j<y; j++){
726 for(x=0; (c=data[i])!='\n'; ){
727 if(c=='0' && data[i+1] == 'x'){
728 i += 2;
729 continue;
731 if(strchr(hex, c)){
732 buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1]));
733 i += 2;
734 continue;
736 i++;
738 i++;
739 loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf);
741 free(data);
742 }else{
743 face = NORMAL;
744 s = 0;
745 b = readimage(display, fd, 0);
746 if(b == 0){
747 mesg("can't read bitmap file %s: %r", file);
748 close(fd);
749 goto Err;
751 if(seek(fd, 0, 1) < d->length)
752 s = readsubfonti(display, file, fd, b, 0);
754 close(fd);
755 t = malloc(sizeof(Thing));
756 if(t == 0){
757 nomem:
758 mesg("malloc failed: %r");
759 if(s)
760 freesubfont(s);
761 else
762 freeimage(b);
763 goto Err;
765 t->name = strdup(file);
766 if(t->name == 0){
767 free(t);
768 goto nomem;
770 t->b = b;
771 t->s = s;
772 t->face = face;
773 t->mod = 0;
774 t->parent = 0;
775 t->c = -1;
776 t->mag = 1;
777 t->off = 0;
778 memmove(err, oerr, sizeof err);
779 return t;
782 int
783 atline(int x, Point p, char *line, char *buf)
785 char *s, *c, *word, *hit;
786 int w, wasblank;
787 Rune r;
789 wasblank = 1;
790 hit = 0;
791 word = 0;
792 for(s=line; *s; s+=w){
793 w = chartorune(&r, s);
794 x += runestringnwidth(font, &r, 1);
795 if(wasblank && r!=' ')
796 word = s;
797 wasblank = 0;
798 if(r == ' '){
799 if(x >= p.x)
800 break;
801 wasblank = 1;
803 if(r == ':')
804 hit = word;
806 if(x < p.x)
807 return 0;
808 c = utfrune(hit, ':');
809 strncpy(buf, hit, c-hit);
810 buf[c-hit] = 0;
811 return 1;
814 int
815 attext(Thing *t, Point p, char *buf)
817 char l0[256], l1[256];
819 if(!ptinrect(p, t->tr))
820 return 0;
821 stext(t, l0, l1);
822 if(p.y < t->tr.min.y+font->height)
823 return atline(t->r.min.x, p, l0, buf);
824 else
825 return atline(t->r.min.x, p, l1, buf);
828 int
829 type(char *buf, char *tag)
831 Rune r;
832 char *p;
834 esetcursor(&busy);
835 p = buf;
836 for(;;){
837 *p = 0;
838 mesg("%s: %s", tag, buf);
839 r = ekbd();
840 switch(r){
841 case '\n':
842 mesg("");
843 esetcursor(0);
844 return p-buf;
845 case 0x15: /* control-U */
846 p = buf;
847 break;
848 case '\b':
849 if(p > buf)
850 --p;
851 break;
852 default:
853 p += runetochar(p, &r);
856 /* return 0; shut up compiler */
859 void
860 textedit(Thing *t, char *tag)
862 char buf[256];
863 char *s;
864 Image *b;
865 Subfont *f;
866 Fontchar *fc, *nfc;
867 Rectangle r;
868 ulong chan;
869 int i, ld, d, w, c, doredraw, fdx, x;
870 Thing *nt;
872 buttons(Up);
873 if(type(buf, tag) == 0)
874 return;
875 if(strcmp(tag, "file") == 0){
876 for(s=buf; *s; s++)
877 if(*s <= ' '){
878 mesg("illegal file name");
879 return;
881 if(strcmp(t->name, buf) != 0){
882 if(t->parent)
883 t->parent->mod = 1;
884 else
885 t->mod = 1;
887 for(nt=thing; nt; nt=nt->next)
888 if(t==nt || t->parent==nt || nt->parent==t){
889 free(nt->name);
890 nt->name = strdup(buf);
891 if(nt->name == 0){
892 mesg("malloc failed: %r");
893 return;
895 text(nt);
897 return;
899 if(strcmp(tag, "depth") == 0){
900 if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || xlog2(d)<0){
901 mesg("illegal ldepth");
902 return;
904 if(d == t->b->depth)
905 return;
906 if(t->parent)
907 t->parent->mod = 1;
908 else
909 t->mod = 1;
910 if(d == 8)
911 chan = CMAP8;
912 else
913 chan = CHAN1(CGrey, d);
914 for(nt=thing; nt; nt=nt->next){
915 if(nt!=t && nt!=t->parent && nt->parent!=t)
916 continue;
917 b = allocimage(display, nt->b->r, chan, 0, 0);
918 if(b == 0){
919 nobmem:
920 mesg("image alloc failed: %r");
921 return;
923 draw(b, b->r, nt->b, nil, nt->b->r.min);
924 freeimage(nt->b);
925 nt->b = b;
926 if(nt->s){
927 b = allocimage(display, nt->b->r, chan, 0, -1);
928 if(b == 0)
929 goto nobmem;
930 draw(b, b->r, nt->b, nil, nt->b->r.min);
931 f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b);
932 if(f == 0){
933 nofmem:
934 freeimage(b);
935 mesg("can't make subfont: %r");
936 return;
938 nt->s->info = 0; /* prevent it being freed */
939 nt->s->bits = 0;
940 freesubfont(nt->s);
941 nt->s = f;
943 drawthing(nt, 0);
945 return;
947 if(strcmp(tag, "mag") == 0){
948 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){
949 mesg("illegal magnification");
950 return;
952 if(t->mag == ld)
953 return;
954 t->mag = ld;
955 redraw(t);
956 return;
958 if(strcmp(tag, "r") == 0){
959 if(t->s){
960 mesg("can't change rectangle of subfont\n");
961 return;
963 s = buf;
964 r.min.x = strtoul(s, &s, 0);
965 r.min.y = strtoul(s, &s, 0);
966 r.max.x = strtoul(s, &s, 0);
967 r.max.y = strtoul(s, &s, 0);
968 if(Dx(r)<=0 || Dy(r)<=0){
969 mesg("illegal rectangle");
970 return;
972 if(t->parent)
973 t = t->parent;
974 for(nt=thing; nt; nt=nt->next){
975 if(nt->parent==t && !rectinrect(nt->b->r, r))
976 tclose1(nt);
978 b = allocimage(display, r, t->b->chan, 0, 0);
979 if(b == 0)
980 goto nobmem;
981 draw(b, r, t->b, nil, r.min);
982 freeimage(t->b);
983 t->b = b;
984 b = allocimage(display, r, t->b->chan, 0, 0);
985 if(b == 0)
986 goto nobmem;
987 redraw(t);
988 t->mod = 1;
989 return;
991 if(strcmp(tag, "ascent") == 0){
992 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){
993 mesg("illegal ascent");
994 return;
996 if(t->s->ascent == ld)
997 return;
998 t->s->ascent = ld;
999 text(t);
1000 t->mod = 1;
1001 return;
1003 if(strcmp(tag, "height") == 0){
1004 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
1005 mesg("illegal height");
1006 return;
1008 if(t->s->height == ld)
1009 return;
1010 t->s->height = ld;
1011 text(t);
1012 t->mod = 1;
1013 return;
1015 if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){
1016 if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){
1017 mesg("illegal value");
1018 return;
1020 fc = &t->parent->s->info[t->c];
1021 if(strcmp(tag, "left")==0){
1022 if(fc->left == ld)
1023 return;
1024 fc->left = ld;
1025 }else{
1026 if(fc->width == ld)
1027 return;
1028 fc->width = ld;
1030 text(t);
1031 t->parent->mod = 1;
1032 return;
1034 if(strcmp(tag, "offset(hex)") == 0){
1035 if(!strchr(hex, buf[0])){
1036 illoff:
1037 mesg("illegal offset");
1038 return;
1040 s = 0;
1041 ld = strtoul(buf, &s, 16);
1042 if(*s)
1043 goto illoff;
1044 t->off = ld;
1045 text(t);
1046 for(nt=thing; nt; nt=nt->next)
1047 if(nt->parent == t)
1048 text(nt);
1049 return;
1051 if(strcmp(tag, "n") == 0){
1052 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){
1053 mesg("illegal n");
1054 return;
1056 f = t->s;
1057 if(w == f->n)
1058 return;
1059 doredraw = 0;
1060 again:
1061 for(nt=thing; nt; nt=nt->next)
1062 if(nt->parent == t){
1063 doredraw = 1;
1064 tclose1(nt);
1065 goto again;
1067 r = t->b->r;
1068 if(w < f->n)
1069 r.max.x = f->info[w].x;
1070 b = allocimage(display, r, t->b->chan, 0, 0);
1071 if(b == 0)
1072 goto nobmem;
1073 draw(b, b->r, t->b, nil, r.min);
1074 fdx = Dx(editr) - 2*Border;
1075 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1076 doredraw = 1;
1077 freeimage(t->b);
1078 t->b = b;
1079 b = allocimage(display, r, t->b->chan, 0, 0);
1080 if(b == 0)
1081 goto nobmem;
1082 draw(b, b->r, t->b, nil, r.min);
1083 nfc = malloc((w+1)*sizeof(Fontchar));
1084 if(nfc == 0){
1085 mesg("malloc failed");
1086 freeimage(b);
1087 return;
1089 fc = f->info;
1090 for(i=0; i<=w && i<=f->n; i++)
1091 nfc[i] = fc[i];
1092 if(w+1 < i)
1093 memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar));
1094 x = fc[f->n].x;
1095 for(; i<=w; i++)
1096 nfc[i].x = x;
1097 f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b);
1098 if(f == 0)
1099 goto nofmem;
1100 t->s->bits = nil; /* don't free it */
1101 freesubfont(t->s);
1102 f->info = nfc;
1103 t->s = f;
1104 if(doredraw)
1105 redraw(thing);
1106 else
1107 drawthing(t, 0);
1108 t->mod = 1;
1109 return;
1111 if(strcmp(tag, "iwidth") == 0){
1112 if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){
1113 mesg("illegal iwidth");
1114 return;
1116 w -= Dx(t->b->r);
1117 if(w == 0)
1118 return;
1119 r = t->parent->b->r;
1120 r.max.x += w;
1121 c = t->c;
1122 t = t->parent;
1123 f = t->s;
1124 b = allocimage(display, r, t->b->chan, 0, 0);
1125 if(b == 0)
1126 goto nobmem;
1127 fc = &f->info[c];
1128 draw(b, Rect(b->r.min.x, b->r.min.y,
1129 b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)),
1130 t->b, nil, t->b->r.min);
1131 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)),
1132 t->b, nil, Pt(fc[1].x, t->b->r.min.y));
1133 fdx = Dx(editr) - 2*Border;
1134 doredraw = 0;
1135 if(Dx(t->b->r)/fdx != Dx(b->r)/fdx)
1136 doredraw = 1;
1137 freeimage(t->b);
1138 t->b = b;
1139 b = allocimage(display, r, t->b->chan, 0, 0);
1140 if(b == 0)
1141 goto nobmem;
1142 draw(b, b->r, t->b, nil, t->b->r.min);
1143 fc = &f->info[c+1];
1144 for(i=c+1; i<=f->n; i++, fc++)
1145 fc->x += w;
1146 f = allocsubfont(t->name, f->n, f->height, f->ascent,
1147 f->info, b);
1148 if(f == 0)
1149 goto nofmem;
1150 /* t->s and f share info; free carefully */
1151 fc = f->info;
1152 t->s->bits = nil;
1153 t->s->info = 0;
1154 freesubfont(t->s);
1155 f->info = fc;
1156 t->s = f;
1157 if(doredraw)
1158 redraw(t);
1159 else
1160 drawthing(t, 0);
1161 /* redraw all affected chars */
1162 for(nt=thing; nt; nt=nt->next){
1163 if(nt->parent!=t || nt->c<c)
1164 continue;
1165 fc = &f->info[nt->c];
1166 r.min.x = fc[0].x;
1167 r.min.y = nt->b->r.min.y;
1168 r.max.x = fc[1].x;
1169 r.max.y = nt->b->r.max.y;
1170 b = allocimage(display, r, nt->b->chan, 0, 0);
1171 if(b == 0)
1172 goto nobmem;
1173 draw(b, r, t->b, nil, r.min);
1174 doredraw = 0;
1175 if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx)
1176 doredraw = 1;
1177 freeimage(nt->b);
1178 nt->b = b;
1179 if(c != nt->c)
1180 text(nt);
1181 else{
1182 if(doredraw)
1183 redraw(nt);
1184 else
1185 drawthing(nt, 0);
1188 t->mod = 1;
1189 return;
1191 mesg("cannot edit %s in file %s", tag, t->name);
1194 void
1195 cntledit(char *tag)
1197 char buf[256];
1198 ulong l;
1200 buttons(Up);
1201 if(type(buf, tag) == 0)
1202 return;
1203 if(strcmp(tag, "mag") == 0){
1204 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){
1205 mesg("illegal magnification");
1206 return;
1208 mag = l;
1209 cntl();
1210 return;
1212 if(strcmp(tag, "but1")==0
1213 || strcmp(tag, "but2")==0){
1214 if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<0 || l>255){
1215 mesg("illegal value");
1216 return;
1218 if(strcmp(tag, "but1") == 0)
1219 but1val = l;
1220 else if(strcmp(tag, "but2") == 0)
1221 but2val = l;
1222 cntl();
1223 return;
1225 if(strcmp(tag, "invert-on-copy")==0){
1226 if(buf[0]=='y' || buf[0]=='1')
1227 invert = 1;
1228 else if(buf[0]=='n' || buf[0]=='0')
1229 invert = 0;
1230 else{
1231 mesg("illegal value");
1232 return;
1234 cntl();
1235 return;
1237 mesg("cannot edit %s", tag);
1240 void
1241 buttons(int ud)
1243 while((mouse.buttons==0) != ud)
1244 mouse = emouse();
1247 Point
1248 screenpt(Thing *t, Point realp)
1250 int fdx, n;
1251 Point p;
1253 fdx = Dx(editr)-2*Border;
1254 if(t->mag > 1)
1255 fdx -= fdx%t->mag;
1256 p = mulpt(subpt(realp, t->b->r.min), t->mag);
1257 if(fdx < Dx(t->b->r)*t->mag){
1258 n = p.x/fdx;
1259 p.y += n * (Dy(t->b->r)*t->mag+Border);
1260 p.x -= n * fdx;
1262 p = addpt(p, t->r.min);
1263 return p;
1266 Point
1267 realpt(Thing *t, Point screenp)
1269 int fdx, n, dy;
1270 Point p;
1272 fdx = (Dx(editr)-2*Border);
1273 if(t->mag > 1)
1274 fdx -= fdx%t->mag;
1275 p.y = screenp.y-t->r.min.y;
1276 p.x = 0;
1277 if(fdx < Dx(t->b->r)*t->mag){
1278 dy = Dy(t->b->r)*t->mag+Border;
1279 n = (p.y/dy);
1280 p.x = n * fdx;
1281 p.y -= n * dy;
1283 p.x += screenp.x-t->r.min.x;
1284 p = addpt(divpt(p, t->mag), t->b->r.min);
1285 return p;
1288 int
1289 sweep(int but, Rectangle *r)
1291 Thing *t;
1292 Point p, q, lastq;
1294 esetcursor(&sweep0);
1295 buttons(Down);
1296 if(mouse.buttons != (1<<(but-1))){
1297 buttons(Up);
1298 esetcursor(0);
1299 return 0;
1301 p = mouse.xy;
1302 for(t=thing; t; t=t->next)
1303 if(ptinrect(p, t->r))
1304 break;
1305 if(t)
1306 p = screenpt(t, realpt(t, p));
1307 r->min = p;
1308 r->max = p;
1309 esetcursor(&box);
1310 lastq = ZP;
1311 while(mouse.buttons == (1<<(but-1))){
1312 edrawgetrect(insetrect(*r, -Borderwidth), 1);
1313 mouse = emouse();
1314 edrawgetrect(insetrect(*r, -Borderwidth), 0);
1315 q = mouse.xy;
1316 if(t)
1317 q = screenpt(t, realpt(t, q));
1318 if(eqpt(q, lastq))
1319 continue;
1320 *r = canonrect(Rpt(p, q));
1321 lastq = q;
1323 esetcursor(0);
1324 if(mouse.buttons){
1325 buttons(Up);
1326 return 0;
1328 return 1;
1331 void
1332 openedit(Thing *t, Point pt, int c)
1334 int x, y;
1335 Point p;
1336 Rectangle r;
1337 Rectangle br;
1338 Fontchar *fc;
1339 Thing *nt;
1341 if(t->b->depth > 8){
1342 mesg("image has depth %d; can't handle >8", t->b->depth);
1343 return;
1345 br = t->b->r;
1346 if(t->s == 0){
1347 c = -1;
1348 /* if big enough to bother, sweep box */
1349 if(Dx(br)<=16 && Dy(br)<=16)
1350 r = br;
1351 else{
1352 if(!sweep(1, &r))
1353 return;
1354 r = rectaddpt(r, subpt(br.min, t->r.min));
1355 if(!rectclip(&r, br))
1356 return;
1357 if(Dx(br) <= 8){
1358 r.min.x = br.min.x;
1359 r.max.x = br.max.x;
1360 }else if(Dx(r) < 4){
1361 toosmall:
1362 mesg("rectangle too small");
1363 return;
1365 if(Dy(br) <= 8){
1366 r.min.y = br.min.y;
1367 r.max.y = br.max.y;
1368 }else if(Dy(r) < 4)
1369 goto toosmall;
1371 }else if(c >= 0){
1372 fc = &t->s->info[c];
1373 r.min.x = fc[0].x;
1374 r.min.y = br.min.y;
1375 r.max.x = fc[1].x;
1376 r.max.y = br.min.y + Dy(br);
1377 }else{
1378 /* just point at character */
1379 fc = t->s->info;
1380 p = addpt(pt, subpt(br.min, t->r.min));
1381 x = br.min.x;
1382 y = br.min.y;
1383 for(c=0; c<t->s->n; c++,fc++){
1384 again:
1385 r.min.x = x;
1386 r.min.y = y;
1387 r.max.x = x + fc[1].x - fc[0].x;
1388 r.max.y = y + Dy(br);
1389 if(ptinrect(p, r))
1390 goto found;
1391 if(r.max.x >= br.min.x+Dx(t->r)){
1392 x -= Dx(t->r);
1393 y += t->s->height;
1394 if(fc[1].x > fc[0].x)
1395 goto again;
1397 x += fc[1].x - fc[0].x;
1399 return;
1400 found:
1401 r = br;
1402 r.min.x = fc[0].x;
1403 r.max.x = fc[1].x;
1405 nt = malloc(sizeof(Thing));
1406 if(nt == 0){
1407 nomem:
1408 mesg("can't allocate: %r");
1409 return;
1411 memset(nt, 0, sizeof(Thing));
1412 nt->c = c;
1413 nt->b = allocimage(display, r, t->b->chan, 0, DNofill);
1414 if(nt->b == 0){
1415 free(nt);
1416 goto nomem;
1418 draw(nt->b, r, t->b, nil, r.min);
1419 nt->name = strdup(t->name);
1420 if(nt->name == 0){
1421 freeimage(nt->b);
1422 free(nt);
1423 goto nomem;
1425 nt->parent = t;
1426 nt->mag = mag;
1427 drawthing(nt, 1);
1430 void
1431 ckinfo(Thing *t, Rectangle mod)
1433 int i, j, k, top, bot, n, zero;
1434 Fontchar *fc;
1435 Rectangle r;
1436 Image *b;
1437 Thing *nt;
1439 if(t->parent)
1440 t = t->parent;
1441 if(t->s==0 || Dy(t->b->r)==0)
1442 return;
1443 b = 0;
1444 /* check bounding boxes */
1445 fc = &t->s->info[0];
1446 r.min.y = t->b->r.min.y;
1447 r.max.y = t->b->r.max.y;
1448 for(i=0; i<t->s->n; i++, fc++){
1449 r.min.x = fc[0].x;
1450 r.max.x = fc[1].x;
1451 if(!rectXrect(mod, r))
1452 continue;
1453 if(b==0 || Dx(b->r)<Dx(r)){
1454 if(b)
1455 freeimage(b);
1456 b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0);
1457 if(b == 0){
1458 mesg("can't alloc image");
1459 break;
1462 draw(b, b->r, display->white, nil, ZP);
1463 draw(b, b->r, t->b, nil, r.min);
1464 top = 100000;
1465 bot = 0;
1466 n = 2+((Dx(r)/8)*t->b->depth);
1467 for(j=0; j<b->r.max.y; j++){
1468 memset(data, 0, n);
1469 unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data);
1470 zero = 1;
1471 for(k=0; k<n; k++)
1472 if(data[k]){
1473 zero = 0;
1474 break;
1476 if(!zero){
1477 if(top > j)
1478 top = j;
1479 bot = j+1;
1482 if(top > j)
1483 top = 0;
1484 if(top!=fc->top || bot!=fc->bottom){
1485 fc->top = top;
1486 fc->bottom = bot;
1487 for(nt=thing; nt; nt=nt->next)
1488 if(nt->parent==t && nt->c==i)
1489 text(nt);
1492 if(b)
1493 freeimage(b);
1496 void
1497 twidpix(Thing *t, Point p, int set)
1499 Image *b, *v;
1500 int c;
1502 b = t->b;
1503 if(!ptinrect(p, b->r))
1504 return;
1505 if(set)
1506 c = but1val;
1507 else
1508 c = but2val;
1509 if(b->chan == GREY8)
1510 v = greyvalues[c];
1511 else
1512 v = values[c];
1513 draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP);
1514 p = screenpt(t, p);
1515 draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP);
1518 void
1519 twiddle(Thing *t)
1521 int set;
1522 Point p, lastp;
1523 Image *b;
1524 Thing *nt;
1525 Rectangle mod;
1527 if(mouse.buttons!=1 && mouse.buttons!=2){
1528 buttons(Up);
1529 return;
1531 set = mouse.buttons==1;
1532 b = t->b;
1533 lastp = addpt(b->r.min, Pt(-1, -1));
1534 mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp);
1535 while(mouse.buttons){
1536 p = realpt(t, mouse.xy);
1537 if(!eqpt(p, lastp)){
1538 lastp = p;
1539 if(ptinrect(p, b->r)){
1540 for(nt=thing; nt; nt=nt->next)
1541 if(nt->parent==t->parent || nt==t->parent)
1542 twidpix(nt, p, set);
1543 if(t->parent)
1544 t->parent->mod = 1;
1545 else
1546 t->mod = 1;
1547 if(p.x < mod.min.x)
1548 mod.min.x = p.x;
1549 if(p.y < mod.min.y)
1550 mod.min.y = p.y;
1551 if(p.x >= mod.max.x)
1552 mod.max.x = p.x+1;
1553 if(p.y >= mod.max.y)
1554 mod.max.y = p.y+1;
1557 mouse = emouse();
1559 ckinfo(t, mod);
1562 void
1563 xselect(void)
1565 Thing *t;
1566 char line[128], buf[128];
1567 Point p;
1569 if(ptinrect(mouse.xy, cntlr)){
1570 scntl(line);
1571 if(atline(cntlr.min.x, mouse.xy, line, buf)){
1572 if(mouse.buttons == 1)
1573 cntledit(buf);
1574 else
1575 buttons(Up);
1576 return;
1578 return;
1580 for(t=thing; t; t=t->next){
1581 if(attext(t, mouse.xy, buf)){
1582 if(mouse.buttons == 1)
1583 textedit(t, buf);
1584 else
1585 buttons(Up);
1586 return;
1588 if(ptinrect(mouse.xy, t->r)){
1589 if(t->parent == 0){
1590 if(mouse.buttons == 1){
1591 p = mouse.xy;
1592 buttons(Up);
1593 openedit(t, p, -1);
1594 }else
1595 buttons(Up);
1596 return;
1598 twiddle(t);
1599 return;
1604 void
1605 twrite(Thing *t)
1607 int i, j, x, y, fd, ws, ld;
1608 Biobuf buf;
1609 Rectangle r;
1611 if(t->parent)
1612 t = t->parent;
1613 esetcursor(&busy);
1614 fd = create(t->name, OWRITE, 0666);
1615 if(fd < 0){
1616 mesg("can't write %s: %r", t->name);
1617 return;
1619 if(t->face && t->b->depth <= 4){
1620 r = t->b->r;
1621 ld = xlog2(t->b->depth);
1622 /* This heuristic reflects peculiarly different formats */
1623 ws = 4;
1624 if(t->face == 2) /* cursor file */
1625 ws = 1;
1626 else if(Dx(r)<32 || ld==0)
1627 ws = 2;
1628 Binit(&buf, fd, OWRITE);
1629 if(t->face == CURSOR)
1630 Bprint(&buf, "{");
1631 for(y=r.min.y; y<r.max.y; y++){
1632 unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data);
1633 j = 0;
1634 for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){
1635 Bprint(&buf, "0x");
1636 for(i=0; i<ws; i++)
1637 Bprint(&buf, "%.2x", data[i+j]);
1638 Bprint(&buf, ", ");
1640 if(t->face == CURSOR){
1641 switch(y){
1642 case 3: case 7: case 11: case 19: case 23: case 27:
1643 Bprint(&buf, "\n ");
1644 break;
1645 case 15:
1646 Bprint(&buf, "},\n{");
1647 break;
1648 case 31:
1649 Bprint(&buf, "}\n");
1650 break;
1652 }else
1653 Bprint(&buf, "\n");
1655 Bterm(&buf);
1656 }else
1657 if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){
1658 close(fd);
1659 mesg("can't write %s: %r", t->name);
1661 t->mod = 0;
1662 close(fd);
1663 mesg("wrote %s", t->name);
1666 void
1667 tpixels(void)
1669 Thing *t;
1670 Point p, lastp;
1672 esetcursor(&pixel);
1673 for(;;){
1674 buttons(Down);
1675 if(mouse.buttons != 4)
1676 break;
1677 for(t=thing; t; t=t->next){
1678 lastp = Pt(-1, -1);
1679 if(ptinrect(mouse.xy, t->r)){
1680 while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){
1681 p = realpt(t, mouse.xy);
1682 if(!eqpt(p, lastp)){
1683 if(p.y != lastp.y)
1684 unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data);
1685 mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x));
1686 lastp = p;
1688 mouse = emouse();
1690 goto Continue;
1693 mouse = emouse();
1694 Continue:;
1696 buttons(Up);
1697 esetcursor(0);
1700 void
1701 tclose1(Thing *t)
1703 Thing *nt;
1705 if(t == thing)
1706 thing = t->next;
1707 else{
1708 for(nt=thing; nt->next!=t; nt=nt->next)
1710 nt->next = t->next;
1713 for(nt=thing; nt; nt=nt->next)
1714 if(nt->parent == t){
1715 tclose1(nt);
1716 break;
1718 while(nt);
1719 if(t->s)
1720 freesubfont(t->s);
1721 else
1722 freeimage(t->b);
1723 free(t->name);
1724 free(t);
1727 void
1728 tclose(Thing *t)
1730 Thing *ct;
1732 if(t->mod){
1733 mesg("%s modified", t->name);
1734 t->mod = 0;
1735 return;
1737 /* fiddle to save redrawing unmoved things */
1738 if(t == thing)
1739 ct = 0;
1740 else
1741 for(ct=thing; ct; ct=ct->next)
1742 if(ct->next==t || ct->next->parent==t)
1743 break;
1744 tclose1(t);
1745 if(ct)
1746 ct = ct->next;
1747 else
1748 ct = thing;
1749 redraw(ct);
1752 void
1753 tread(Thing *t)
1755 Thing *nt, *new;
1756 Fontchar *i;
1757 Rectangle r;
1758 int nclosed;
1760 if(t->parent)
1761 t = t->parent;
1762 new = tget(t->name);
1763 if(new == 0)
1764 return;
1765 nclosed = 0;
1766 again:
1767 for(nt=thing; nt; nt=nt->next)
1768 if(nt->parent == t){
1769 if(!rectinrect(nt->b->r, new->b->r)
1770 || new->b->depth!=nt->b->depth){
1771 closeit:
1772 nclosed++;
1773 nt->parent = 0;
1774 tclose1(nt);
1775 goto again;
1777 if((t->s==0) != (new->s==0))
1778 goto closeit;
1779 if((t->face==0) != (new->face==0))
1780 goto closeit;
1781 if(t->s){ /* check same char */
1782 if(nt->c >= new->s->n)
1783 goto closeit;
1784 i = &new->s->info[nt->c];
1785 r.min.x = i[0].x;
1786 r.max.x = i[1].x;
1787 r.min.y = new->b->r.min.y;
1788 r.max.y = new->b->r.max.y;
1789 if(!eqrect(r, nt->b->r))
1790 goto closeit;
1792 nt->parent = new;
1793 draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min);
1795 new->next = t->next;
1796 if(t == thing)
1797 thing = new;
1798 else{
1799 for(nt=thing; nt->next!=t; nt=nt->next)
1801 nt->next = new;
1803 if(t->s)
1804 freesubfont(t->s);
1805 else
1806 freeimage(t->b);
1807 free(t->name);
1808 free(t);
1809 for(nt=thing; nt; nt=nt->next)
1810 if(nt==new || nt->parent==new)
1811 if(nclosed == 0)
1812 drawthing(nt, 0); /* can draw in place */
1813 else{
1814 redraw(nt); /* must redraw all below */
1815 break;
1819 void
1820 tchar(Thing *t)
1822 char buf[256], *p;
1823 Rune r;
1824 ulong c, d;
1826 if(t->s == 0){
1827 t = t->parent;
1828 if(t==0 || t->s==0){
1829 mesg("not a subfont");
1830 return;
1833 if(type(buf, "char (hex or character or hex-hex)") == 0)
1834 return;
1835 if(utflen(buf) == 1){
1836 chartorune(&r, buf);
1837 c = r;
1838 d = r;
1839 }else{
1840 if(!strchr(hex, buf[0])){
1841 mesg("illegal hex character");
1842 return;
1844 c = strtoul(buf, 0, 16);
1845 d = c;
1846 p = utfrune(buf, '-');
1847 if(p){
1848 d = strtoul(p+1, 0, 16);
1849 if(d < c){
1850 mesg("invalid range");
1851 return;
1855 c -= t->off;
1856 d -= t->off;
1857 while(c <= d){
1858 if(c<0 || c>=t->s->n){
1859 mesg("0x%lux not in font %s", c+t->off, t->name);
1860 return;
1862 openedit(t, Pt(0, 0), c);
1863 c++;
1867 void
1868 apply(void (*f)(Thing*))
1870 Thing *t;
1872 esetcursor(&sight);
1873 buttons(Down);
1874 if(mouse.buttons == 4)
1875 for(t=thing; t; t=t->next)
1876 if(ptinrect(mouse.xy, t->er)){
1877 buttons(Up);
1878 f(t);
1879 break;
1881 buttons(Up);
1882 esetcursor(0);
1885 int
1886 complement(Image *t)
1888 int i, n;
1889 uchar *buf;
1891 n = Dy(t->r)*bytesperline(t->r, t->depth);
1892 buf = malloc(n);
1893 if(buf == 0)
1894 return 0;
1895 unloadimage(t, t->r, buf, n);
1896 for(i=0; i<n; i++)
1897 buf[i] = ~buf[i];
1898 loadimage(t, t->r, buf, n);
1899 free(buf);
1900 return 1;
1903 void
1904 copy(void)
1906 Thing *st, *dt, *nt;
1907 Rectangle sr, dr, fr;
1908 Image *tmp;
1909 Point p1, p2;
1910 int but, up;
1912 if(!sweep(3, &sr))
1913 return;
1914 for(st=thing; st; st=st->next)
1915 if(rectXrect(sr, st->r))
1916 break;
1917 if(st == 0)
1918 return;
1919 /* click gives full rectangle */
1920 if(Dx(sr)<4 && Dy(sr)<4)
1921 sr = st->r;
1922 rectclip(&sr, st->r);
1923 p1 = realpt(st, sr.min);
1924 p2 = realpt(st, Pt(sr.min.x, sr.max.y));
1925 up = 0;
1926 if(p1.x != p2.x){ /* swept across a fold */
1927 onafold:
1928 mesg("sweep spans a fold");
1929 goto Return;
1931 p2 = realpt(st, sr.max);
1932 sr.min = p1;
1933 sr.max = p2;
1934 fr.min = screenpt(st, sr.min);
1935 fr.max = screenpt(st, sr.max);
1936 p1 = subpt(p2, p1); /* diagonal */
1937 if(p1.x==0 || p1.y==0)
1938 return;
1939 border(screen, fr, -1, values[Blue], ZP);
1940 esetcursor(&box);
1941 for(; mouse.buttons==0; mouse=emouse()){
1942 for(dt=thing; dt; dt=dt->next)
1943 if(ptinrect(mouse.xy, dt->er))
1944 break;
1945 if(up)
1946 edrawgetrect(insetrect(dr, -Borderwidth), 0);
1947 up = 0;
1948 if(dt == 0)
1949 continue;
1950 dr.max = screenpt(dt, realpt(dt, mouse.xy));
1951 dr.min = subpt(dr.max, mulpt(p1, dt->mag));
1952 if(!rectXrect(dr, dt->r))
1953 continue;
1954 edrawgetrect(insetrect(dr, -Borderwidth), 1);
1955 up = 1;
1957 /* if up==1, we had a hit */
1958 esetcursor(0);
1959 if(up)
1960 edrawgetrect(insetrect(dr, -Borderwidth), 0);
1961 but = mouse.buttons;
1962 buttons(Up);
1963 if(!up || but!=4)
1964 goto Return;
1965 dt = 0;
1966 for(nt=thing; nt; nt=nt->next)
1967 if(rectXrect(dr, nt->r)){
1968 if(dt){
1969 mesg("ambiguous sweep");
1970 return;
1972 dt = nt;
1974 if(dt == 0)
1975 goto Return;
1976 p1 = realpt(dt, dr.min);
1977 p2 = realpt(dt, Pt(dr.min.x, dr.max.y));
1978 if(p1.x != p2.x)
1979 goto onafold;
1980 p2 = realpt(dt, dr.max);
1981 dr.min = p1;
1982 dr.max = p2;
1984 if(invert){
1985 tmp = allocimage(display, dr, dt->b->chan, 0, 255);
1986 if(tmp == 0){
1987 nomem:
1988 mesg("can't allocate temporary");
1989 goto Return;
1991 draw(tmp, dr, st->b, nil, sr.min);
1992 if(!complement(tmp))
1993 goto nomem;
1994 draw(dt->b, dr, tmp, nil, dr.min);
1995 freeimage(tmp);
1996 }else
1997 draw(dt->b, dr, st->b, nil, sr.min);
1998 if(dt->parent){
1999 draw(dt->parent->b, dr, dt->b, nil, dr.min);
2000 dt = dt->parent;
2002 drawthing(dt, 0);
2003 for(nt=thing; nt; nt=nt->next)
2004 if(nt->parent==dt && rectXrect(dr, nt->b->r)){
2005 draw(nt->b, dr, dt->b, nil, dr.min);
2006 drawthing(nt, 0);
2008 ckinfo(dt, dr);
2009 dt->mod = 1;
2011 Return:
2012 /* clear blue box */
2013 drawthing(st, 0);
2016 void
2017 menu(void)
2019 Thing *t;
2020 char *mod;
2021 int sel;
2022 char buf[256];
2024 sel = emenuhit(3, &mouse, &menu3);
2025 switch(sel){
2026 case Mopen:
2027 if(type(buf, "file")){
2028 t = tget(buf);
2029 if(t)
2030 drawthing(t, 1);
2032 break;
2033 case Mwrite:
2034 apply(twrite);
2035 break;
2036 case Mread:
2037 apply(tread);
2038 break;
2039 case Mchar:
2040 apply(tchar);
2041 break;
2042 case Mcopy:
2043 copy();
2044 break;
2045 case Mpixels:
2046 tpixels();
2047 break;
2048 case Mclose:
2049 apply(tclose);
2050 break;
2051 case Mexit:
2052 mod = 0;
2053 for(t=thing; t; t=t->next)
2054 if(t->mod){
2055 mod = t->name;
2056 t->mod = 0;
2058 if(mod){
2059 mesg("%s modified", mod);
2060 break;
2062 esetcursor(&skull);
2063 buttons(Down);
2064 if(mouse.buttons == 4){
2065 buttons(Up);
2066 exits(0);
2068 buttons(Up);
2069 esetcursor(0);
2070 break;