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
main(int argc,char * argv[])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
error(Display *,char * s)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
redraw(Thing * t)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
eresized(int new)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
mesgstr(Point p,int line,char * s)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
mesg(char * fmt,...)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
tmesg(Thing * t,int line,char * fmt,...)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
scntl(char * l)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
cntl(void)326 cntl(void)
327 {
328 char buf[256];
329
330 scntl(buf);
331 mesgstr(cntlr.min, 0, buf);
332 }
333
334 void
stext(Thing * t,char * l0,char * l1)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
text(Thing * t)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
drawall(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
value(Image * b,int x)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
bvalue(int v,int d)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
drawthing(Thing * nt,int link)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
tohex(int c)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*
tget(char * file)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
atline(int x,Point p,char * line,char * buf)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
attext(Thing * t,Point p,char * buf)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
type(char * buf,char * tag)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
textedit(Thing * t,char * tag)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
cntledit(char * tag)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
buttons(int ud)1217 buttons(int ud)
1218 {
1219 while((mouse.buttons==0) != ud)
1220 mouse = emouse();
1221 }
1222
1223 Point
screenpt(Thing * t,Point realp)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
realpt(Thing * t,Point screenp)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
sweep(int but,Rectangle * r)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
openedit(Thing * t,Point pt,int c)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
ckinfo(Thing * t,Rectangle mod)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
twidpix(Thing * t,Point p,int set)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
twiddle(Thing * t)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
select(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
twrite(Thing * t)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
tpixels(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
tclose1(Thing * t)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
tclose(Thing * t)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
tread(Thing * t)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
tchar(Thing * t)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
apply(void (* f)(Thing *))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
complement(Image * t)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
copy(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
menu(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 }
2049