| #include <u.h> |
| #include <libc.h> |
| #include <draw.h> |
| #include <cursor.h> |
| #include <event.h> |
| #include <bio.h> |
| |
| typedef struct Thing Thing; |
| |
| struct Thing |
| { |
| Image *b; |
| Subfont *s; |
| char *name; /* file name */ |
| int face; /* is 48x48 face file or cursor file*/ |
| Rectangle r; /* drawing region */ |
| Rectangle tr; /* text region */ |
| Rectangle er; /* entire region */ |
| long c; /* character number in subfont */ |
| int mod; /* modified */ |
| int mag; /* magnification */ |
| Rune off; /* offset for subfont indices */ |
| Thing *parent; /* thing of which i'm an edit */ |
| Thing *next; |
| }; |
| |
| enum |
| { |
| Border = 1, |
| Up = 1, |
| Down = 0, |
| Mag = 4, |
| Maxmag = 10 |
| }; |
| |
| enum |
| { |
| NORMAL =0, |
| FACE =1, |
| CURSOR =2 |
| }; |
| |
| enum |
| { |
| Mopen, |
| Mread, |
| Mwrite, |
| Mcopy, |
| Mchar, |
| Mpixels, |
| Mclose, |
| Mexit |
| }; |
| |
| enum |
| { |
| Blue = 54 |
| }; |
| |
| char *menu3str[] = { |
| "open", |
| "read", |
| "write", |
| "copy", |
| "char", |
| "pixels", |
| "close", |
| "exit", |
| 0 |
| }; |
| |
| Menu menu3 = { |
| menu3str |
| }; |
| |
| Cursor sweep0 = { |
| {-7, -7}, |
| {0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, |
| 0x03, 0xC0, 0x03, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xC0, 0x03, 0xC0, |
| 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0, 0x03, 0xC0}, |
| {0x00, 0x00, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, |
| 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x7F, 0xFE, |
| 0x7F, 0xFE, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, |
| 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x00} |
| }; |
| |
| Cursor box = { |
| {-7, -7}, |
| {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, |
| 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, |
| {0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, |
| 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, |
| 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, 0x70, 0x0E, |
| 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x00, 0x00} |
| }; |
| |
| Cursor sight = { |
| {-7, -7}, |
| {0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, |
| 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, |
| 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, |
| 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,}, |
| {0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, |
| 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, |
| 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, |
| 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,} |
| }; |
| |
| Cursor pixel = { |
| {-7, -7}, |
| {0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xf8, 0x1f, |
| 0xf0, 0x0f, 0xe0, 0x07, 0xe0, 0x07, 0xfe, 0x7f, |
| 0xfe, 0x7f, 0xe0, 0x07, 0xe0, 0x07, 0xf0, 0x0f, |
| 0x78, 0x1f, 0x7f, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, }, |
| {0x00, 0x00, 0x0f, 0xf0, 0x31, 0x8c, 0x21, 0x84, |
| 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x40, 0x02, |
| 0x40, 0x02, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, |
| 0x21, 0x84, 0x31, 0x8c, 0x0f, 0xf0, 0x00, 0x00, } |
| }; |
| |
| Cursor busy = { |
| {-7, -7}, |
| {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x0c, 0x00, 0x8e, 0x1d, 0xc7, |
| 0xff, 0xe3, 0xff, 0xf3, 0xff, 0xff, 0x7f, 0xfe, |
| 0x3f, 0xf8, 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00,}, |
| {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x82, |
| 0x04, 0x41, 0xff, 0xe1, 0x5f, 0xf1, 0x3f, 0xfe, |
| 0x17, 0xf0, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00,} |
| }; |
| |
| Cursor skull = { |
| {-7,-7}, |
| {0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0xe7, 0xe7, |
| 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xf8, |
| 0x0f, 0xf0, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, |
| 0xef, 0xf7, 0xc7, 0xe3, 0x00, 0x00, 0x00, 0x00,}, |
| {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, |
| 0xE7, 0xE7, 0x3F, 0xFC, 0x0F, 0xF0, 0x0D, 0xB0, |
| 0x07, 0xE0, 0x06, 0x60, 0x37, 0xEC, 0xE4, 0x27, |
| 0xC3, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,} |
| }; |
| |
| Rectangle cntlr; /* control region */ |
| Rectangle editr; /* editing region */ |
| Rectangle textr; /* text region */ |
| Thing *thing; |
| Mouse mouse; |
| char hex[] = "0123456789abcdefABCDEF"; |
| jmp_buf err; |
| char *file; |
| int mag; |
| int but1val = 0; |
| int but2val = 255; |
| int invert = 0; |
| Image *values[256]; |
| Image *greyvalues[256]; |
| uchar data[8192]; |
| |
| Thing* tget(char*); |
| void mesg(char*, ...); |
| void drawthing(Thing*, int); |
| void xselect(void); |
| void menu(void); |
| void error(Display*, char*); |
| void buttons(int); |
| void drawall(void); |
| void tclose1(Thing*); |
| |
| void |
| usage(void) |
| { |
| fprint(2, "usage: tweak [-W winsize] file...\n"); |
| exits("usage"); |
| } |
| |
| void |
| main(volatile int argc, char **volatile argv) |
| { |
| volatile int i; |
| Event e; |
| Thing *t; |
| |
| ARGBEGIN{ |
| case 'W': |
| winsize = EARGF(usage()); |
| break; |
| default: |
| usage(); |
| }ARGEND |
| mag = Mag; |
| if(initdraw(error, 0, "tweak") < 0){ |
| fprint(2, "tweak: initdraw failed: %r\n"); |
| exits("initdraw"); |
| } |
| for(i=0; i<256; i++){ |
| values[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, cmap2rgba(i)); |
| greyvalues[i] = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, (i<<24)|(i<<16)|(i<<8)|0xFF); |
| if(values[i] == 0 || greyvalues[i] == 0) |
| drawerror(display, "can't allocate image"); |
| } |
| einit(Emouse|Ekeyboard); |
| eresized(0); |
| i = 0; |
| setjmp(err); |
| for(; i<argc; i++){ |
| file = argv[i]; |
| t = tget(argv[i]); |
| if(t) |
| drawthing(t, 1); |
| flushimage(display, 1); |
| } |
| file = 0; |
| setjmp(err); |
| for(;;) |
| switch(event(&e)){ |
| case Ekeyboard: |
| break; |
| case Emouse: |
| mouse = e.mouse; |
| if(mouse.buttons & 3){ |
| xselect(); |
| break; |
| } |
| if(mouse.buttons & 4) |
| menu(); |
| } |
| } |
| |
| int |
| xlog2(int n) |
| { |
| int i; |
| |
| for(i=0; (1<<i) <= n; i++) |
| if((1<<i) == n) |
| return i; |
| fprint(2, "log2 %d = 0\n", n); |
| return 0; |
| } |
| |
| void |
| error(Display *d, char *s) |
| { |
| USED(d); |
| |
| if(file) |
| mesg("can't read %s: %s: %r", file, s); |
| else |
| mesg("/dev/bitblt error: %s", s); |
| if(err[0]) |
| longjmp(err, 1); |
| exits(s); |
| } |
| |
| void |
| redraw(Thing *t) |
| { |
| Thing *nt; |
| Point p; |
| |
| if(thing==0 || thing==t) |
| draw(screen, editr, display->white, nil, ZP); |
| if(thing == 0) |
| return; |
| if(thing != t){ |
| for(nt=thing; nt->next!=t; nt=nt->next) |
| ; |
| draw(screen, Rect(screen->r.min.x, nt->er.max.y, editr.max.x, editr.max.y), |
| display->white, nil, ZP); |
| } |
| for(nt=t; nt; nt=nt->next){ |
| drawthing(nt, 0); |
| if(nt->next == 0){ |
| p = Pt(editr.min.x, nt->er.max.y); |
| draw(screen, Rpt(p, editr.max), display->white, nil, ZP); |
| } |
| } |
| mesg(""); |
| } |
| |
| void |
| eresized(int new) |
| { |
| if(new && getwindow(display, Refnone) < 0) |
| error(display, "can't reattach to window"); |
| cntlr = insetrect(screen->clipr, 1); |
| editr = cntlr; |
| textr = editr; |
| textr.min.y = textr.max.y - font->height; |
| cntlr.max.y = cntlr.min.y + font->height; |
| editr.min.y = cntlr.max.y+1; |
| editr.max.y = textr.min.y-1; |
| draw(screen, screen->clipr, display->white, nil, ZP); |
| draw(screen, Rect(editr.min.x, editr.max.y, editr.max.x+1, editr.max.y+1), display->black, nil, ZP); |
| replclipr(screen, 0, editr); |
| drawall(); |
| } |
| |
| void |
| mesgstr(Point p, int line, char *s) |
| { |
| Rectangle c, r; |
| |
| r.min = p; |
| r.min.y += line*font->height; |
| r.max.y = r.min.y+font->height; |
| r.max.x = editr.max.x; |
| c = screen->clipr; |
| replclipr(screen, 0, r); |
| draw(screen, r, values[0xDD], nil, ZP); |
| r.min.x++; |
| string(screen, r.min, display->black, ZP, font, s); |
| replclipr(screen, 0, c); |
| flushimage(display, 1); |
| } |
| |
| void |
| mesg(char *fmt, ...) |
| { |
| char buf[1024]; |
| va_list arg; |
| |
| va_start(arg, fmt); |
| vseprint(buf, buf+sizeof(buf), fmt, arg); |
| va_end(arg); |
| mesgstr(textr.min, 0, buf); |
| } |
| |
| void |
| tmesg(Thing *t, int line, char *fmt, ...) |
| { |
| char buf[1024]; |
| va_list arg; |
| |
| va_start(arg, fmt); |
| vseprint(buf, buf+sizeof(buf), fmt, arg); |
| va_end(arg); |
| mesgstr(t->tr.min, line, buf); |
| } |
| |
| |
| void |
| scntl(char *l) |
| { |
| sprint(l, "mag: %d but1: %d but2: %d invert-on-copy: %c", mag, but1val, but2val, "ny"[invert]); |
| } |
| |
| void |
| cntl(void) |
| { |
| char buf[256]; |
| |
| scntl(buf); |
| mesgstr(cntlr.min, 0, buf); |
| } |
| |
| void |
| stext(Thing *t, char *l0, char *l1) |
| { |
| Fontchar *fc; |
| char buf[256]; |
| |
| l1[0] = 0; |
| sprint(buf, "depth:%d r:%d %d %d %d ", |
| t->b->depth, t->b->r.min.x, t->b->r.min.y, |
| t->b->r.max.x, t->b->r.max.y); |
| if(t->parent) |
| sprint(buf+strlen(buf), "mag: %d ", t->mag); |
| sprint(l0, "%s file: %s", buf, t->name); |
| if(t->c >= 0){ |
| fc = &t->parent->s->info[t->c]; |
| sprint(l1, "c(hex): %x c(char): %C x: %d " |
| "top: %d bottom: %d left: %d width: %d iwidth: %d", |
| (int)(t->c+t->parent->off), (int)(t->c+t->parent->off), |
| fc->x, fc->top, fc->bottom, fc->left, |
| fc->width, Dx(t->b->r)); |
| }else if(t->s) |
| sprint(l1, "offset(hex): %ux n:%d height:%d ascent:%d", |
| t->off, t->s->n, t->s->height, t->s->ascent); |
| } |
| |
| void |
| text(Thing *t) |
| { |
| char l0[256], l1[256]; |
| |
| stext(t, l0, l1); |
| tmesg(t, 0, l0); |
| if(l1[0]) |
| tmesg(t, 1, l1); |
| } |
| |
| void |
| drawall(void) |
| { |
| Thing *t; |
| |
| cntl(); |
| for(t=thing; t; t=t->next) |
| drawthing(t, 0); |
| } |
| |
| int |
| value(Image *b, int x) |
| { |
| int v, l, w; |
| uchar mask; |
| |
| w = b->depth; |
| if(w > 8){ |
| mesg("ldepth too large"); |
| return 0; |
| } |
| l = xlog2(w); |
| mask = (1<<w)-1; /* ones at right end of word */ |
| x -= b->r.min.x&~(7>>l); /* adjust x relative to first pixel */ |
| v = data[x>>(3-l)]; |
| v >>= ((7>>l)<<l) - ((x&(7>>l))<<l); /* pixel at right end of word */ |
| v &= mask; /* pixel at right end of word */ |
| return v; |
| } |
| |
| int |
| bvalue(int v, int d) |
| { |
| v &= (1<<d)-1; |
| if(d > screen->depth) |
| v >>= d - screen->depth; |
| else |
| while(d < screen->depth && d < 8){ |
| v |= v << d; |
| d <<= 1; |
| } |
| if(v<0 || v>255){ |
| mesg("internal error: bad color"); |
| return Blue; |
| } |
| return v; |
| } |
| |
| void |
| drawthing(Thing *nt, int link) |
| { |
| int n, nl, nf, i, x, y, sx, sy, fdx, dx, dy, v; |
| Thing *t; |
| Subfont *s; |
| Image *b, *col; |
| Point p, p1, p2; |
| |
| if(link){ |
| nt->next = 0; |
| if(thing == 0){ |
| thing = nt; |
| y = editr.min.y; |
| }else{ |
| for(t=thing; t->next; t=t->next) |
| ; |
| t->next = nt; |
| y = t->er.max.y; |
| } |
| }else{ |
| if(thing == nt) |
| y = editr.min.y; |
| else{ |
| for(t=thing; t->next!=nt; t=t->next) |
| ; |
| y = t->er.max.y; |
| } |
| } |
| s = nt->s; |
| b = nt->b; |
| nl = font->height; |
| if(s || nt->c>=0) |
| nl += font->height; |
| fdx = Dx(editr) - 2*Border; |
| dx = Dx(b->r); |
| dy = Dy(b->r); |
| if(nt->mag > 1){ |
| dx *= nt->mag; |
| dy *= nt->mag; |
| fdx -= fdx%nt->mag; |
| } |
| nf = 1 + dx/fdx; |
| nt->er.min.y = y; |
| nt->er.min.x = editr.min.x; |
| nt->er.max.x = nt->er.min.x + Border + dx + Border; |
| if(nt->er.max.x > editr.max.x) |
| nt->er.max.x = editr.max.x; |
| nt->er.max.y = nt->er.min.y + Border + nf*(dy+Border); |
| nt->r = insetrect(nt->er, Border); |
| nt->er.max.x = editr.max.x; |
| draw(screen, nt->er, display->white, nil, ZP); |
| for(i=0; i<nf; i++){ |
| p1 = Pt(nt->r.min.x-1, nt->r.min.y+i*(Border+dy)); |
| /* draw portion of bitmap */ |
| p = Pt(p1.x+1, p1.y); |
| if(nt->mag == 1) |
| draw(screen, Rect(p.x, p.y, p.x+fdx+Dx(b->r), p.y+Dy(b->r)), |
| b, nil, Pt(b->r.min.x+i*fdx, b->r.min.y)); |
| else{ |
| for(y=b->r.min.y; y<b->r.max.y; y++){ |
| sy = p.y+(y-b->r.min.y)*nt->mag; |
| if((n=unloadimage(b, Rect(b->r.min.x, y, b->r.max.x, y+1), data, sizeof data)) < 0) |
| fprint(2, "unloadimage: %r\n"); |
| for(x=b->r.min.x+i*(fdx/nt->mag); x<b->r.max.x; x++){ |
| sx = p.x+(x-i*(fdx/nt->mag)-b->r.min.x)*nt->mag; |
| if(sx >= nt->r.max.x) |
| break; |
| v = bvalue(value(b, x), b->depth); |
| if(v == 255) |
| continue; |
| if(b->chan == GREY8) |
| draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag), |
| greyvalues[v], nil, ZP); |
| else |
| draw(screen, Rect(sx, sy, sx+nt->mag, sy+nt->mag), |
| values[v], nil, ZP); |
| } |
| |
| } |
| } |
| /* line down left */ |
| if(i == 0) |
| col = display->black; |
| else |
| col = display->white; |
| draw(screen, Rect(p1.x, p1.y, p1.x+1, p1.y+dy+Border), col, nil, ZP); |
| /* line across top */ |
| draw(screen, Rect(p1.x, p1.y-1, nt->r.max.x+Border, p1.y), display->black, nil, ZP); |
| p2 = p1; |
| if(i == nf-1){ |
| p2.x += 1 + dx%fdx; |
| col = display->black; |
| }else{ |
| p2.x = nt->r.max.x; |
| col = display->white; |
| } |
| /* line down right */ |
| draw(screen, Rect(p2.x, p2.y, p2.x+1, p2.y+dy+Border), col, nil, ZP); |
| /* line across bottom */ |
| if(i == nf-1){ |
| p1.y += Border+dy; |
| draw(screen, Rect(p1.x, p1.y-1, p2.x,p1.y), display->black, nil, ZP); |
| } |
| } |
| nt->tr.min.x = editr.min.x; |
| nt->tr.max.x = editr.max.x; |
| nt->tr.min.y = nt->er.max.y + Border; |
| nt->tr.max.y = nt->tr.min.y + nl; |
| nt->er.max.y = nt->tr.max.y + Border; |
| text(nt); |
| } |
| |
| int |
| tohex(int c) |
| { |
| if('0'<=c && c<='9') |
| return c - '0'; |
| if('a'<=c && c<='f') |
| return 10 + (c - 'a'); |
| if('A'<=c && c<='F') |
| return 10 + (c - 'A'); |
| return 0; |
| } |
| |
| Thing* |
| tget(char *file) |
| { |
| int i, j, fd, face, x, y, c, chan; |
| Image *b; |
| Subfont *s; |
| Thing *t; |
| Dir *volatile d; |
| jmp_buf oerr; |
| uchar buf[256]; |
| char *data; |
| |
| buf[0] = '\0'; |
| errstr((char*)buf, sizeof buf); /* flush pending error message */ |
| memmove(oerr, err, sizeof err); |
| d = nil; |
| if(setjmp(err)){ |
| Err: |
| free(d); |
| memmove(err, oerr, sizeof err); |
| return 0; |
| } |
| fd = open(file, OREAD); |
| if(fd < 0){ |
| mesg("can't open %s: %r", file); |
| goto Err; |
| } |
| d = dirfstat(fd); |
| if(d == nil){ |
| mesg("can't stat bitmap file %s: %r", file); |
| close(fd); |
| goto Err; |
| } |
| if(read(fd, buf, 11) != 11){ |
| mesg("can't read %s: %r", file); |
| close(fd); |
| goto Err; |
| } |
| seek(fd, 0, 0); |
| data = (char*)buf; |
| if(*data == '{') |
| data++; |
| if(memcmp(data, "0x", 2)==0 && data[4]==','){ |
| /* |
| * cursor file |
| */ |
| face = CURSOR; |
| s = 0; |
| data = malloc(d->length+1); |
| if(data == 0){ |
| mesg("can't malloc buffer: %r"); |
| close(fd); |
| goto Err; |
| } |
| data[d->length] = 0; |
| if(read(fd, data, d->length) != d->length){ |
| mesg("can't read cursor file %s: %r", file); |
| close(fd); |
| goto Err; |
| } |
| b = allocimage(display, Rect(0, 0, 16, 32), GREY1, 0, DNofill); |
| if(b == 0){ |
| mesg("image alloc failed file %s: %r", file); |
| free(data); |
| close(fd); |
| goto Err; |
| } |
| i = 0; |
| for(x=0;x<64; ){ |
| if((c=data[i]) == '\0') |
| goto ill; |
| if(c=='0' && data[i+1] == 'x'){ |
| i += 2; |
| continue; |
| } |
| if(strchr(hex, c)){ |
| buf[x++] = (tohex(c)<<4) | tohex(data[i+1]); |
| i += 2; |
| continue; |
| } |
| i++; |
| } |
| loadimage(b, Rect(0, 0, 16, 32), buf, sizeof buf); |
| free(data); |
| }else if(memcmp(buf, "0x", 2)==0){ |
| /* |
| * face file |
| */ |
| face = FACE; |
| s = 0; |
| data = malloc(d->length+1); |
| if(data == 0){ |
| mesg("can't malloc buffer: %r"); |
| close(fd); |
| goto Err; |
| } |
| data[d->length] = 0; |
| if(read(fd, data, d->length) != d->length){ |
| mesg("can't read bitmap file %s: %r", file); |
| close(fd); |
| goto Err; |
| } |
| for(y=0,i=0; i<d->length; i++) |
| if(data[i] == '\n') |
| y++; |
| if(y == 0){ |
| ill: |
| mesg("ill-formed face file %s", file); |
| close(fd); |
| free(data); |
| goto Err; |
| } |
| for(x=0,i=0; (c=data[i])!='\n'; ){ |
| if(c==',' || c==' ' || c=='\t'){ |
| i++; |
| continue; |
| } |
| if(c=='0' && data[i+1] == 'x'){ |
| i += 2; |
| continue; |
| } |
| if(strchr(hex, c)){ |
| x += 4; |
| i++; |
| continue; |
| } |
| goto ill; |
| } |
| if(x % y) |
| goto ill; |
| switch(x / y){ |
| default: |
| goto ill; |
| case 1: |
| chan = GREY1; |
| break; |
| case 2: |
| chan = GREY2; |
| break; |
| case 4: |
| chan = GREY4; |
| break; |
| case 8: |
| chan = CMAP8; |
| break; |
| } |
| b = allocimage(display, Rect(0, 0, y, y), chan, 0, -1); |
| if(b == 0){ |
| mesg("image alloc failed file %s: %r", file); |
| free(data); |
| close(fd); |
| goto Err; |
| } |
| i = 0; |
| for(j=0; j<y; j++){ |
| for(x=0; (c=data[i])!='\n'; ){ |
| if(c=='0' && data[i+1] == 'x'){ |
| i += 2; |
| continue; |
| } |
| if(strchr(hex, c)){ |
| buf[x++] = ~((tohex(c)<<4) | tohex(data[i+1])); |
| i += 2; |
| continue; |
| } |
| i++; |
| } |
| i++; |
| loadimage(b, Rect(0, j, y, j+1), buf, sizeof buf); |
| } |
| free(data); |
| }else{ |
| face = NORMAL; |
| s = 0; |
| b = readimage(display, fd, 0); |
| if(b == 0){ |
| mesg("can't read bitmap file %s: %r", file); |
| close(fd); |
| goto Err; |
| } |
| if(seek(fd, 0, 1) < d->length) |
| s = readsubfonti(display, file, fd, b, 0); |
| } |
| close(fd); |
| t = malloc(sizeof(Thing)); |
| if(t == 0){ |
| nomem: |
| mesg("malloc failed: %r"); |
| if(s) |
| freesubfont(s); |
| else |
| freeimage(b); |
| goto Err; |
| } |
| t->name = strdup(file); |
| if(t->name == 0){ |
| free(t); |
| goto nomem; |
| } |
| t->b = b; |
| t->s = s; |
| t->face = face; |
| t->mod = 0; |
| t->parent = 0; |
| t->c = -1; |
| t->mag = 1; |
| t->off = 0; |
| memmove(err, oerr, sizeof err); |
| return t; |
| } |
| |
| int |
| atline(int x, Point p, char *line, char *buf) |
| { |
| char *s, *c, *word, *hit; |
| int w, wasblank; |
| Rune r; |
| |
| wasblank = 1; |
| hit = 0; |
| word = 0; |
| for(s=line; *s; s+=w){ |
| w = chartorune(&r, s); |
| x += runestringnwidth(font, &r, 1); |
| if(wasblank && r!=' ') |
| word = s; |
| wasblank = 0; |
| if(r == ' '){ |
| if(x >= p.x) |
| break; |
| wasblank = 1; |
| } |
| if(r == ':') |
| hit = word; |
| } |
| if(x < p.x) |
| return 0; |
| c = utfrune(hit, ':'); |
| strncpy(buf, hit, c-hit); |
| buf[c-hit] = 0; |
| return 1; |
| } |
| |
| int |
| attext(Thing *t, Point p, char *buf) |
| { |
| char l0[256], l1[256]; |
| |
| if(!ptinrect(p, t->tr)) |
| return 0; |
| stext(t, l0, l1); |
| if(p.y < t->tr.min.y+font->height) |
| return atline(t->r.min.x, p, l0, buf); |
| else |
| return atline(t->r.min.x, p, l1, buf); |
| } |
| |
| int |
| type(char *buf, char *tag) |
| { |
| Rune r; |
| char *p; |
| |
| esetcursor(&busy); |
| p = buf; |
| for(;;){ |
| *p = 0; |
| mesg("%s: %s", tag, buf); |
| r = ekbd(); |
| switch(r){ |
| case '\n': |
| mesg(""); |
| esetcursor(0); |
| return p-buf; |
| case 0x15: /* control-U */ |
| p = buf; |
| break; |
| case '\b': |
| if(p > buf) |
| --p; |
| break; |
| default: |
| p += runetochar(p, &r); |
| } |
| } |
| /* return 0; shut up compiler */ |
| } |
| |
| void |
| textedit(Thing *t, char *tag) |
| { |
| char buf[256]; |
| char *s; |
| Image *b; |
| Subfont *f; |
| Fontchar *fc, *nfc; |
| Rectangle r; |
| ulong chan; |
| int i, ld, d, w, c, doredraw, fdx, x; |
| Thing *nt; |
| |
| buttons(Up); |
| if(type(buf, tag) == 0) |
| return; |
| if(strcmp(tag, "file") == 0){ |
| for(s=buf; *s; s++) |
| if(*s <= ' '){ |
| mesg("illegal file name"); |
| return; |
| } |
| if(strcmp(t->name, buf) != 0){ |
| if(t->parent) |
| t->parent->mod = 1; |
| else |
| t->mod = 1; |
| } |
| for(nt=thing; nt; nt=nt->next) |
| if(t==nt || t->parent==nt || nt->parent==t){ |
| free(nt->name); |
| nt->name = strdup(buf); |
| if(nt->name == 0){ |
| mesg("malloc failed: %r"); |
| return; |
| } |
| text(nt); |
| } |
| return; |
| } |
| if(strcmp(tag, "depth") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (d=atoi(buf))<0 || d>8 || xlog2(d)<0){ |
| mesg("illegal ldepth"); |
| return; |
| } |
| if(d == t->b->depth) |
| return; |
| if(t->parent) |
| t->parent->mod = 1; |
| else |
| t->mod = 1; |
| if(d == 8) |
| chan = CMAP8; |
| else |
| chan = CHAN1(CGrey, d); |
| for(nt=thing; nt; nt=nt->next){ |
| if(nt!=t && nt!=t->parent && nt->parent!=t) |
| continue; |
| b = allocimage(display, nt->b->r, chan, 0, 0); |
| if(b == 0){ |
| nobmem: |
| mesg("image alloc failed: %r"); |
| return; |
| } |
| draw(b, b->r, nt->b, nil, nt->b->r.min); |
| freeimage(nt->b); |
| nt->b = b; |
| if(nt->s){ |
| b = allocimage(display, nt->b->r, chan, 0, -1); |
| if(b == 0) |
| goto nobmem; |
| draw(b, b->r, nt->b, nil, nt->b->r.min); |
| f = allocsubfont(t->name, nt->s->n, nt->s->height, nt->s->ascent, nt->s->info, b); |
| if(f == 0){ |
| nofmem: |
| freeimage(b); |
| mesg("can't make subfont: %r"); |
| return; |
| } |
| nt->s->info = 0; /* prevent it being freed */ |
| nt->s->bits = 0; |
| freesubfont(nt->s); |
| nt->s = f; |
| } |
| drawthing(nt, 0); |
| } |
| return; |
| } |
| if(strcmp(tag, "mag") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<=0 || ld>Maxmag){ |
| mesg("illegal magnification"); |
| return; |
| } |
| if(t->mag == ld) |
| return; |
| t->mag = ld; |
| redraw(t); |
| return; |
| } |
| if(strcmp(tag, "r") == 0){ |
| if(t->s){ |
| mesg("can't change rectangle of subfont\n"); |
| return; |
| } |
| s = buf; |
| r.min.x = strtoul(s, &s, 0); |
| r.min.y = strtoul(s, &s, 0); |
| r.max.x = strtoul(s, &s, 0); |
| r.max.y = strtoul(s, &s, 0); |
| if(Dx(r)<=0 || Dy(r)<=0){ |
| mesg("illegal rectangle"); |
| return; |
| } |
| if(t->parent) |
| t = t->parent; |
| for(nt=thing; nt; nt=nt->next){ |
| if(nt->parent==t && !rectinrect(nt->b->r, r)) |
| tclose1(nt); |
| } |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| draw(b, r, t->b, nil, r.min); |
| freeimage(t->b); |
| t->b = b; |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| redraw(t); |
| t->mod = 1; |
| return; |
| } |
| if(strcmp(tag, "ascent") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0 || ld>t->s->height){ |
| mesg("illegal ascent"); |
| return; |
| } |
| if(t->s->ascent == ld) |
| return; |
| t->s->ascent = ld; |
| text(t); |
| t->mod = 1; |
| return; |
| } |
| if(strcmp(tag, "height") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){ |
| mesg("illegal height"); |
| return; |
| } |
| if(t->s->height == ld) |
| return; |
| t->s->height = ld; |
| text(t); |
| t->mod = 1; |
| return; |
| } |
| if(strcmp(tag, "left")==0 || strcmp(tag, "width") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (ld=atoi(buf))<0){ |
| mesg("illegal value"); |
| return; |
| } |
| fc = &t->parent->s->info[t->c]; |
| if(strcmp(tag, "left")==0){ |
| if(fc->left == ld) |
| return; |
| fc->left = ld; |
| }else{ |
| if(fc->width == ld) |
| return; |
| fc->width = ld; |
| } |
| text(t); |
| t->parent->mod = 1; |
| return; |
| } |
| if(strcmp(tag, "offset(hex)") == 0){ |
| if(!strchr(hex, buf[0])){ |
| illoff: |
| mesg("illegal offset"); |
| return; |
| } |
| s = 0; |
| ld = strtoul(buf, &s, 16); |
| if(*s) |
| goto illoff; |
| t->off = ld; |
| text(t); |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent == t) |
| text(nt); |
| return; |
| } |
| if(strcmp(tag, "n") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<=0){ |
| mesg("illegal n"); |
| return; |
| } |
| f = t->s; |
| if(w == f->n) |
| return; |
| doredraw = 0; |
| again: |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent == t){ |
| doredraw = 1; |
| tclose1(nt); |
| goto again; |
| } |
| r = t->b->r; |
| if(w < f->n) |
| r.max.x = f->info[w].x; |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| draw(b, b->r, t->b, nil, r.min); |
| fdx = Dx(editr) - 2*Border; |
| if(Dx(t->b->r)/fdx != Dx(b->r)/fdx) |
| doredraw = 1; |
| freeimage(t->b); |
| t->b = b; |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| draw(b, b->r, t->b, nil, r.min); |
| nfc = malloc((w+1)*sizeof(Fontchar)); |
| if(nfc == 0){ |
| mesg("malloc failed"); |
| freeimage(b); |
| return; |
| } |
| fc = f->info; |
| for(i=0; i<=w && i<=f->n; i++) |
| nfc[i] = fc[i]; |
| if(w+1 < i) |
| memset(nfc+i, 0, ((w+1)-i)*sizeof(Fontchar)); |
| x = fc[f->n].x; |
| for(; i<=w; i++) |
| nfc[i].x = x; |
| f = allocsubfont(t->name, w, f->height, f->ascent, nfc, b); |
| if(f == 0) |
| goto nofmem; |
| t->s->bits = nil; /* don't free it */ |
| freesubfont(t->s); |
| f->info = nfc; |
| t->s = f; |
| if(doredraw) |
| redraw(thing); |
| else |
| drawthing(t, 0); |
| t->mod = 1; |
| return; |
| } |
| if(strcmp(tag, "iwidth") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (w=atoi(buf))<0){ |
| mesg("illegal iwidth"); |
| return; |
| } |
| w -= Dx(t->b->r); |
| if(w == 0) |
| return; |
| r = t->parent->b->r; |
| r.max.x += w; |
| c = t->c; |
| t = t->parent; |
| f = t->s; |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| fc = &f->info[c]; |
| draw(b, Rect(b->r.min.x, b->r.min.y, |
| b->r.min.x+(fc[1].x-t->b->r.min.x), b->r.min.y+Dy(t->b->r)), |
| t->b, nil, t->b->r.min); |
| 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)), |
| t->b, nil, Pt(fc[1].x, t->b->r.min.y)); |
| fdx = Dx(editr) - 2*Border; |
| doredraw = 0; |
| if(Dx(t->b->r)/fdx != Dx(b->r)/fdx) |
| doredraw = 1; |
| freeimage(t->b); |
| t->b = b; |
| b = allocimage(display, r, t->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| draw(b, b->r, t->b, nil, t->b->r.min); |
| fc = &f->info[c+1]; |
| for(i=c+1; i<=f->n; i++, fc++) |
| fc->x += w; |
| f = allocsubfont(t->name, f->n, f->height, f->ascent, |
| f->info, b); |
| if(f == 0) |
| goto nofmem; |
| /* t->s and f share info; free carefully */ |
| fc = f->info; |
| t->s->bits = nil; |
| t->s->info = 0; |
| freesubfont(t->s); |
| f->info = fc; |
| t->s = f; |
| if(doredraw) |
| redraw(t); |
| else |
| drawthing(t, 0); |
| /* redraw all affected chars */ |
| for(nt=thing; nt; nt=nt->next){ |
| if(nt->parent!=t || nt->c<c) |
| continue; |
| fc = &f->info[nt->c]; |
| r.min.x = fc[0].x; |
| r.min.y = nt->b->r.min.y; |
| r.max.x = fc[1].x; |
| r.max.y = nt->b->r.max.y; |
| b = allocimage(display, r, nt->b->chan, 0, 0); |
| if(b == 0) |
| goto nobmem; |
| draw(b, r, t->b, nil, r.min); |
| doredraw = 0; |
| if(Dx(nt->b->r)/fdx != Dx(b->r)/fdx) |
| doredraw = 1; |
| freeimage(nt->b); |
| nt->b = b; |
| if(c != nt->c) |
| text(nt); |
| else{ |
| if(doredraw) |
| redraw(nt); |
| else |
| drawthing(nt, 0); |
| } |
| } |
| t->mod = 1; |
| return; |
| } |
| mesg("cannot edit %s in file %s", tag, t->name); |
| } |
| |
| void |
| cntledit(char *tag) |
| { |
| char buf[256]; |
| ulong l; |
| |
| buttons(Up); |
| if(type(buf, tag) == 0) |
| return; |
| if(strcmp(tag, "mag") == 0){ |
| if(buf[0]<'0' || '9'<buf[0] || (l=atoi(buf))<=0 || l>Maxmag){ |
| mesg("illegal magnification"); |
| return; |
| } |
| mag = l; |
| cntl(); |
| return; |
| } |
| if(strcmp(tag, "but1")==0 |
| || strcmp(tag, "but2")==0){ |
| if(buf[0]<'0' || '9'<buf[0] || (long)(l=atoi(buf))<0 || l>255){ |
| mesg("illegal value"); |
| return; |
| } |
| if(strcmp(tag, "but1") == 0) |
| but1val = l; |
| else if(strcmp(tag, "but2") == 0) |
| but2val = l; |
| cntl(); |
| return; |
| } |
| if(strcmp(tag, "invert-on-copy")==0){ |
| if(buf[0]=='y' || buf[0]=='1') |
| invert = 1; |
| else if(buf[0]=='n' || buf[0]=='0') |
| invert = 0; |
| else{ |
| mesg("illegal value"); |
| return; |
| } |
| cntl(); |
| return; |
| } |
| mesg("cannot edit %s", tag); |
| } |
| |
| void |
| buttons(int ud) |
| { |
| while((mouse.buttons==0) != ud) |
| mouse = emouse(); |
| } |
| |
| Point |
| screenpt(Thing *t, Point realp) |
| { |
| int fdx, n; |
| Point p; |
| |
| fdx = Dx(editr)-2*Border; |
| if(t->mag > 1) |
| fdx -= fdx%t->mag; |
| p = mulpt(subpt(realp, t->b->r.min), t->mag); |
| if(fdx < Dx(t->b->r)*t->mag){ |
| n = p.x/fdx; |
| p.y += n * (Dy(t->b->r)*t->mag+Border); |
| p.x -= n * fdx; |
| } |
| p = addpt(p, t->r.min); |
| return p; |
| } |
| |
| Point |
| realpt(Thing *t, Point screenp) |
| { |
| int fdx, n, dy; |
| Point p; |
| |
| fdx = (Dx(editr)-2*Border); |
| if(t->mag > 1) |
| fdx -= fdx%t->mag; |
| p.y = screenp.y-t->r.min.y; |
| p.x = 0; |
| if(fdx < Dx(t->b->r)*t->mag){ |
| dy = Dy(t->b->r)*t->mag+Border; |
| n = (p.y/dy); |
| p.x = n * fdx; |
| p.y -= n * dy; |
| } |
| p.x += screenp.x-t->r.min.x; |
| p = addpt(divpt(p, t->mag), t->b->r.min); |
| return p; |
| } |
| |
| int |
| sweep(int but, Rectangle *r) |
| { |
| Thing *t; |
| Point p, q, lastq; |
| |
| esetcursor(&sweep0); |
| buttons(Down); |
| if(mouse.buttons != (1<<(but-1))){ |
| buttons(Up); |
| esetcursor(0); |
| return 0; |
| } |
| p = mouse.xy; |
| for(t=thing; t; t=t->next) |
| if(ptinrect(p, t->r)) |
| break; |
| if(t) |
| p = screenpt(t, realpt(t, p)); |
| r->min = p; |
| r->max = p; |
| esetcursor(&box); |
| lastq = ZP; |
| while(mouse.buttons == (1<<(but-1))){ |
| edrawgetrect(insetrect(*r, -Borderwidth), 1); |
| mouse = emouse(); |
| edrawgetrect(insetrect(*r, -Borderwidth), 0); |
| q = mouse.xy; |
| if(t) |
| q = screenpt(t, realpt(t, q)); |
| if(eqpt(q, lastq)) |
| continue; |
| *r = canonrect(Rpt(p, q)); |
| lastq = q; |
| } |
| esetcursor(0); |
| if(mouse.buttons){ |
| buttons(Up); |
| return 0; |
| } |
| return 1; |
| } |
| |
| void |
| openedit(Thing *t, Point pt, int c) |
| { |
| int x, y; |
| Point p; |
| Rectangle r; |
| Rectangle br; |
| Fontchar *fc; |
| Thing *nt; |
| |
| if(t->b->depth > 8){ |
| mesg("image has depth %d; can't handle >8", t->b->depth); |
| return; |
| } |
| br = t->b->r; |
| if(t->s == 0){ |
| c = -1; |
| /* if big enough to bother, sweep box */ |
| if(Dx(br)<=16 && Dy(br)<=16) |
| r = br; |
| else{ |
| if(!sweep(1, &r)) |
| return; |
| r = rectaddpt(r, subpt(br.min, t->r.min)); |
| if(!rectclip(&r, br)) |
| return; |
| if(Dx(br) <= 8){ |
| r.min.x = br.min.x; |
| r.max.x = br.max.x; |
| }else if(Dx(r) < 4){ |
| toosmall: |
| mesg("rectangle too small"); |
| return; |
| } |
| if(Dy(br) <= 8){ |
| r.min.y = br.min.y; |
| r.max.y = br.max.y; |
| }else if(Dy(r) < 4) |
| goto toosmall; |
| } |
| }else if(c >= 0){ |
| fc = &t->s->info[c]; |
| r.min.x = fc[0].x; |
| r.min.y = br.min.y; |
| r.max.x = fc[1].x; |
| r.max.y = br.min.y + Dy(br); |
| }else{ |
| /* just point at character */ |
| fc = t->s->info; |
| p = addpt(pt, subpt(br.min, t->r.min)); |
| x = br.min.x; |
| y = br.min.y; |
| for(c=0; c<t->s->n; c++,fc++){ |
| again: |
| r.min.x = x; |
| r.min.y = y; |
| r.max.x = x + fc[1].x - fc[0].x; |
| r.max.y = y + Dy(br); |
| if(ptinrect(p, r)) |
| goto found; |
| if(r.max.x >= br.min.x+Dx(t->r)){ |
| x -= Dx(t->r); |
| y += t->s->height; |
| if(fc[1].x > fc[0].x) |
| goto again; |
| } |
| x += fc[1].x - fc[0].x; |
| } |
| return; |
| found: |
| r = br; |
| r.min.x = fc[0].x; |
| r.max.x = fc[1].x; |
| } |
| nt = malloc(sizeof(Thing)); |
| if(nt == 0){ |
| nomem: |
| mesg("can't allocate: %r"); |
| return; |
| } |
| memset(nt, 0, sizeof(Thing)); |
| nt->c = c; |
| nt->b = allocimage(display, r, t->b->chan, 0, DNofill); |
| if(nt->b == 0){ |
| free(nt); |
| goto nomem; |
| } |
| draw(nt->b, r, t->b, nil, r.min); |
| nt->name = strdup(t->name); |
| if(nt->name == 0){ |
| freeimage(nt->b); |
| free(nt); |
| goto nomem; |
| } |
| nt->parent = t; |
| nt->mag = mag; |
| drawthing(nt, 1); |
| } |
| |
| void |
| ckinfo(Thing *t, Rectangle mod) |
| { |
| int i, j, k, top, bot, n, zero; |
| Fontchar *fc; |
| Rectangle r; |
| Image *b; |
| Thing *nt; |
| |
| if(t->parent) |
| t = t->parent; |
| if(t->s==0 || Dy(t->b->r)==0) |
| return; |
| b = 0; |
| /* check bounding boxes */ |
| fc = &t->s->info[0]; |
| r.min.y = t->b->r.min.y; |
| r.max.y = t->b->r.max.y; |
| for(i=0; i<t->s->n; i++, fc++){ |
| r.min.x = fc[0].x; |
| r.max.x = fc[1].x; |
| if(!rectXrect(mod, r)) |
| continue; |
| if(b==0 || Dx(b->r)<Dx(r)){ |
| if(b) |
| freeimage(b); |
| b = allocimage(display, rectsubpt(r, r.min), t->b->chan, 0, 0); |
| if(b == 0){ |
| mesg("can't alloc image"); |
| break; |
| } |
| } |
| draw(b, b->r, display->white, nil, ZP); |
| draw(b, b->r, t->b, nil, r.min); |
| top = 100000; |
| bot = 0; |
| n = 2+((Dx(r)/8)*t->b->depth); |
| for(j=0; j<b->r.max.y; j++){ |
| memset(data, 0, n); |
| unloadimage(b, Rect(b->r.min.x, j, b->r.max.x, j+1), data, sizeof data); |
| zero = 1; |
| for(k=0; k<n; k++) |
| if(data[k]){ |
| zero = 0; |
| break; |
| } |
| if(!zero){ |
| if(top > j) |
| top = j; |
| bot = j+1; |
| } |
| } |
| if(top > j) |
| top = 0; |
| if(top!=fc->top || bot!=fc->bottom){ |
| fc->top = top; |
| fc->bottom = bot; |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent==t && nt->c==i) |
| text(nt); |
| } |
| } |
| if(b) |
| freeimage(b); |
| } |
| |
| void |
| twidpix(Thing *t, Point p, int set) |
| { |
| Image *b, *v; |
| int c; |
| |
| b = t->b; |
| if(!ptinrect(p, b->r)) |
| return; |
| if(set) |
| c = but1val; |
| else |
| c = but2val; |
| if(b->chan == GREY8) |
| v = greyvalues[c]; |
| else |
| v = values[c]; |
| draw(b, Rect(p.x, p.y, p.x+1, p.y+1), v, nil, ZP); |
| p = screenpt(t, p); |
| draw(screen, Rect(p.x, p.y, p.x+t->mag, p.y+t->mag), v, nil, ZP); |
| } |
| |
| void |
| twiddle(Thing *t) |
| { |
| int set; |
| Point p, lastp; |
| Image *b; |
| Thing *nt; |
| Rectangle mod; |
| |
| if(mouse.buttons!=1 && mouse.buttons!=2){ |
| buttons(Up); |
| return; |
| } |
| set = mouse.buttons==1; |
| b = t->b; |
| lastp = addpt(b->r.min, Pt(-1, -1)); |
| mod = Rpt(addpt(b->r.max, Pt(1, 1)), lastp); |
| while(mouse.buttons){ |
| p = realpt(t, mouse.xy); |
| if(!eqpt(p, lastp)){ |
| lastp = p; |
| if(ptinrect(p, b->r)){ |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent==t->parent || nt==t->parent) |
| twidpix(nt, p, set); |
| if(t->parent) |
| t->parent->mod = 1; |
| else |
| t->mod = 1; |
| if(p.x < mod.min.x) |
| mod.min.x = p.x; |
| if(p.y < mod.min.y) |
| mod.min.y = p.y; |
| if(p.x >= mod.max.x) |
| mod.max.x = p.x+1; |
| if(p.y >= mod.max.y) |
| mod.max.y = p.y+1; |
| } |
| } |
| mouse = emouse(); |
| } |
| ckinfo(t, mod); |
| } |
| |
| void |
| xselect(void) |
| { |
| Thing *t; |
| char line[128], buf[128]; |
| Point p; |
| |
| if(ptinrect(mouse.xy, cntlr)){ |
| scntl(line); |
| if(atline(cntlr.min.x, mouse.xy, line, buf)){ |
| if(mouse.buttons == 1) |
| cntledit(buf); |
| else |
| buttons(Up); |
| return; |
| } |
| return; |
| } |
| for(t=thing; t; t=t->next){ |
| if(attext(t, mouse.xy, buf)){ |
| if(mouse.buttons == 1) |
| textedit(t, buf); |
| else |
| buttons(Up); |
| return; |
| } |
| if(ptinrect(mouse.xy, t->r)){ |
| if(t->parent == 0){ |
| if(mouse.buttons == 1){ |
| p = mouse.xy; |
| buttons(Up); |
| openedit(t, p, -1); |
| }else |
| buttons(Up); |
| return; |
| } |
| twiddle(t); |
| return; |
| } |
| } |
| } |
| |
| void |
| twrite(Thing *t) |
| { |
| int i, j, x, y, fd, ws, ld; |
| Biobuf buf; |
| Rectangle r; |
| |
| if(t->parent) |
| t = t->parent; |
| esetcursor(&busy); |
| fd = create(t->name, OWRITE, 0666); |
| if(fd < 0){ |
| mesg("can't write %s: %r", t->name); |
| return; |
| } |
| if(t->face && t->b->depth <= 4){ |
| r = t->b->r; |
| ld = xlog2(t->b->depth); |
| /* This heuristic reflects peculiarly different formats */ |
| ws = 4; |
| if(t->face == 2) /* cursor file */ |
| ws = 1; |
| else if(Dx(r)<32 || ld==0) |
| ws = 2; |
| Binit(&buf, fd, OWRITE); |
| if(t->face == CURSOR) |
| Bprint(&buf, "{"); |
| for(y=r.min.y; y<r.max.y; y++){ |
| unloadimage(t->b, Rect(r.min.x, y, r.max.x, y+1), data, sizeof data); |
| j = 0; |
| for(x=r.min.x; x<r.max.x; j+=ws,x+=ws*8>>ld){ |
| Bprint(&buf, "0x"); |
| for(i=0; i<ws; i++) |
| Bprint(&buf, "%.2x", data[i+j]); |
| Bprint(&buf, ", "); |
| } |
| if(t->face == CURSOR){ |
| switch(y){ |
| case 3: case 7: case 11: case 19: case 23: case 27: |
| Bprint(&buf, "\n "); |
| break; |
| case 15: |
| Bprint(&buf, "},\n{"); |
| break; |
| case 31: |
| Bprint(&buf, "}\n"); |
| break; |
| } |
| }else |
| Bprint(&buf, "\n"); |
| } |
| Bterm(&buf); |
| }else |
| if(writeimage(fd, t->b, 0)<0 || (t->s && writesubfont(fd, t->s)<0)){ |
| close(fd); |
| mesg("can't write %s: %r", t->name); |
| } |
| t->mod = 0; |
| close(fd); |
| mesg("wrote %s", t->name); |
| } |
| |
| void |
| tpixels(void) |
| { |
| Thing *t; |
| Point p, lastp; |
| |
| esetcursor(&pixel); |
| for(;;){ |
| buttons(Down); |
| if(mouse.buttons != 4) |
| break; |
| for(t=thing; t; t=t->next){ |
| lastp = Pt(-1, -1); |
| if(ptinrect(mouse.xy, t->r)){ |
| while(ptinrect(mouse.xy, t->r) && mouse.buttons==4){ |
| p = realpt(t, mouse.xy); |
| if(!eqpt(p, lastp)){ |
| if(p.y != lastp.y) |
| unloadimage(t->b, Rect(t->b->r.min.x, p.y, t->b->r.max.x, p.y+1), data, sizeof data); |
| mesg("[%d,%d] = %d=0x%ux", p.x, p.y, value(t->b, p.x), value(t->b, p.x)); |
| lastp = p; |
| } |
| mouse = emouse(); |
| } |
| goto Continue; |
| } |
| } |
| mouse = emouse(); |
| Continue:; |
| } |
| buttons(Up); |
| esetcursor(0); |
| } |
| |
| void |
| tclose1(Thing *t) |
| { |
| Thing *nt; |
| |
| if(t == thing) |
| thing = t->next; |
| else{ |
| for(nt=thing; nt->next!=t; nt=nt->next) |
| ; |
| nt->next = t->next; |
| } |
| do |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent == t){ |
| tclose1(nt); |
| break; |
| } |
| while(nt); |
| if(t->s) |
| freesubfont(t->s); |
| else |
| freeimage(t->b); |
| free(t->name); |
| free(t); |
| } |
| |
| void |
| tclose(Thing *t) |
| { |
| Thing *ct; |
| |
| if(t->mod){ |
| mesg("%s modified", t->name); |
| t->mod = 0; |
| return; |
| } |
| /* fiddle to save redrawing unmoved things */ |
| if(t == thing) |
| ct = 0; |
| else |
| for(ct=thing; ct; ct=ct->next) |
| if(ct->next==t || ct->next->parent==t) |
| break; |
| tclose1(t); |
| if(ct) |
| ct = ct->next; |
| else |
| ct = thing; |
| redraw(ct); |
| } |
| |
| void |
| tread(Thing *t) |
| { |
| Thing *nt, *new; |
| Fontchar *i; |
| Rectangle r; |
| int nclosed; |
| |
| if(t->parent) |
| t = t->parent; |
| new = tget(t->name); |
| if(new == 0) |
| return; |
| nclosed = 0; |
| again: |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent == t){ |
| if(!rectinrect(nt->b->r, new->b->r) |
| || new->b->depth!=nt->b->depth){ |
| closeit: |
| nclosed++; |
| nt->parent = 0; |
| tclose1(nt); |
| goto again; |
| } |
| if((t->s==0) != (new->s==0)) |
| goto closeit; |
| if((t->face==0) != (new->face==0)) |
| goto closeit; |
| if(t->s){ /* check same char */ |
| if(nt->c >= new->s->n) |
| goto closeit; |
| i = &new->s->info[nt->c]; |
| r.min.x = i[0].x; |
| r.max.x = i[1].x; |
| r.min.y = new->b->r.min.y; |
| r.max.y = new->b->r.max.y; |
| if(!eqrect(r, nt->b->r)) |
| goto closeit; |
| } |
| nt->parent = new; |
| draw(nt->b, nt->b->r, new->b, nil, nt->b->r.min); |
| } |
| new->next = t->next; |
| if(t == thing) |
| thing = new; |
| else{ |
| for(nt=thing; nt->next!=t; nt=nt->next) |
| ; |
| nt->next = new; |
| } |
| if(t->s) |
| freesubfont(t->s); |
| else |
| freeimage(t->b); |
| free(t->name); |
| free(t); |
| for(nt=thing; nt; nt=nt->next) |
| if(nt==new || nt->parent==new) |
| if(nclosed == 0) |
| drawthing(nt, 0); /* can draw in place */ |
| else{ |
| redraw(nt); /* must redraw all below */ |
| break; |
| } |
| } |
| |
| void |
| tchar(Thing *t) |
| { |
| char buf[256], *p; |
| Rune r; |
| ulong c, d; |
| |
| if(t->s == 0){ |
| t = t->parent; |
| if(t==0 || t->s==0){ |
| mesg("not a subfont"); |
| return; |
| } |
| } |
| if(type(buf, "char (hex or character or hex-hex)") == 0) |
| return; |
| if(utflen(buf) == 1){ |
| chartorune(&r, buf); |
| c = r; |
| d = r; |
| }else{ |
| if(!strchr(hex, buf[0])){ |
| mesg("illegal hex character"); |
| return; |
| } |
| c = strtoul(buf, 0, 16); |
| d = c; |
| p = utfrune(buf, '-'); |
| if(p){ |
| d = strtoul(p+1, 0, 16); |
| if(d < c){ |
| mesg("invalid range"); |
| return; |
| } |
| } |
| } |
| c -= t->off; |
| d -= t->off; |
| while(c <= d){ |
| if((long)c<0 || c>=t->s->n){ |
| mesg("0x%lux not in font %s", c+t->off, t->name); |
| return; |
| } |
| openedit(t, Pt(0, 0), c); |
| c++; |
| } |
| } |
| |
| void |
| apply(void (*f)(Thing*)) |
| { |
| Thing *t; |
| |
| esetcursor(&sight); |
| buttons(Down); |
| if(mouse.buttons == 4) |
| for(t=thing; t; t=t->next) |
| if(ptinrect(mouse.xy, t->er)){ |
| buttons(Up); |
| f(t); |
| break; |
| } |
| buttons(Up); |
| esetcursor(0); |
| } |
| |
| int |
| complement(Image *t) |
| { |
| int i, n; |
| uchar *buf; |
| |
| n = Dy(t->r)*bytesperline(t->r, t->depth); |
| buf = malloc(n); |
| if(buf == 0) |
| return 0; |
| unloadimage(t, t->r, buf, n); |
| for(i=0; i<n; i++) |
| buf[i] = ~buf[i]; |
| loadimage(t, t->r, buf, n); |
| free(buf); |
| return 1; |
| } |
| |
| void |
| copy(void) |
| { |
| Thing *st, *dt, *nt; |
| Rectangle sr, dr, fr; |
| Image *tmp; |
| Point p1, p2; |
| int but, up; |
| |
| if(!sweep(3, &sr)) |
| return; |
| for(st=thing; st; st=st->next) |
| if(rectXrect(sr, st->r)) |
| break; |
| if(st == 0) |
| return; |
| /* click gives full rectangle */ |
| if(Dx(sr)<4 && Dy(sr)<4) |
| sr = st->r; |
| rectclip(&sr, st->r); |
| p1 = realpt(st, sr.min); |
| p2 = realpt(st, Pt(sr.min.x, sr.max.y)); |
| up = 0; |
| if(p1.x != p2.x){ /* swept across a fold */ |
| onafold: |
| mesg("sweep spans a fold"); |
| goto Return; |
| } |
| p2 = realpt(st, sr.max); |
| sr.min = p1; |
| sr.max = p2; |
| fr.min = screenpt(st, sr.min); |
| fr.max = screenpt(st, sr.max); |
| p1 = subpt(p2, p1); /* diagonal */ |
| if(p1.x==0 || p1.y==0) |
| return; |
| border(screen, fr, -1, values[Blue], ZP); |
| esetcursor(&box); |
| for(; mouse.buttons==0; mouse=emouse()){ |
| for(dt=thing; dt; dt=dt->next) |
| if(ptinrect(mouse.xy, dt->er)) |
| break; |
| if(up) |
| edrawgetrect(insetrect(dr, -Borderwidth), 0); |
| up = 0; |
| if(dt == 0) |
| continue; |
| dr.max = screenpt(dt, realpt(dt, mouse.xy)); |
| dr.min = subpt(dr.max, mulpt(p1, dt->mag)); |
| if(!rectXrect(dr, dt->r)) |
| continue; |
| edrawgetrect(insetrect(dr, -Borderwidth), 1); |
| up = 1; |
| } |
| /* if up==1, we had a hit */ |
| esetcursor(0); |
| if(up) |
| edrawgetrect(insetrect(dr, -Borderwidth), 0); |
| but = mouse.buttons; |
| buttons(Up); |
| if(!up || but!=4) |
| goto Return; |
| dt = 0; |
| for(nt=thing; nt; nt=nt->next) |
| if(rectXrect(dr, nt->r)){ |
| if(dt){ |
| mesg("ambiguous sweep"); |
| return; |
| } |
| dt = nt; |
| } |
| if(dt == 0) |
| goto Return; |
| p1 = realpt(dt, dr.min); |
| p2 = realpt(dt, Pt(dr.min.x, dr.max.y)); |
| if(p1.x != p2.x) |
| goto onafold; |
| p2 = realpt(dt, dr.max); |
| dr.min = p1; |
| dr.max = p2; |
| |
| if(invert){ |
| tmp = allocimage(display, dr, dt->b->chan, 0, 255); |
| if(tmp == 0){ |
| nomem: |
| mesg("can't allocate temporary"); |
| goto Return; |
| } |
| draw(tmp, dr, st->b, nil, sr.min); |
| if(!complement(tmp)) |
| goto nomem; |
| draw(dt->b, dr, tmp, nil, dr.min); |
| freeimage(tmp); |
| }else |
| draw(dt->b, dr, st->b, nil, sr.min); |
| if(dt->parent){ |
| draw(dt->parent->b, dr, dt->b, nil, dr.min); |
| dt = dt->parent; |
| } |
| drawthing(dt, 0); |
| for(nt=thing; nt; nt=nt->next) |
| if(nt->parent==dt && rectXrect(dr, nt->b->r)){ |
| draw(nt->b, dr, dt->b, nil, dr.min); |
| drawthing(nt, 0); |
| } |
| ckinfo(dt, dr); |
| dt->mod = 1; |
| |
| Return: |
| /* clear blue box */ |
| drawthing(st, 0); |
| } |
| |
| void |
| menu(void) |
| { |
| Thing *t; |
| char *mod; |
| int sel; |
| char buf[256]; |
| |
| sel = emenuhit(3, &mouse, &menu3); |
| switch(sel){ |
| case Mopen: |
| if(type(buf, "file")){ |
| t = tget(buf); |
| if(t) |
| drawthing(t, 1); |
| } |
| break; |
| case Mwrite: |
| apply(twrite); |
| break; |
| case Mread: |
| apply(tread); |
| break; |
| case Mchar: |
| apply(tchar); |
| break; |
| case Mcopy: |
| copy(); |
| break; |
| case Mpixels: |
| tpixels(); |
| break; |
| case Mclose: |
| apply(tclose); |
| break; |
| case Mexit: |
| mod = 0; |
| for(t=thing; t; t=t->next) |
| if(t->mod){ |
| mod = t->name; |
| t->mod = 0; |
| } |
| if(mod){ |
| mesg("%s modified", mod); |
| break; |
| } |
| esetcursor(&skull); |
| buttons(Down); |
| if(mouse.buttons == 4){ |
| buttons(Up); |
| exits(0); |
| } |
| buttons(Up); |
| esetcursor(0); |
| break; |
| } |
| } |