diff --git a/sys/src/cmd/gopher/README.md b/sys/src/cmd/gopher/README.md new file mode 100644 index 000000000..563b927ce --- /dev/null +++ b/sys/src/cmd/gopher/README.md @@ -0,0 +1,25 @@ +gopher +======= +A mostly functional gopher browser for plan9. + +![gopher](gopher.png) + +Most gopher item types are handled: +- Text and submenu items are displayed within the browser +- Images and documents are opened through page(1) +- HTML items are sent to the plumber(4) +- Binary items (bin, dos, uuencoded files and sound) are downloaded to disk +- Other types are not handled (e.g. telnet) + +Following keyboard shortcuts are available: +- b: previous page in history +- n: next page in history +- q: quit + +This has not been thoroughly tested so many bugs are just waiting to be found. + +Usage: +------ +Install with ``mk install`` +Run with ``gopher [address]`` + diff --git a/sys/src/cmd/gopher/dat.h b/sys/src/cmd/gopher/dat.h new file mode 100644 index 000000000..7a2d6f2fc --- /dev/null +++ b/sys/src/cmd/gopher/dat.h @@ -0,0 +1,46 @@ +typedef struct Gmenu Gmenu; +typedef struct Link Link; +typedef struct Hist Hist; + +struct Gmenu +{ + Link *link; + Rtext *text; +}; + +struct Link +{ + char *addr; + char *sel; + int type; +}; + +struct Hist +{ + Hist *p; + Hist *n; + Gmenu *m; +}; + +enum +{ + Ttext, + Tmenu, + Tns, + Terror, + Tbinhex, + Tdos, + Tuuencoded, + Tsearch, + Ttelnet, + Tbinary, + Tmirror, + Tgif, + Timage, + Tt3270, + Tdoc, + Thtml, + Tinfo, + Tsound, + Teof +}; diff --git a/sys/src/cmd/gopher/gopher.c b/sys/src/cmd/gopher/gopher.c new file mode 100644 index 000000000..55e6b30aa --- /dev/null +++ b/sys/src/cmd/gopher/gopher.c @@ -0,0 +1,941 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dat.h" +#include "icons.h" + +void texthit(Panel *p, int b, Rtext *t); +void message(char *s, ...); + +Image *backi; +Image *fwdi; +Image *reloadi; +Panel *root; +Panel *backp; +Panel *fwdp; +Panel *reloadp; +Panel *entryp; +Panel *urlp; +Panel *textp; +Panel *statusp; +Panel *popup; +char *url; +Mouse *mouse; +Hist *hist = nil; +char *bdir; + +enum +{ + Msearch, + Maddbookmark, + Mbookmarks, + Mexit, +}; + +char *menu3[] = { + "search", + "add bookmark", + "bookmarks", + "exit", + 0 +}; + +Link* +mklink(char *addr, char *sel, int type) +{ + Link *l; + + l = malloc(sizeof *l); + if(l==nil) + sysfatal("malloc: %r"); + l->addr = strdup(addr); + l->sel = sel!=nil ? strdup(sel) : nil; + l->type = type; + return l; +} + +Link* +clonelink(Link *l) +{ + if(l==nil) + return nil; + return mklink(l->addr, l->sel, l->type); +} + +int +seltype(char c) +{ + int t; + + t = -c; + switch(c){ + case '0': t = Ttext; break; + case '1': t = Tmenu; break; + case '2': t = Tns; break; + case '3': t = Terror; break; + case '4': t = Tbinhex; break; + case '5': t = Tdos; break; + case '6': t = Tuuencoded; break; + case '7': t = Tsearch; break; + case '8': t = Ttelnet; break; + case '9': t = Tbinary; break; + case '+': t = Tmirror; break; + case 'g': t = Tgif; break; + case 'I': t = Timage; break; + case 'T': t = Tt3270; break; + case 'd': t = Tdoc; break; + case 'h': t = Thtml; break; + case 'i': t = Tinfo; break; + case 's': t = Tsound; break; + case '.': t = Teof; break; + default: break; + } + return t; +} + +static char Typechar[] = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', '+', 'g', + 'I', 'T', 'd', 'h', 'i', 's', + '.', +}; + +static char *Typestr[] = { + "FILE", "DIR", "NS", "ERR", "HEX", + "DOS", "UU", "?", "TELNET", "BIN", + "MIRROR", "GIF", "IMG", "T3270", "DOC", + "HTML", "", "SND", "EOF", +}; + +char* +seltypestr(int type) +{ + if(type<0) + return smprint("UNKN:%c", (char) -type); + return smprint("%6s", Typestr[type]); +}; + + +Gmenu* +rendermenu(Link *l, Biobuf *bp) +{ + char *s, *f[5], *t; + Gmenu *m; + Link *n; + int type; + + m = malloc(sizeof *m); + if(m==nil) + sysfatal("malloc: %r"); + m->link = clonelink(l); + m->text = nil; + plrtstr(&m->text, 1000000, 0, 0, font, strdup(" "), 0, 0); + for(;;){ + n = nil; + s = Brdstr(bp, '\n', 0); + if(s==nil || s[0]=='.') + break; + type = seltype(s[0]); + getfields(s+1, f, 5, 0, "\t\r\n"); + switch(type){ + case Tinfo: + break; + case Thtml: + n = mklink(strdup(f[1]+4), nil, Thtml); /* +4 skip URL: */ + break; + default: + n = mklink(netmkaddr(f[2], "tcp", f[3]), strdup(f[1]), type); + break; + } + t = strdup(f[0]); + plrtstr(&m->text, 1000000, 8, 0, font, seltypestr(type), PL_HEAD, 0); + if(type == Tinfo || type < 0) + plrtstr(&m->text, 8, 0, 0, font, t, 0, 0); + else + plrtstr(&m->text, 8, 0, 0, font, t, PL_HOT, n); + + } + return m; +} + +Gmenu* +rendertext(Link *l, Biobuf *bp) +{ + Gmenu *m; + String *buf; + int c, n; + + m = malloc(sizeof *m); + if(m==nil) + sysfatal("malloc: %r"); + m->link = clonelink(l); + m->text = nil; + plrtstr(&m->text, 1000000, 0, 0, font, strdup(" "), 0, 0); + n = 0; + buf = s_new(); + for(;;){ + c = Bgetc(bp); + if(c<0) + break; + else if(c=='\r' || c=='\n'){ + if(c=='\r' && Bgetc(bp)!='\n') + Bungetc(bp); + if(n==1 && s_to_c(buf)[0]=='.') + break; + s_terminate(buf); + plrtstr(&m->text, 1000000, 8, 0, font, strdup(s_to_c(buf)), 0, 0); + s_reset(buf); + n = 0; + }else if(c=='\t'){ + n += 4; + s_append(buf, " "); + }else{ + n++; + s_putc(buf, c); + } + } + s_free(buf); + return m; +} + +Gmenu* +render(Link *l) +{ + int fd; + Biobuf *bp; + Gmenu *m; + + fd = dial(l->addr, 0, 0, 0); + if(fd < 0){ + message("unable to connect to %s: %r", l->addr); + return nil; + } + fprint(fd, "%s\r\n", l->sel); + bp = Bfdopen(fd, OREAD); + if(bp==nil){ + close(fd); + sysfatal("bfdopen: %r"); + } + switch(l->type){ + case Tmenu: + m = rendermenu(l, bp); + break; + case Ttext: + m = rendertext(l, bp); + break; + default: + /* TODO error */ + m = nil; + break; + } + Bterm(bp); + close(fd); + return m; +} + +void +message(char *s, ...) +{ + static char buf[1024]; + char *out; + va_list args; + + va_start(args, s); + out = buf + vsnprint(buf, sizeof(buf), s, args); + va_end(args); + *out='\0'; + plinitlabel(statusp, PACKN|FILLX, buf); + pldraw(statusp, screen); + flushimage(display, 1); +} + +Link* +urltolink(char *url) +{ + char *a, *sel, *hostport, *p; + int type; + Link *l; + + a = strdup(url); + hostport = a; + if(strncmp(a, "gopher://", 9) == 0) + hostport += 9; + p = strchr(hostport, '/'); + if(p){ + *p++ = 0; + type = *p ? seltype(*p++) : Tmenu; + if(type < 0) + return nil; + sel = *p ? p : ""; + }else{ + type = Tmenu; + sel = ""; + } + p = strchr(hostport, ':'); + if(p){ + *p++ = 0; + l = mklink(netmkaddr(hostport, "tcp", p), sel, type); + }else{ + l = mklink(netmkaddr(hostport, "tcp", "70"), sel, type); + } + free(a); + return l; +} + +char* +linktourl(Link *l) +{ + char *f[3], *a, *s; + int n; + + a = strdup(l->addr); + n = getfields(a, f, 3, 0, "!"); + if(n != 3) + s = smprint("Url: gopher://%s/%d%s", l->addr, l->type, l->sel); + else if(atoi(f[2])!=70) + s = smprint("Url: gopher://%s:%s/%d%s", f[1], f[2], l->type, l->sel); + else + s = smprint("Url: gopher://%s/%d%s", f[1], l->type, l->sel); + free(a); + return s; +} + +void +seturl(Link *l) +{ + free(url); + url = linktourl(l); +} + +void +show(Gmenu *m) +{ + plinittextview(textp, PACKE|EXPAND, ZP, m->text, texthit); + pldraw(textp, screen); + plinitlabel(urlp, PACKN|FILLX, url); + pldraw(urlp, screen); + message("gopher!"); +} + +void +freetext(Rtext *t){ + Rtext *tt; + Link *l; + + tt = t; + for(; t!=0; t = t->next){ + t->b=0; + free(t->text); + t->text = 0; + if(l = t->user){ + t->user = 0; + free(l->addr); + if(l->sel!=nil && l->sel[0]!=0) + free(l->sel); + free(l); + } + } + plrtfree(tt); +} + +void +freehist(Hist *h) +{ + Hist *n; + Gmenu *m; + + for(n = h->n; h; h = n){ + m = h->m; + freetext(m->text); + if(m->link!=nil){ + free(m->link->addr); + free(m->link->sel); + free(m->link); + } + free(h); + } +} + +void +visit(Link *l, int sethist) +{ + Gmenu *m; + Hist *h; + + seturl(l); + message("loading %s...", url); + m = render(l); + if(m==nil) + return; + show(m); + if(!sethist) + return; + h = malloc(sizeof *h); + if(h == nil) + sysfatal("malloc: %r"); +/* FIXME + if(hist != nil && hist->n != nil) + freehist(hist->n); +*/ + h->p = hist; + h->n = nil; + h->m = m; + hist = h; +} + +void +plumburl(char *u) +{ + int fd; + + fd = plumbopen("send", OWRITE|OCEXEC); + if(fd<0) + return; + plumbsendtext(fd, "gopher", nil, nil, u); + close(fd); +} + +void +dupfds(int fd, ...) +{ + int mfd, n, i; + va_list arg; + Dir *dir; + + va_start(arg, fd); + for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++) + if(fd != mfd) + if(dup(fd, mfd) < 0) + sysfatal("dup: %r"); + va_end(arg); + if((fd = open("/fd", OREAD)) < 0) + sysfatal("open: %r"); + n = dirreadall(fd, &dir); + for(i=0; i= mfd) + close(fd); + } + free(dir); +} + +void +page(Link *l) +{ + int fd; + + fd = dial(l->addr, 0, 0, 0); + if(fd < 0) + sysfatal("dial: %r"); + fprint(fd, "%s\r\n", l->sel); + switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){ + case -1: + fprint(2, "Can't fork!"); + break; + case 0: + dupfds(fd, 1, 2, -1); + execl("/bin/rc", "rc", "-c", "page -w", nil); + _exits(0); + } + close(fd); +} + +void +save(Link *l, char *name){ + char buf[1024]; + int ifd, ofd; + + ifd = dial(l->addr, 0, 0, 0); + if(ifd < 0){ + message("save: %s: %r", name); + return; + } + fprint(ifd, "%s\r\n", l->sel); + ofd=create(name, OWRITE, 0666); + if(ofd < 0){ + message("save: %s: %r", name); + return; + } + switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){ + case -1: + message("Can't fork: %r"); + break; + case 0: + dup(ifd, 0); + close(ifd); + dup(ofd, 1); + close(ofd); + + snprint(buf, sizeof(buf), + "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name); + execl("/bin/rc", "rc", "-c", buf, nil); + exits("exec"); + } + close(ifd); + close(ofd); +} + +void +search(void) +{ + static char last[256]; + char buf[256]; + Reprog *re; + Rtext *tp; + int yoff; + + for(;;){ + if(hist == nil || hist->m == nil || hist->m->text == nil) + return; + strncpy(buf, last, sizeof(buf)-1); + if(eenter("Search for", buf, sizeof(buf), mouse) <= 0) + return; + strncpy(last, buf, sizeof(buf)-1); + re = regcompnl(buf); + if(re == nil){ + message("%r"); + continue; + } + for(tp=hist->m->text;tp;tp=tp->next) + if(tp->flags & PL_SEL) + break; + if(tp == nil) + tp = hist->m->text; + else { + tp->flags &= ~PL_SEL; + tp = tp->next; + } + while(tp != nil){ + tp->flags &= ~PL_SEL; + if(tp->text && *tp->text) + if(regexec(re, tp->text, nil, 0)){ + tp->flags |= PL_SEL; + plsetpostextview(textp, tp->topy); + break; + } + tp = tp->next; + } + free(re); + yoff = plgetpostextview(textp); + plinittextview(textp, PACKE|EXPAND, ZP, hist->m->text, texthit); + plsetpostextview(textp, yoff); + pldraw(textp, screen); + } + +} + +void +addbookmark(void) +{ + Link *l; + char buf[255] = {0}, *f, *u[3]; + int n, fd; + + if(hist==nil) + return; + l = hist->m->link; + n = eenter("Name:", buf, sizeof buf, mouse); + if(n<=0) + return; + f = smprint("%s/bookmarks", bdir); + fd = open(f, OWRITE); + if(fd<0){ + fd = create(f, OWRITE, 0644); + if(fd<0){ + message("cannot open %s", f); + free(f); + return; + } + fprint(fd, "iGOPHER Bookmarks\n"); + fprint(fd, "i=================\n"); + fprint(fd, "i \n"); + } + free(f); + f = strdup(l->addr); + getfields(f, u, 3, 0, "!"); + seek(fd, 0, 2); + fprint(fd, "%c%s\t%s\t%s\t%s\n", Typechar[l->type], buf, l->sel, u[1], u[2]); + fprint(fd, "i%s\n", linktourl(l)); + free(f); + close(fd); + message("added bookmark %s", buf); +} + +void +showbookmarks(void) +{ + char *f; + Biobuf *bp; + Gmenu *m; + + f = smprint("%s/bookmarks", bdir); + bp = Bopen(f, OREAD); + if(bp==nil){ + message("cannot open %s", f); + free(f); + return; + } + m = rendermenu(nil, bp); + show(m); + free(f); + Bterm(bp); +} + +char* +linktofile(Link *l){ + char *n, *s; + + if(l==nil) + return nil; + n = l->sel; + if(n==nil || n[0]==0) + n = "/"; + if(s = strrchr(n, '/')) + n = s+1; + if(n[0]==0) + n = "file"; + return n; +} + +void +texthit(Panel *p, int b, Rtext *t) +{ + Link *l; + char *s, buf[1024] = {0}; + + USED(p); + if(b!=1) + return; + if(t->user==nil) + return; + l = t->user; + switch(l->type){ + case Tmenu: + case Ttext: + visit(l, 1); + break; + case Thtml: + plumburl(l->addr); + break; + case Tdoc: + case Tgif: + case Timage: + page(l); + break; + case Tsearch: + if(eenter("Search:", buf, sizeof buf, mouse)>0){ + s = smprint("%s\t%s", l->sel, buf); + visit(mklink(l->addr, s, Tmenu), 1); + free(s); + } + break; + case Tdos: + case Tbinary: + case Tbinhex: + case Tuuencoded: + snprint(buf, sizeof buf, "%s", linktofile(l)); + if(eenter("Save as:", buf, sizeof buf, mouse)>0){ + save(l, buf); + } + break; + default: + message("unhandled item type '%s'", Typestr[l->type]); + break; + } +} + +void +backhit(Panel *p, int b) +{ + USED(p); + if(b!=1) + return; + if(hist==nil || hist->p==nil) + return; + hist->p->n = hist; + hist = hist->p; + seturl(hist->m->link); + show(hist->m); +} + +void +nexthit(Panel *p, int b) +{ + USED(p); + if(b!=1) + return; + if(hist==nil || hist->n==nil) + return; + hist = hist->n; + seturl(hist->m->link); + show(hist->m); +} + +void +reloadhit(Panel *p, int b) +{ + USED(p); + if(b!=1) + return; + visit(hist->m->link, 0); +} + +void +menuhit(int button, int item) +{ + USED(button); + + switch(item){ + case Msearch: + search(); + break; + case Maddbookmark: + addbookmark(); + break; + case Mbookmarks: + showbookmarks(); + break; + case Mexit: + exits(nil); + break; + } +} + +void +entryhit(Panel *p, char *t) +{ + Link *l; + + USED(p); + switch(strlen(t)){ + case 0: + return; + case 1: + switch(*t){ + case 'b': + backhit(backp, 1); + break; + case 'n': + nexthit(fwdp, 1); + break; + case 'q': + exits(nil); + break; + default: + message("unknown command %s", t); + break; + } + break; + default: + l = urltolink(t); + if(l==nil) + message("invalid url %s", t); + else + visit(l, 1); + } + plinitentry(entryp, PACKN|FILLX, 0, "", entryhit); + pldraw(root, screen); +} + +void +mkpanels(void) +{ + Panel *p, *ybar, *xbar, *m; + + m = plmenu(0, 0, menu3, PACKN|FILLX, menuhit); + root = plpopup(0, EXPAND, 0, 0, m); + p = plgroup(root, PACKN|FILLX); + statusp = pllabel(p, PACKN|FILLX, "gopher!"); + plplacelabel(statusp, PLACEW); + plbutton(p, PACKW|BITMAP|NOBORDER, backi, backhit); + plbutton(p, PACKW|BITMAP|NOBORDER, fwdi, nexthit); + plbutton(p, PACKW|BITMAP|NOBORDER, reloadi, reloadhit); + pllabel(p, PACKW, "Go:"); + entryp = plentry(p, PACKN|FILLX, 0, "", entryhit); + p = plgroup(root, PACKN|FILLX); + urlp = pllabel(p, PACKN|FILLX, ""); + plplacelabel(urlp, PLACEW); + p = plgroup(root, PACKN|EXPAND); + ybar = plscrollbar(p, PACKW|USERFL); + xbar = plscrollbar(p, IGNORE); + textp = pltextview(p, PACKE|EXPAND, ZP, nil, nil); + plscroll(textp, xbar, ybar); + plgrabkb(entryp); +} + +void +eresized(int new) +{ + if(new && getwindow(display, Refnone)<0) + sysfatal("cannot reattach: %r"); + plpack(root, screen->r); + pldraw(root, screen); +} + +Image* +loadicon(Rectangle r, uchar *data, int ndata) +{ + Image *i; + int n; + + i = allocimage(display, r, RGBA32, 0, DNofill); + if(i==nil) + sysfatal("allocimage: %r"); + n = loadimage(i, r, data, ndata); + if(n<0) + sysfatal("loadimage: %r"); + return i; +} + +void +loadicons(void) +{ + Rectangle r = Rect(0,0,16,16); + + backi = loadicon(r, ibackdata, sizeof ibackdata); + fwdi = loadicon(r, ifwddata, sizeof ifwddata); + reloadi = loadicon(r, ireloaddata, sizeof ireloaddata); +} + +void scrolltext(int dy, int whence) +{ + Scroll s; + + s = plgetscroll(textp); + switch(whence){ + case 0: + s.pos.y = dy; + break; + case 1: + s.pos.y += dy; + break; + case 2: + s.pos.y = s.size.y+dy; + break; + } + if(s.pos.y > s.size.y) + s.pos.y = s.size.y; + if(s.pos.y < 0) + s.pos.y = 0; + plsetscroll(textp, s); + /* BUG: there is a redraw issue when scrolling + This fixes the issue albeit not properly */ + pldraw(textp, screen); +} + +void +ensurebdir(void) +{ + char *home, *tmp; + int fd; + + home = getenv("home"); + if(home){ + tmp = smprint("%s/lib", home); + fd = create(tmp, OREAD, DMDIR|0777); + if(fd>0) + close(fd); + free(tmp); + bdir = smprint("%s/lib/gopher", home); + fd = create(bdir, OREAD, DMDIR|0777); + if(fd>0) + close(fd); + }else + bdir = strdup("/tmp"); +} + +void +main(int argc, char *argv[]) +{ + enum { Eplumb = 128 }; + Event e; + Link *l; + char *url; + Plumbmsg *pm; + + if(argc == 2) + url = argv[1]; + else + url = "gopher.floodgap.com"; + quotefmtinstall(); + ensurebdir(); + if(initdraw(nil, nil, "gopher")<0) + sysfatal("initdraw: %r"); + einit(Emouse|Ekeyboard); + plinit(screen->depth); + loadicons(); + mkpanels(); + l = urltolink(url); + if(l==nil) + message("invalid url %s", url); + else + visit(l, 1); + eresized(0); + eplumb(Eplumb, "gopher"); + for(;;){ + switch(event(&e)){ + case Eplumb: + pm = e.v; + if(pm->ndata > 0){ + l = urltolink(pm->data); + if(l!=nil) + visit(l, 1); + } + plumbfree(pm); + break; + case Ekeyboard: + switch(e.kbdc){ + default: + plgrabkb(entryp); + plkeyboard(e.kbdc); + break; + case Khome: + scrolltext(0, 0); + break; + case Kup: + scrolltext(-textp->size.y/4, 1); + break; + case Kpgup: + scrolltext(-textp->size.y/2, 1); + break; + case Kdown: + scrolltext(textp->size.y/4, 1); + break; + case Kpgdown: + scrolltext(textp->size.y/2, 1); + break; + case Kend: + scrolltext(-textp->size.y, 2); + break; + case Kdel: + exits(nil); + break; + } + break; + case Emouse: + mouse = &e.mouse; + if(mouse->buttons & (8|16) && ptinrect(mouse->xy, textp->r)){ + if(mouse->buttons & 8) + scrolltext(textp->r.min.y - mouse->xy.y, 1); + else + scrolltext(mouse->xy.y - textp->r.min.y, 1); + break; + } + plmouse(root, mouse); + /* BUG: there is a redraw issue when scrolling + This fixes the issue albeit not properly */ + //pldraw(textp, screen); + break; + } + } +} + diff --git a/sys/src/cmd/gopher/gopher.png b/sys/src/cmd/gopher/gopher.png new file mode 100644 index 000000000..e3b272fbf Binary files /dev/null and b/sys/src/cmd/gopher/gopher.png differ diff --git a/sys/src/cmd/gopher/icons.h b/sys/src/cmd/gopher/icons.h new file mode 100644 index 000000000..9acb00c67 --- /dev/null +++ b/sys/src/cmd/gopher/icons.h @@ -0,0 +1,200 @@ +uchar ibackdata[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, + 0xf5, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, + 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x58, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2c, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uchar ifwddata[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0xfb, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf1, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, + 0xfb, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, + 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, + 0xfb, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +uchar ireloaddata[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, + 0xd9, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0xed, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, + 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0xaf, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/sys/src/cmd/gopher/libpanel/button.c b/sys/src/cmd/gopher/libpanel/button.c new file mode 100644 index 000000000..8f73ca2be --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/button.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Button Button; +struct Button{ + int btype; /* button type */ + Icon *icon; /* what to write on the button */ + int check; /* for check/radio buttons */ + void (*hit)(Panel *, int, int); /* call back user code on check/radio hit */ + void (*menuhit)(int, int); /* call back user code on menu item hit */ + void (*pl_buttonhit)(Panel *, int); /* call back user code on button hit */ + int index; /* arg to menuhit */ + int buttons; +}; +/* + * Button types + */ +#define BUTTON 1 +#define CHECK 2 +#define RADIO 3 +void pl_drawbutton(Panel *p){ + Rectangle r; + Button *bp; + bp=p->data; + r=pl_boxf(p->b, p->r, p->flags, p->state); + switch(bp->btype){ + case CHECK: + r=pl_check(p->b, r, bp->check); + break; + case RADIO: + r=pl_radio(p->b, r, bp->check); + break; + } + pl_drawicon(p->b, r, PLACECEN, p->flags, bp->icon); +} +int pl_hitbutton(Panel *p, Mouse *m){ + int oldstate, hitme; + Panel *sib; + Button *bp; + bp=p->data; + oldstate=p->state; + if(m->buttons&OUT){ + hitme=0; + p->state=UP; + } + else if(m->buttons&7){ + hitme=0; + p->state=DOWN; + bp->buttons=m->buttons; + } + else{ /* mouse inside, but no buttons down */ + hitme=p->state==DOWN; + p->state=UP; + } + if(hitme) switch(bp->btype){ + case CHECK: + if(hitme) bp->check=!bp->check; + break; + case RADIO: + if(bp->check) bp->check=0; + else{ + if(p->parent){ + for(sib=p->parent->child;sib;sib=sib->next){ + if(sib->hit==pl_hitbutton + && ((Button *)sib->data)->btype==RADIO + && ((Button *)sib->data)->check){ + ((Button *)sib->data)->check=0; + pldraw(sib, p->b); + } + } + } + bp->check=1; + } + break; + } + if(hitme || oldstate!=p->state) pldraw(p, p->b); + if(hitme && bp->hit){ + bp->hit(p, bp->buttons, bp->check); + p->state=UP; + } + return 0; +} +void pl_typebutton(Panel *g, Rune c){ + USED(g, c); +} +Point pl_getsizebutton(Panel *p, Point children){ + Point s; + int ckw; + Button *bp; + USED(children); /* shouldn't have any children */ + bp=p->data; + s=pl_iconsize(p->flags, bp->icon); + if(bp->btype!=BUTTON){ + ckw=pl_ckwid(); + if(s.ystate); +} +void pl_childspacebutton(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +void pl_initbtype(Panel *v, int flags, Icon *icon, void (*hit)(Panel *, int, int), int btype){ + Button *bp; + bp=v->data; + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawbutton; + v->hit=pl_hitbutton; + v->type=pl_typebutton; + v->getsize=pl_getsizebutton; + v->childspace=pl_childspacebutton; + bp->btype=btype; + bp->check=0; + bp->hit=hit; + bp->icon=icon; + switch(btype){ + case BUTTON: v->kind="button"; break; + case CHECK: v->kind="checkbutton"; break; + case RADIO: v->kind="radiobutton"; break; + } +} +void pl_buttonhit(Panel *p, int buttons, int check){ + USED(check); + if(((Button *)p->data)->pl_buttonhit) ((Button *)p->data)->pl_buttonhit(p, buttons); +} +void plinitbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int)){ + ((Button *)p->data)->pl_buttonhit=hit; + pl_initbtype(p, flags, icon, pl_buttonhit, BUTTON); +} +void plinitcheckbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ + pl_initbtype(p, flags, icon, hit, CHECK); +} +void plinitradiobutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ + pl_initbtype(p, flags, icon, hit, RADIO); +} +Panel *plbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int)){ + Panel *p; + p=pl_newpanel(parent, sizeof(Button)); + plinitbutton(p, flags, icon, hit); + return p; +} +Panel *plcheckbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ + Panel *p; + p=pl_newpanel(parent, sizeof(Button)); + plinitcheckbutton(p, flags, icon, hit); + return p; +} +Panel *plradiobutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ + Panel *p; + p=pl_newpanel(parent, sizeof(Button)); + plinitradiobutton(p, flags, icon, hit); + return p; +} +void pl_hitmenu(Panel *p, int buttons){ + void (*hit)(int, int); + hit=((Button *)p->data)->menuhit; + if(hit) hit(buttons, ((Button *)p->data)->index); +} +void plinitmenu(Panel *v, int flags, Icon **item, int cflags, void (*hit)(int, int)){ + Panel *b; + int i; + v->flags=flags; + v->kind="menu"; + if(v->child){ + plfree(v->child); + v->child=0; + } + for(i=0;item[i];i++){ + b=plbutton(v, cflags, item[i], pl_hitmenu); + ((Button *)b->data)->menuhit=hit; + ((Button *)b->data)->index=i; + } +} +Panel *plmenu(Panel *parent, int flags, Icon **item, int cflags, void (*hit)(int, int)){ + Panel *v; + v=plgroup(parent, flags); + plinitmenu(v, flags, item, cflags, hit); + return v; +} +void plsetbutton(Panel *p, int val){ + ((Button *)p->data)->check=val; +} diff --git a/sys/src/cmd/gopher/libpanel/canvas.c b/sys/src/cmd/gopher/libpanel/canvas.c new file mode 100644 index 000000000..c0ebbc793 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/canvas.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Canvas Canvas; +struct Canvas{ + void (*draw)(Panel *); + void (*hit)(Panel *, Mouse *); +}; +void pl_drawcanvas(Panel *p){ + Canvas *c; + c=p->data; + if(c->draw) c->draw(p); +} +int pl_hitcanvas(Panel *p, Mouse *m){ + Canvas *c; + c=p->data; + if(c->hit) c->hit(p, m); + return 0; +} +void pl_typecanvas(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizecanvas(Panel *p, Point children){ + USED(p, children); + return Pt(0,0); +} +void pl_childspacecanvas(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +void plinitcanvas(Panel *v, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){ + Canvas *c; + v->flags=flags|LEAF; + v->draw=pl_drawcanvas; + v->hit=pl_hitcanvas; + v->type=pl_typecanvas; + v->getsize=pl_getsizecanvas; + v->childspace=pl_childspacecanvas; + v->kind="canvas"; + c=v->data; + c->draw=draw; + c->hit=hit; +} +Panel *plcanvas(Panel *parent, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){ + Panel *p; + p=pl_newpanel(parent, sizeof(Canvas)); + plinitcanvas(p, flags, draw, hit); + return p; +} diff --git a/sys/src/cmd/gopher/libpanel/draw.c b/sys/src/cmd/gopher/libpanel/draw.c new file mode 100644 index 000000000..edb9d3443 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/draw.c @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +#define PWID 1 /* width of label border */ +#define BWID 1 /* width of button relief */ +#define FWID 2 /* width of frame relief */ +#define SPACE 1 /* space inside relief of button or frame */ +#define CKSIZE 3 /* size of check mark */ +#define CKSPACE 2 /* space around check mark */ +#define CKWID 1 /* width of frame around check mark */ +#define CKINSET 1 /* space around check mark frame */ +#define CKBORDER 2 /* space around X inside frame */ +static int plldepth; +static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit; +int pl_drawinit(int ldepth){ + plldepth=ldepth; + pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF); + //pl_light=allocimagemix(display, DPalebluegreen, DWhite); + pl_light=display->white; + //pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); + pl_dark = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0X999999FF); + pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF); + pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80); + if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0) return 0; + return 1; +} +void pl_relief(Image *b, Image *ul, Image *lr, Rectangle r, int wid){ + int x, y; + draw(b, Rect(r.min.x, r.max.y-wid, r.max.x, r.max.y), lr, 0, ZP); /* bottom */ + draw(b, Rect(r.max.x-wid, r.min.y, r.max.x, r.max.y), lr, 0, ZP); /* right */ + draw(b, Rect(r.min.x, r.min.y, r.min.x+wid, r.max.y), ul, 0, ZP); /* left */ + draw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+wid), ul, 0, ZP); /* top */ + for(x=0;x!=wid;x++) for(y=wid-1-x;y!=wid;y++){ + draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.max.x-wid, y+r.min.y)), lr, 0, ZP); + draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.min.x, y+r.max.y-wid)), lr, 0, ZP); + } +} +Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){ + if(plldepth==0) switch(style){ + case UP: + pl_relief(b, pl_black, pl_black, r, BWID); + r=insetrect(r, BWID); + if(fill) draw(b, r, pl_white, 0, ZP); + else border(b, r, SPACE, pl_white, ZP); + break; + case DOWN: + case DOWN1: + case DOWN2: + case DOWN3: + pl_relief(b, pl_black, pl_black, r, BWID); + r=insetrect(r, BWID); + if(fill) draw(b, r, pl_black, 0, ZP); + border(b, r, SPACE, pl_black, ZP); + break; + case PASSIVE: + if(fill) draw(b, r, pl_white, 0, ZP); + r=insetrect(r, PWID); + if(!fill) border(b, r, SPACE, pl_white, ZP); + break; + case FRAME: + pl_relief(b, pl_white, pl_black, r, FWID); + r=insetrect(r, FWID); + pl_relief(b, pl_black, pl_white, r, FWID); + r=insetrect(r, FWID); + if(fill) draw(b, r, pl_white, 0, ZP); + else border(b, r, SPACE, pl_white, ZP); + break; + } + else switch(style){ + case UP: + /* + pl_relief(b, pl_white, pl_black, r, BWID); + r=insetrect(r, BWID); + if(fill) draw(b, r, pl_light, 0, ZP); + else border(b, r, SPACE, pl_white, ZP); + */ + draw(b, r, pl_light, 0, ZP); + border(b, r, 1, pl_black, ZP); + break; + case DOWN: + case DOWN1: + case DOWN2: + case DOWN3: + pl_relief(b, pl_black, pl_white, r, BWID); + r=insetrect(r, BWID); + if(fill) draw(b, r, pl_dark, 0, ZP); + else border(b, r, SPACE, pl_black, ZP); + break; + case PASSIVE: + if(fill) draw(b, r, pl_light, 0, ZP); + r=insetrect(r, PWID); + if(!fill) border(b, r, SPACE, pl_white, ZP); + break; + case FRAME: + border(b, r, 1, pl_black, ZP); + /* + pl_relief(b, pl_white, pl_black, r, FWID); + r=insetrect(r, FWID); + pl_relief(b, pl_black, pl_white, r, FWID); + r=insetrect(r, FWID); + if(fill) draw(b, r, pl_light, 0, ZP); + else border(b, r, SPACE, pl_white, ZP); + */ + break; + } + return insetrect(r, SPACE); +} +Rectangle pl_outline(Image *b, Rectangle r, int style){ + return pl_boxoutline(b, r, style, 0); +} +Rectangle pl_box(Image *b, Rectangle r, int style){ + return pl_boxoutline(b, r, style, 1); +} +Rectangle pl_boxoutlinef(Image *b, Rectangle r, int flags, int style, int fill){ + switch(style){ + case UP: + draw(b, r, pl_light, 0, ZP); + if(!(flags&NOBORDER)) + border(b, r, 1, pl_black, ZP); + break; + case DOWN: + case DOWN1: + case DOWN2: + case DOWN3: + if(!(flags&NOBORDER)) + pl_relief(b, pl_black, pl_white, r, BWID); + r=insetrect(r, BWID); + if(fill) draw(b, r, pl_dark, 0, ZP); + else if(!(flags&NOBORDER)) border(b, r, SPACE, pl_black, ZP); + break; + case PASSIVE: + if(fill) draw(b, r, pl_light, 0, ZP); + r=insetrect(r, PWID); + if(!fill) border(b, r, SPACE, pl_white, ZP); + break; + case FRAME: + border(b, r, 1, pl_black, ZP); + break; + } + return insetrect(r, SPACE); +} +Rectangle pl_outlinef(Image *b, Rectangle r, int flags, int style){ + return pl_boxoutlinef(b, r, flags, style, 0); +} +Rectangle pl_boxf(Image *b, Rectangle r, int flags, int style){ + return pl_boxoutlinef(b, r, flags, style, 1); +} +Point pl_boxsize(Point interior, int state){ + switch(state){ + case UP: + case DOWN: + case DOWN1: + case DOWN2: + case DOWN3: + return addpt(interior, Pt(2*(BWID+SPACE), 2*(BWID+SPACE))); + case PASSIVE: + return addpt(interior, Pt(2*(PWID+SPACE), 2*(PWID+SPACE))); + case FRAME: + return addpt(interior, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE)); + } + return Pt(0, 0); +} +void pl_interior(int state, Point *ul, Point *size){ + switch(state){ + case UP: + case DOWN: + case DOWN1: + case DOWN2: + case DOWN3: + *ul=addpt(*ul, Pt(BWID+SPACE, BWID+SPACE)); + *size=subpt(*size, Pt(2*(BWID+SPACE), 2*(BWID+SPACE))); + break; + case PASSIVE: + *ul=addpt(*ul, Pt(PWID+SPACE, PWID+SPACE)); + *size=subpt(*size, Pt(2*(PWID+SPACE), 2*(PWID+SPACE))); + break; + case FRAME: + *ul=addpt(*ul, Pt(2*FWID+SPACE, 2*FWID+SPACE)); + *size=subpt(*size, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE)); + } +} + +void pl_drawicon(Image *b, Rectangle r, int stick, int flags, Icon *s){ + Rectangle save; + Point ul, offs; + ul=r.min; + offs=subpt(subpt(r.max, r.min), pl_iconsize(flags, s)); + switch(stick){ + case PLACENW: break; + case PLACEN: ul.x+=offs.x/2; break; + case PLACENE: ul.x+=offs.x; break; + case PLACEW: ul.y+=offs.y/2; break; + case PLACECEN: ul.x+=offs.x/2; ul.y+=offs.y/2; break; + case PLACEE: ul.x+=offs.x; break; + case PLACESW: ul.y+=offs.y; break; + case PLACES: ul.x+=offs.x/2; ul.y+=offs.y; break; + case PLACESE: ul.x+=offs.x; ul.y+=offs.y; break; + } + save=b->clipr; + if(!rectclip(&r, save)) + return; + replclipr(b, b->repl, r); + if(flags&BITMAP) draw(b, Rpt(ul, addpt(ul, pl_iconsize(flags, s))), s, 0, ZP); + else string(b, ul, pl_black, ZP, font, s); + replclipr(b, b->repl, save); +} +/* + * Place a check mark at the left end of r. Return the unused space. + * Caller must guarantee that r.max.x-r.min.x>=r.max.y-r.min.y! + */ +Rectangle pl_radio(Image *b, Rectangle r, int val){ + Rectangle remainder; + remainder=r; + r.max.x=r.min.x+r.max.y-r.min.y; + remainder.min.x=r.max.x; + r=insetrect(r, CKINSET); + if(plldepth==0) + pl_relief(b, pl_black, pl_black, r, CKWID); + else + pl_relief(b, pl_black, pl_white, r, CKWID); + r=insetrect(r, CKWID); + if(plldepth==0) + draw(b, r, pl_white, 0, ZP); + else + draw(b, r, pl_light, 0, ZP); + if(val) draw(b, insetrect(r, CKSPACE), pl_black, 0, ZP); + return remainder; +} +Rectangle pl_check(Image *b, Rectangle r, int val){ + Rectangle remainder; + remainder=r; + r.max.x=r.min.x+r.max.y-r.min.y; + remainder.min.x=r.max.x; + r=insetrect(r, CKINSET); + if(plldepth==0) + pl_relief(b, pl_black, pl_black, r, CKWID); + else + pl_relief(b, pl_black, pl_white, r, CKWID); + r=insetrect(r, CKWID); + if(plldepth==0) + draw(b, r, pl_white, 0, ZP); + else + draw(b, r, pl_light, 0, ZP); + r=insetrect(r, CKBORDER); + if(val){ + line(b, Pt(r.min.x, r.min.y+1), Pt(r.max.x-1, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP); + line(b, Pt(r.min.x, r.min.y ), Pt(r.max.x, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP); + line(b, Pt(r.min.x+1, r.min.y ), Pt(r.max.x, r.max.y-1), Endsquare, Endsquare, 0, pl_black, ZP); + line(b, Pt(r.min.x , r.max.y-2), Pt(r.max.x-1, r.min.y-1), Endsquare, Endsquare, 0, pl_black, ZP); + line(b, Pt(r.min.x, r.max.y-1), Pt(r.max.x, r.min.y-1), Endsquare, Endsquare, 0, pl_black, ZP); + line(b, Pt(r.min.x+1, r.max.y-1), Pt(r.max.x, r.min.y ), Endsquare, Endsquare, 0, pl_black, ZP); + } + return remainder; +} +int pl_ckwid(void){ + return 2*(CKINSET+CKSPACE+CKWID)+CKSIZE; +} +void pl_sliderupd(Image *b, Rectangle r1, int dir, int lo, int hi){ + Rectangle r2, r3; + r2=r1; + r3=r1; + if(lo<0) lo=0; + if(hi<=lo) hi=lo+1; + switch(dir){ + case HORIZ: + r1.max.x=r1.min.x+lo; + r2.min.x=r1.max.x; + r2.max.x=r1.min.x+hi; + if(r2.max.x>r3.max.x) r2.max.x=r3.max.x; + r3.min.x=r2.max.x; + break; + case VERT: + r1.max.y=r1.min.y+lo; + r2.min.y=r1.max.y; + r2.max.y=r1.min.y+hi; + if(r2.max.y>r3.max.y) r2.max.y=r3.max.y; + r3.min.y=r2.max.y; + break; + } + draw(b, r1, pl_light, 0, ZP); + draw(b, r2, pl_dark, 0, ZP); + draw(b, r3, pl_light, 0, ZP); +} +void pl_draw1(Panel *p, Image *b); +void pl_drawall(Panel *p, Image *b){ + if(p->flags&INVIS || p->flags&IGNORE) return; + p->b=b; + p->draw(p); + for(p=p->child;p;p=p->next) pl_draw1(p, b); +} +void pl_draw1(Panel *p, Image *b){ + if(b!=0) + pl_drawall(p, b); +} +void pldraw(Panel *p, Image *b){ + pl_draw1(p, b); +} +void pl_invis(Panel *p, int v){ + for(;p;p=p->next){ + if(v) p->flags|=INVIS; else p->flags&=~INVIS; + pl_invis(p->child, v); + } +} +Point pl_iconsize(int flags, Icon *p){ + if(flags&BITMAP) return subpt(((Image *)p)->r.max, ((Image *)p)->r.min); + return stringsize(font, (char *)p); +} +void pl_highlight(Image *b, Rectangle r){ + draw(b, r, pl_dark, pl_hilit, ZP); +} +void pl_clr(Image *b, Rectangle r){ + draw(b, r, display->white, 0, ZP); +} +void pl_fill(Image *b, Rectangle r){ + draw(b, r, plldepth==0? pl_white : pl_light, 0, ZP); +} +void pl_cpy(Image *b, Point dst, Rectangle src){ + draw(b, Rpt(dst, addpt(dst, subpt(src.max, src.min))), b, 0, src.min); +} diff --git a/sys/src/cmd/gopher/libpanel/edit.c b/sys/src/cmd/gopher/libpanel/edit.c new file mode 100644 index 000000000..987843135 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/edit.c @@ -0,0 +1,297 @@ +/* + * Interface includes: + * void plescroll(Panel *p, int top); + * move the given character position onto the top line + * void plegetsel(Panel *p, int *sel0, int *sel1); + * read the selection back + * int plelen(Panel *p); + * read the length of the text back + * Rune *pleget(Panel *p); + * get a pointer to the text + * void plesel(Panel *p, int sel0, int sel1); + * set the selection -- adjusts hiliting + * void plepaste(Panel *p, Rune *text, int ntext); + * replace the selection with the given text + */ +#include +#include +#include +#include +#include +#include "pldefs.h" +#include + +typedef struct Edit Edit; +struct Edit{ + Point minsize; + void (*hit)(Panel *); + int sel0, sel1; + Textwin *t; + Rune *text; + int ntext; +}; +void pl_drawedit(Panel *p){ + Edit *ep; + Panel *sb; + ep=p->data; + if(ep->t==0){ + ep->t=twnew(p->b, font, ep->text, ep->ntext); + if(ep->t==0){ + fprint(2, "pl_drawedit: can't allocate\n"); + exits("no mem"); + } + } + ep->t->b=p->b; + twreshape(ep->t, p->r); + twhilite(ep->t, ep->sel0, ep->sel1, 1); + sb=p->yscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, ep->t->top, ep->t->bot, ep->t->etext-ep->t->text); +} + +char *pl_snarfedit(Panel *p){ + int s0, s1; + Rune *t; + t=pleget(p); + plegetsel(p, &s0, &s1); + if(t==0 || s0>=s1) + return nil; + return smprint("%.*S", s1-s0, t+s0); +} +void pl_pasteedit(Panel *p, char *s){ + Rune *t; + if(t=runesmprint("%s", s)){ + plepaste(p, t, runestrlen(t)); + free(t); + } +} + +/* + * Should do double-clicks: + * If ep->sel0==ep->sel1 on entry and the + * call to twselect returns the same selection, then + * expand selections (| marks possible selection points, ... is expanded selection) + * <|...|> <> must nest + * (|...|) () must nest + * [|...|] [] must nest + * {|...|} {} must nest + * '|...|' no ' in ... + * "|...|" no " in ... + * \n|...|\n either newline may be the corresponding end of text + * include the trailing newline in the selection + * ...|I... I and ... are characters satisfying pl_idchar(I) + * ...I| + */ +int pl_hitedit(Panel *p, Mouse *m){ + Edit *ep; + ep=p->data; + if(ep->t && m->buttons&1){ + plgrabkb(p); + ep->t->b=p->b; + twhilite(ep->t, ep->sel0, ep->sel1, 0); + twselect(ep->t, m); + ep->sel0=ep->t->sel0; + ep->sel1=ep->t->sel1; + if((m->buttons&7)==3){ + plsnarf(p); + plepaste(p, 0, 0); /* cut */ + } + else if((m->buttons&7)==5) + plpaste(p); + else if(ep->hit) + (*ep->hit)(p); + } + return 0; +} +void pl_scrolledit(Panel *p, int dir, int buttons, int num, int den){ + Edit *ep; + Textwin *t; + Panel *sb; + int index, nline; + if(dir!=VERT) return; + ep=p->data; + t=ep->t; + if(t==0) return; + t->b=p->b; + switch(buttons){ + default: + return; + case 1: /* top line moves to mouse position */ + nline=(t->r.max.y-t->r.min.y)/t->hgt*num/den; + index=t->top; + while(index!=0 && nline!=0) + if(t->text[--index]=='\n') --nline; + break; + case 2: /* absolute */ + index=(t->etext-t->text)*num/den; + break; + case 4: /* mouse points at new top line */ + index=twpt2rune(t, + Pt(t->r.min.x, t->r.min.y+(t->r.max.y-t->r.min.y)*num/den)); + break; + } + while(index!=0 && t->text[index-1]!='\n') --index; + if(index!=t->top){ + twhilite(ep->t, ep->sel0, ep->sel1, 0); + twscroll(t, index); + p->scr.pos.y=t->top; + twhilite(ep->t, ep->sel0, ep->sel1, 1); + sb=p->yscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text); + } +} +void pl_typeedit(Panel *p, Rune c){ + Edit *ep; + Textwin *t; + int bot, scrolled; + Panel *sb; + ep=p->data; + t=ep->t; + if(t==0) return; + t->b=p->b; + twhilite(t, ep->sel0, ep->sel1, 0); + switch(c){ + case Kesc: + plsnarf(p); + plepaste(p, 0, 0); /* cut */ + break; + case Kdel: /* clear */ + ep->sel0=0; + ep->sel1=plelen(p); + plepaste(p, 0, 0); /* cut */ + break; + case Kbs: /* ^H: erase character */ + if(ep->sel0!=0) --ep->sel0; + twreplace(t, ep->sel0, ep->sel1, 0, 0); + break; + case Knack: /* ^U: erase line */ + while(ep->sel0!=0 && t->text[ep->sel0-1]!='\n') --ep->sel0; + twreplace(t, ep->sel0, ep->sel1, 0, 0); + break; + case Ketb: /* ^W: erase word */ + while(ep->sel0!=0 && !pl_idchar(t->text[ep->sel0-1])) --ep->sel0; + while(ep->sel0!=0 && pl_idchar(t->text[ep->sel0-1])) --ep->sel0; + twreplace(t, ep->sel0, ep->sel1, 0, 0); + break; + default: + if((c & 0xFF00) == KF || (c & 0xFF00) == Spec) + break; + twreplace(t, ep->sel0, ep->sel1, &c, 1); + ++ep->sel0; + break; + } + ep->sel1=ep->sel0; + /* + * Scroll up until ep->sel0 is above t->bot. + */ + scrolled=0; + do{ + bot=t->bot; + if(ep->sel0<=bot) break; + twscroll(t, twpt2rune(t, Pt(t->r.min.x, t->r.min.y+font->height))); + scrolled++; + }while(bot!=t->bot); + if(scrolled){ + sb=p->yscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text); + } + twhilite(t, ep->sel0, ep->sel1, 1); +} +Point pl_getsizeedit(Panel *p, Point children){ + USED(children); + return pl_boxsize(((Edit *)p->data)->minsize, p->state); +} +void pl_childspaceedit(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +void pl_freeedit(Panel *p){ + Edit *ep; + ep=p->data; + if(ep->t) twfree(ep->t); + ep->t=0; +} +void plinitedit(Panel *v, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){ + Edit *ep; + ep=v->data; + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawedit; + v->hit=pl_hitedit; + v->type=pl_typeedit; + v->getsize=pl_getsizeedit; + v->childspace=pl_childspaceedit; + v->free=pl_freeedit; + v->snarf=pl_snarfedit; + v->paste=pl_pasteedit; + v->kind="edit"; + ep->hit=hit; + ep->minsize=minsize; + ep->text=text; + ep->ntext=ntext; + if(ep->t!=0) twfree(ep->t); + ep->t=0; + ep->sel0=-1; + ep->sel1=-1; + v->scroll=pl_scrolledit; + v->scr.pos=Pt(0,0); + v->scr.size=Pt(ntext,0); +} +Panel *pledit(Panel *parent, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){ + Panel *v; + v=pl_newpanel(parent, sizeof(Edit)); + ((Edit *)v->data)->t=0; + plinitedit(v, flags, minsize, text, ntext, hit); + return v; +} +void plescroll(Panel *p, int top){ + Textwin *t; + t=((Edit*)p->data)->t; + if(t) twscroll(t, top); +} +void plegetsel(Panel *p, int *sel0, int *sel1){ + Edit *ep; + ep=p->data; + *sel0=ep->sel0; + *sel1=ep->sel1; +} +int plelen(Panel *p){ + Textwin *t; + t=((Edit*)p->data)->t; + if(t==0) return 0; + return t->etext-t->text; +} +Rune *pleget(Panel *p){ + Textwin *t; + t=((Edit*)p->data)->t; + if(t==0) return 0; + return t->text; +} +void plesel(Panel *p, int sel0, int sel1){ + Edit *ep; + ep=p->data; + if(ep->t==0) return; + ep->t->b=p->b; + twhilite(ep->t, ep->sel0, ep->sel1, 0); + ep->sel0=sel0; + ep->sel1=sel1; + twhilite(ep->t, ep->sel0, ep->sel1, 1); +} +void plepaste(Panel *p, Rune *text, int ntext){ + Edit *ep; + ep=p->data; + if(ep->t==0) return; + ep->t->b=p->b; + twhilite(ep->t, ep->sel0, ep->sel1, 0); + twreplace(ep->t, ep->sel0, ep->sel1, text, ntext); + ep->sel1=ep->sel0+ntext; + twhilite(ep->t, ep->sel0, ep->sel1, 1); + p->scr.size.y=ep->t->etext-ep->t->text; + p->scr.pos.y=ep->t->top; +} +void plemove(Panel *p, Point d){ + Edit *ep; + ep=p->data; + if(ep->t && !eqpt(d, Pt(0,0))) twmove(ep->t, d); +} diff --git a/sys/src/cmd/gopher/libpanel/entry.c b/sys/src/cmd/gopher/libpanel/entry.c new file mode 100644 index 000000000..19649d31e --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/entry.c @@ -0,0 +1,192 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +#include + +typedef struct Entry Entry; +struct Entry{ + char *entry; + char *entp; + char *eent; + void (*hit)(Panel *, char *); + Point minsize; +}; +#define SLACK 7 /* enough for one extra rune and ◀ and a nul */ +char *pl_snarfentry(Panel *p){ + Entry *ep; + int n; + + if(p->flags&USERFL) /* no snarfing from password entry */ + return nil; + ep=p->data; + n=utfnlen(ep->entry, ep->entp-ep->entry); + if(n<1) return nil; + return smprint("%.*s", n, ep->entry); +} +void pl_pasteentry(Panel *p, char *s){ + Entry *ep; + char *e; + int n, m; + + ep=p->data; + n=ep->entp-ep->entry; + m=strlen(s); + e=pl_erealloc(ep->entry,n+m+SLACK); + ep->entry=e; + e+=n; + strncpy(e, s, m); + e+=m; + *e='\0'; + ep->entp=ep->eent=e; + pldraw(p, p->b); +} +void pl_drawentry(Panel *p){ + Rectangle r; + Entry *ep; + char *s; + + ep=p->data; + r=pl_box(p->b, p->r, p->state); + s=ep->entry; + if(p->flags & USERFL){ + char *p; + s=strdup(s); + for(p=s; *p; p++) + *p='*'; + } + if(stringwidth(font, s)<=r.max.x-r.min.x) + pl_drawicon(p->b, r, PLACEW, 0, s); + else + pl_drawicon(p->b, r, PLACEE, 0, s); + if(s != ep->entry) + free(s); +} +int pl_hitentry(Panel *p, Mouse *m){ + if((m->buttons&7)==1){ + plgrabkb(p); + + p->state=DOWN; + pldraw(p, p->b); + while(m->buttons&1){ + int old; + old=m->buttons; + if(display->bufp > display->buf) + flushimage(display, 1); + *m=emouse(); + if((old&7)==1){ + if((m->buttons&7)==3){ + Entry *ep; + + plsnarf(p); + + /* cut */ + ep=p->data; + ep->entp=ep->entry; + *ep->entp='\0'; + pldraw(p, p->b); + } + if((m->buttons&7)==5) + plpaste(p); + } + } + p->state=UP; + pldraw(p, p->b); + } + return 0; +} +void pl_typeentry(Panel *p, Rune c){ + int n; + Entry *ep; + ep=p->data; + switch(c){ + case '\n': + case '\r': + *ep->entp='\0'; + if(ep->hit) ep->hit(p, ep->entry); + return; + case Kesc: + plsnarf(p); + /* no break */ + case Kdel: /* clear */ + case Knack: /* ^U: erase line */ + ep->entp=ep->entry; + *ep->entp='\0'; + break; + case Kbs: /* ^H: erase character */ + while(ep->entp!=ep->entry && !pl_rune1st(ep->entp[-1])) *--ep->entp='\0'; + if(ep->entp!=ep->entry) *--ep->entp='\0'; + break; + case Ketb: /* ^W: erase word */ + while(ep->entp!=ep->entry && !pl_idchar(ep->entp[-1])) + --ep->entp; + while(ep->entp!=ep->entry && pl_idchar(ep->entp[-1])) + --ep->entp; + *ep->entp='\0'; + break; + default: + if(c < 0x20 || (c & 0xFF00) == KF || (c & 0xFF00) == Spec) + break; + ep->entp+=runetochar(ep->entp, &c); + if(ep->entp>ep->eent){ + n=ep->entp-ep->entry; + ep->entry=pl_erealloc(ep->entry, n+100+SLACK); + ep->entp=ep->entry+n; + ep->eent=ep->entp+100; + } + *ep->entp='\0'; + break; + } + pldraw(p, p->b); +} +Point pl_getsizeentry(Panel *p, Point children){ + USED(children); + return pl_boxsize(((Entry *)p->data)->minsize, p->state); +} +void pl_childspaceentry(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +void pl_freeentry(Panel *p){ + Entry *ep; + ep = p->data; + free(ep->entry); + ep->entry = ep->eent = ep->entp = 0; +} +void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, char *)){ + int elen; + Entry *ep; + ep=v->data; + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawentry; + v->hit=pl_hitentry; + v->type=pl_typeentry; + v->getsize=pl_getsizeentry; + v->childspace=pl_childspaceentry; + ep->minsize=Pt(wid, font->height); + v->free=pl_freeentry; + v->snarf=pl_snarfentry; + v->paste=pl_pasteentry; + elen=100; + if(str) elen+=strlen(str); + ep->entry=pl_erealloc(ep->entry, elen+SLACK); + ep->eent=ep->entry+elen; + strecpy(ep->entry, ep->eent, str ? str : ""); + ep->entp=ep->entry+strlen(ep->entry); + ep->hit=hit; + v->kind="entry"; +} +Panel *plentry(Panel *parent, int flags, int wid, char *str, void (*hit)(Panel *, char *)){ + Panel *v; + v=pl_newpanel(parent, sizeof(Entry)); + plinitentry(v, flags, wid, str, hit); + return v; +} +char *plentryval(Panel *p){ + Entry *ep; + ep=p->data; + *ep->entp='\0'; + return ep->entry; +} diff --git a/sys/src/cmd/gopher/libpanel/event.c b/sys/src/cmd/gopher/libpanel/event.c new file mode 100644 index 000000000..4a9355d58 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/event.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" + +void plgrabkb(Panel *g){ + plkbfocus=g; +} +void plkeyboard(Rune c){ + if(plkbfocus) + plkbfocus->type(plkbfocus, c); +} + +/* + * Return the most leafward, highest priority panel containing p + */ +Panel *pl_ptinpanel(Point p, Panel *g){ + Panel *v; + for(;g;g=g->next) if(ptinrect(p, g->r)){ + v=pl_ptinpanel(p, g->child); + if(v && v->pri(v, p)>=g->pri(g, p)) return v; + return g; + } + return 0; +} +void plmouse(Panel *g, Mouse *m){ + Panel *hit, *last; + if(g->flags&REMOUSE) + hit=g->lastmouse; + else{ + hit=pl_ptinpanel(m->xy, g); + last=g->lastmouse; + if(last && last!=hit){ + m->buttons|=OUT; + last->hit(last, m); + m->buttons&=~OUT; + } + } + if(hit){ + if(hit->hit(hit, m)) + g->flags|=REMOUSE; + else + g->flags&=~REMOUSE; + g->lastmouse=hit; + } +} diff --git a/sys/src/cmd/gopher/libpanel/frame.c b/sys/src/cmd/gopher/libpanel/frame.c new file mode 100644 index 000000000..cbfac12d7 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/frame.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +void pl_drawframe(Panel *p){ + pl_box(p->b, p->r, FRAME); +} +int pl_hitframe(Panel *p, Mouse *m){ + USED(p, m); + return 0; +} +void pl_typeframe(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizeframe(Panel *p, Point children){ + USED(p); + return pl_boxsize(children, FRAME); +} +void pl_childspaceframe(Panel *p, Point *ul, Point *size){ + USED(p); + pl_interior(FRAME, ul, size); +} +void plinitframe(Panel *v, int flags){ + v->flags=flags; + v->draw=pl_drawframe; + v->hit=pl_hitframe; + v->type=pl_typeframe; + v->getsize=pl_getsizeframe; + v->childspace=pl_childspaceframe; + v->kind="frame"; +} +Panel *plframe(Panel *parent, int flags){ + Panel *p; + p=pl_newpanel(parent, 0); + plinitframe(p, flags); + return p; +} diff --git a/sys/src/cmd/gopher/libpanel/group.c b/sys/src/cmd/gopher/libpanel/group.c new file mode 100644 index 000000000..7a0fa7e74 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/group.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +void pl_drawgroup(Panel *p){ + USED(p); +} +int pl_hitgroup(Panel *p, Mouse *m){ + USED(p, m); + return 0; +} +void pl_typegroup(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizegroup(Panel *p, Point children){ + USED(p); + return children; +} +void pl_childspacegroup(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +void plinitgroup(Panel *v, int flags){ + v->flags=flags; + v->draw=pl_drawgroup; + v->hit=pl_hitgroup; + v->type=pl_typegroup; + v->getsize=pl_getsizegroup; + v->childspace=pl_childspacegroup; + v->kind="group"; +} +Panel *plgroup(Panel *parent, int flags){ + Panel *p; + p=pl_newpanel(parent, 0); + plinitgroup(p, flags); + return p; +} diff --git a/sys/src/cmd/gopher/libpanel/init.c b/sys/src/cmd/gopher/libpanel/init.c new file mode 100644 index 000000000..452909722 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/init.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +/* + * Just a wrapper for all the initialization routines + */ +int plinit(int ldepth){ + if(!pl_drawinit(ldepth)) return 0; + return 1; +} diff --git a/sys/src/cmd/gopher/libpanel/label.c b/sys/src/cmd/gopher/libpanel/label.c new file mode 100644 index 000000000..c7f16be54 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/label.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Label Label; +struct Label{ + int placement; + Icon *icon; +}; +void pl_drawlabel(Panel *p){ + Label *l; + l=p->data; + pl_drawicon(p->b, pl_box(p->b, p->r, PASSIVE), l->placement, p->flags, l->icon); +} +int pl_hitlabel(Panel *p, Mouse *m){ + USED(p, m); + return 0; +} +void pl_typelabel(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizelabel(Panel *p, Point children){ + USED(children); /* shouldn't have any children */ + return pl_boxsize(pl_iconsize(p->flags, ((Label *)p->data)->icon), PASSIVE); +} +void pl_childspacelabel(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +void plinitlabel(Panel *v, int flags, Icon *icon){ + v->flags=flags|LEAF; + ((Label *)(v->data))->icon=icon; + v->draw=pl_drawlabel; + v->hit=pl_hitlabel; + v->type=pl_typelabel; + v->getsize=pl_getsizelabel; + v->childspace=pl_childspacelabel; + v->kind="label"; +} +Panel *pllabel(Panel *parent, int flags, Icon *icon){ + Panel *p; + p=pl_newpanel(parent, sizeof(Label)); + plinitlabel(p, flags, icon); + plplacelabel(p, PLACECEN); + return p; +} +void plplacelabel(Panel *p, int placement){ + ((Label *)(p->data))->placement=placement; +} diff --git a/sys/src/cmd/gopher/libpanel/list.c b/sys/src/cmd/gopher/libpanel/list.c new file mode 100644 index 000000000..8f4b0766f --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/list.c @@ -0,0 +1,190 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct List List; +struct List{ + void (*hit)(Panel *, int, int); /* call user back on hit */ + char *(*gen)(Panel *, int); /* return text given index or 0 if out of range */ + int lo; /* indices of first, last items displayed */ + int sel; /* index of hilited item */ + int len; /* # of items in list */ + Rectangle listr; + Point minsize; + int buttons; +}; +#define MAXHGT 12 +void pl_listsel(Panel *p, int sel, int on){ + List *lp; + int hi; + Rectangle r; + lp=p->data; + hi=lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height; + if(lp->lo>=0 && lp->lo<=sel && sellen){ + r=lp->listr; + r.min.y+=(sel-lp->lo)*font->height; + r.max.y=r.min.y+font->height; + if(on) + pl_highlight(p->b, r); + else{ + pl_fill(p->b, r); + pl_drawicon(p->b, r, PLACEW, 0, lp->gen(p, sel)); + } + } +} +void pl_liststrings(Panel *p, int lo, int hi, Rectangle r){ + Panel *sb; + List *lp; + char *s; + int i; + lp=p->data; + for(i=lo;i!=hi && (s=lp->gen(p, i));i++){ + r.max.y=r.min.y+font->height; + pl_drawicon(p->b, r, PLACEW, 0, s); + r.min.y+=font->height; + } + if(lo<=lp->sel && lp->selsel, 1); + sb=p->yscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, lp->lo, + lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, lp->len); +} +void pl_drawlist(Panel *p){ + List *lp; + lp=p->data; + lp->listr=pl_box(p->b, p->r, UP); + pl_liststrings(p, lp->lo, lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, + lp->listr); +} +int pl_hitlist(Panel *p, Mouse *m){ + int oldsel, hitme; + Point ul, size; + List *lp; + lp=p->data; + hitme=0; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + oldsel=lp->sel; + if(m->buttons&OUT){ + p->state=UP; + if(m->buttons&~OUT) lp->sel=-1; + } + else if(p->state==DOWN || m->buttons&7){ + lp->sel=(m->xy.y-ul.y)/font->height+lp->lo; + if(m->buttons&7){ + lp->buttons=m->buttons; + p->state=DOWN; + } + else{ + hitme=1; + p->state=UP; + } + } + if(oldsel!=lp->sel){ + pl_listsel(p, oldsel, 0); + pl_listsel(p, lp->sel, 1); + } + if(hitme && 0<=lp->sel && lp->sellen && lp->hit) + lp->hit(p, lp->buttons, lp->sel); + return 0; +} +void pl_scrolllist(Panel *p, int dir, int buttons, int val, int len){ + Point ul, size; + int nlist, oldlo, hi, nline, y; + List *lp; + Rectangle r; + lp=p->data; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + nlist=size.y/font->height; + oldlo=lp->lo; + if(dir==VERT) switch(buttons){ + case 1: lp->lo-=nlist*val/len; break; + case 2: lp->lo=lp->len*val/len; break; + case 4: lp->lo+=nlist*val/len; break; + } + if(lp->lo<0) lp->lo=0; + if(lp->lo>=lp->len) lp->lo=lp->len-1; + if(lp->lo==oldlo) return; + p->scr.pos.y=lp->lo; + r=lp->listr; + nline=(r.max.y-r.min.y)/font->height; + hi=lp->lo+nline; + if(hi<=oldlo || lp->lo>=oldlo+nline){ + pl_box(p->b, r, PASSIVE); + pl_liststrings(p, lp->lo, hi, r); + } + else if(lp->lolo)*font->height; + pl_cpy(p->b, Pt(r.min.x, y), + Rect(r.min.x, r.min.y, r.max.x, r.min.y+(hi-oldlo)*font->height)); + r.max.y=y; + pl_box(p->b, r, PASSIVE); + pl_liststrings(p, lp->lo, oldlo, r); + } + else{ + pl_cpy(p->b, r.min, Rect(r.min.x, r.min.y+(lp->lo-oldlo)*font->height, + r.max.x, r.max.y)); + r.min.y=r.min.y+(oldlo+nline-lp->lo)*font->height; + pl_box(p->b, r, PASSIVE); + pl_liststrings(p, oldlo+nline, hi, r); + } +} +void pl_typelist(Panel *g, Rune c){ + USED(g, c); +} +Point pl_getsizelist(Panel *p, Point children){ + USED(children); + return pl_boxsize(((List *)p->data)->minsize, p->state); +} +void pl_childspacelist(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +void plinitlist(Panel *v, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){ + List *lp; + int wid, max; + char *str; + lp=v->data; + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawlist; + v->hit=pl_hitlist; + v->type=pl_typelist; + v->getsize=pl_getsizelist; + v->childspace=pl_childspacelist; + lp->gen=gen; + lp->hit=hit; + max=0; + for(lp->len=0;str=gen(v, lp->len);lp->len++){ + wid=stringwidth(font, str); + if(wid>max) max=wid; + } + if(flags&(FILLX|EXPAND)){ + for(lp->len=0;gen(v, lp->len);lp->len++); + lp->minsize=Pt(0, nlist*font->height); + } + else{ + max=0; + for(lp->len=0;str=gen(v, lp->len);lp->len++){ + wid=stringwidth(font, str); + if(wid>max) max=wid; + } + lp->minsize=Pt(max, nlist*font->height); + } + lp->sel=-1; + lp->lo=0; + v->scroll=pl_scrolllist; + v->scr.pos=Pt(0,0); + v->scr.size=Pt(0,lp->len); + v->kind="list"; +} +Panel *pllist(Panel *parent, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){ + Panel *v; + v=pl_newpanel(parent, sizeof(List)); + plinitlist(v, flags, gen, nlist, hit); + return v; +} diff --git a/sys/src/cmd/gopher/libpanel/mem.c b/sys/src/cmd/gopher/libpanel/mem.c new file mode 100644 index 000000000..4816df5b3 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/mem.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +void *pl_emalloc(int n){ + void *v; + v=mallocz(n, 1); + if(v==0){ + fprint(2, "Can't malloc!\n"); + exits("no mem"); + } + setmalloctag(v, getcallerpc(&n)); + return v; +} +void *pl_erealloc(void *v, int n) +{ + v=realloc(v, n); + if(v==0){ + fprint(2, "Can't realloc!\n"); + exits("no mem"); + } + setrealloctag(v, getcallerpc(&v)); + return v; +} +void pl_unexpected(Panel *g, char *rou){ + fprint(2, "%s called unexpectedly (%s %#p)\n", rou, g->kind, g); + abort(); +} +void pl_drawerror(Panel *g){ + pl_unexpected(g, "draw"); +} +int pl_hiterror(Panel *g, Mouse *m){ + USED(m); + pl_unexpected(g, "hit"); + return 0; +} +void pl_typeerror(Panel *g, Rune c){ + USED(c); + pl_unexpected(g, "type"); +} +Point pl_getsizeerror(Panel *g, Point childsize){ + pl_unexpected(g, "getsize"); + return childsize; +} +void pl_childspaceerror(Panel *g, Point *ul, Point *size){ + USED(ul, size); + pl_unexpected(g, "childspace"); +} +void pl_scrollerror(Panel *g, int dir, int button, int num, int den){ + USED(dir, button, num, den); + pl_unexpected(g, "scroll"); +} +void pl_setscrollbarerror(Panel *g, int top, int bot, int den){ + USED(top, bot, den); + pl_unexpected(g, "setscrollbar"); +} +int pl_prinormal(Panel *, Point){ + return PRI_NORMAL; +} +Panel *pl_newpanel(Panel *parent, int ndata){ + Panel *v; + if(parent && parent->flags&LEAF){ + fprint(2, "newpanel: can't create child of %s %#p\n", parent->kind, parent); + exits("bad newpanel"); + } + v=pl_emalloc(sizeof(Panel)); + v->r=Rect(0,0,0,0); + v->flags=0; + v->ipad=Pt(0,0); + v->pad=Pt(0,0); + v->size=Pt(0,0); + v->sizereq=Pt(0,0); + v->lastmouse=0; + v->next=0; + v->child=0; + v->echild=0; + v->b=0; + v->pri=pl_prinormal; + v->scrollee=0; + v->xscroller=0; + v->yscroller=0; + v->parent=parent; + v->scr.pos=Pt(0,0); + v->scr.size=Pt(0,0); + if(parent){ + if(parent->child==0) + parent->child=v; + else + parent->echild->next=v; + parent->echild=v; + } + v->draw=pl_drawerror; + v->hit=pl_hiterror; + v->type=pl_typeerror; + v->getsize=pl_getsizeerror; + v->childspace=pl_childspaceerror; + v->scroll=pl_scrollerror; + v->setscrollbar=pl_setscrollbarerror; + v->free=0; + v->snarf=0; + v->paste=0; + if(ndata) + v->data=pl_emalloc(ndata); + else + v->data=0; + return v; +} +void plfree(Panel *p){ + Panel *cp, *ncp; + if(p==0) + return; + if(p==plkbfocus) + plkbfocus=0; + for(cp=p->child;cp;cp=ncp){ + ncp=cp->next; + plfree(cp); + } + if(p->free) p->free(p); + if(p->data) free(p->data); + free(p); +} diff --git a/sys/src/cmd/gopher/libpanel/message.c b/sys/src/cmd/gopher/libpanel/message.c new file mode 100644 index 000000000..7c3dbed07 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/message.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Message Message; +struct Message{ + char *text; + Point minsize; +}; +void pl_textmsg(Image *b, Rectangle r, Font *f, char *s){ + char *start, *end; /* of line */ + Point where; + int lwid, c, wid; + where=r.min; + wid=r.max.x-r.min.x; + do{ + start=s; + lwid=0; + end=s; + do{ + for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); + if(lwid>wid) break; + end=s; + for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); + }while(*s!='\0'); + if(end==start) /* can't even fit one word on line! */ + end=s; + c=*end; + *end='\0'; + string(b, where, display->black, ZP, f, start); + *end=c; + where.y+=font->height; + s=end; + while(*s==' ') s=pl_nextrune(s); + }while(*s!='\0'); +} +Point pl_foldsize(Font *f, char *s, int wid){ + char *start, *end; /* of line */ + Point size; + int lwid, ewid; + size=Pt(0,0); + do{ + start=s; + lwid=0; + end=s; + ewid=lwid; + do{ + for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); + if(lwid>wid) break; + end=s; + ewid=lwid; + for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); + }while(*s!='\0'); + if(end==start){ /* can't even fit one word on line! */ + ewid=lwid; + end=s; + } + if(ewid>size.x) size.x=ewid; + size.y+=font->height; + s=end; + while(*s==' ') s=pl_nextrune(s); + }while(*s!='\0'); + return size; +} +void pl_drawmessage(Panel *p){ + pl_textmsg(p->b, pl_box(p->b, p->r, PASSIVE), font, ((Message *)p->data)->text); +} +int pl_hitmessage(Panel *g, Mouse *m){ + USED(g, m); + return 0; +} +void pl_typemessage(Panel *g, Rune c){ + USED(g, c); +} +Point pl_getsizemessage(Panel *p, Point children){ + Message *mp; + USED(children); + mp=p->data; + return pl_boxsize(pl_foldsize(font, mp->text, mp->minsize.x), PASSIVE); +} +void pl_childspacemessage(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +void plinitmessage(Panel *v, int flags, int wid, char *msg){ + Message *mp; + mp=v->data; + v->flags=flags|LEAF; + v->draw=pl_drawmessage; + v->hit=pl_hitmessage; + v->type=pl_typemessage; + v->getsize=pl_getsizemessage; + v->childspace=pl_childspacemessage; + mp->text=msg; + mp->minsize=Pt(wid, font->height); + v->kind="message"; +} +Panel *plmessage(Panel *parent, int flags, int wid, char *msg){ + Panel *v; + v=pl_newpanel(parent, sizeof(Message)); + plinitmessage(v, flags, wid, msg); + return v; +} diff --git a/sys/src/cmd/gopher/libpanel/mkfile b/sys/src/cmd/gopher/libpanel/mkfile new file mode 100644 index 000000000..5939cb6a6 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/mkfile @@ -0,0 +1,34 @@ + +#include +#include +#include +#include +#include "pldefs.h" +int pl_max(int a, int b){ + return a>b?a:b; +} +Point pl_sizesibs(Panel *p){ + Point s; + if(p==0) return Pt(0,0); + s=pl_sizesibs(p->next); + switch(p->flags&PACK){ + case PACKN: + case PACKS: + s.x=pl_max(s.x, p->sizereq.x); + s.y+=p->sizereq.y; + break; + case PACKE: + case PACKW: + s.x+=p->sizereq.x; + s.y=pl_max(s.y, p->sizereq.y); + break; + } + return s; +} +/* + * Compute the requested size of p and its descendants. + */ +void pl_sizereq(Panel *p){ + Panel *cp; + Point maxsize; + maxsize=Pt(0,0); + for(cp=p->child;cp;cp=cp->next){ + pl_sizereq(cp); + if(cp->sizereq.x>maxsize.x) maxsize.x=cp->sizereq.x; + if(cp->sizereq.y>maxsize.y) maxsize.y=cp->sizereq.y; + } + for(cp=p->child;cp;cp=cp->next){ + if(cp->flags&MAXX) cp->sizereq.x=maxsize.x; + if(cp->flags&MAXY) cp->sizereq.y=maxsize.y; + } + p->childreq=pl_sizesibs(p->child); + p->sizereq=addpt(addpt(p->getsize(p, p->childreq), p->ipad), p->pad); + if(p->flags&FIXEDX) p->sizereq.x=p->fixedsize.x; + if(p->flags&FIXEDY) p->sizereq.y=p->fixedsize.y; +} +Point pl_getshare(Panel *p){ + Point share; + if(p==0) return Pt(0,0); + share=pl_getshare(p->next); + if(p->flags&EXPAND) switch(p->flags&PACK){ + case PACKN: + case PACKS: + if(share.x==0) share.x=1; + share.y++; + break; + case PACKE: + case PACKW: + share.x++; + if(share.y==0) share.y=1; + break; + } + return share; +} +/* + * Set the sizes and rectangles of p and its descendants, given their requested sizes. + */ +void pl_setrect(Panel *p, Point ul, Point avail){ + Point space, newul, newspace, slack, share; + int l; + Panel *c; + p->size=subpt(p->sizereq, p->pad); + ul=addpt(ul, divpt(p->pad, 2)); + avail=subpt(avail, p->pad); + if(p->size.x>avail.x) + p->size.x = avail.x; + if(p->size.y>avail.y) + p->size.y = avail.y; + if(p->flags&(FILLX|EXPAND)) p->size.x=avail.x; + if(p->flags&(FILLY|EXPAND)) p->size.y=avail.y; + switch(p->flags&PLACE){ + case PLACECEN: ul.x+=(avail.x-p->size.x)/2; ul.y+=(avail.y-p->size.y)/2; break; + case PLACES: ul.x+=(avail.x-p->size.x)/2; ul.y+= avail.y-p->size.y ; break; + case PLACEE: ul.x+= avail.x-p->size.x ; ul.y+=(avail.y-p->size.y)/2; break; + case PLACEW: ul.y+=(avail.y-p->size.y)/2; break; + case PLACEN: ul.x+=(avail.x-p->size.x)/2; break; + case PLACENE: ul.x+= avail.x-p->size.x ; break; + case PLACENW: break; + case PLACESE: ul.x+= avail.x-p->size.x ; ul.y+= avail.y-p->size.y ; break; + case PLACESW: ul.y+= avail.y-p->size.y ; break; + } + p->r=Rpt(ul, addpt(ul, p->size)); + space=p->size; + p->childspace(p, &ul, &space); + slack=subpt(space, p->childreq); + share=pl_getshare(p->child); + for(c=p->child;c;c=c->next){ + if(c->flags&IGNORE) continue; + if(c->flags&EXPAND){ + switch(c->flags&PACK){ + case PACKN: + case PACKS: + c->sizereq.x+=slack.x; + l=slack.y/share.y; + c->sizereq.y+=l; + slack.y-=l; + --share.y; + break; + case PACKE: + case PACKW: + l=slack.x/share.x; + c->sizereq.x+=l; + slack.x-=l; + --share.x; + c->sizereq.y+=slack.y; + break; + } + } + switch(c->flags&PACK){ + case PACKN: + newul=Pt(ul.x, ul.y+c->sizereq.y); + newspace=Pt(space.x, space.y-c->sizereq.y); + pl_setrect(c, ul, Pt(space.x, c->sizereq.y)); + break; + case PACKW: + newul=Pt(ul.x+c->sizereq.x, ul.y); + newspace=Pt(space.x-c->sizereq.x, space.y); + pl_setrect(c, ul, Pt(c->sizereq.x, space.y)); + break; + case PACKS: + newul=ul; + newspace=Pt(space.x, space.y-c->sizereq.y); + pl_setrect(c, Pt(ul.x, ul.y+space.y-c->sizereq.y), + Pt(space.x, c->sizereq.y)); + break; + case PACKE: + newul=ul; + newspace=Pt(space.x-c->sizereq.x, space.y); + pl_setrect(c, Pt(ul.x+space.x-c->sizereq.x, ul.y), + Pt(c->sizereq.x, space.y)); + break; + } + ul=newul; + space=newspace; + } +} +void plpack(Panel *p, Rectangle where){ + pl_sizereq(p); + pl_setrect(p, where.min, subpt(where.max, where.min)); +} +/* + * move an already-packed panel so that p->r=raddp(p->r, d) + */ +void plmove(Panel *p, Point d){ + if(strcmp(p->kind, "edit") == 0) /* sorry */ + plemove(p, d); + p->r=rectaddpt(p->r, d); + for(p=p->child;p;p=p->next) plmove(p, d); +} diff --git a/sys/src/cmd/gopher/libpanel/panel.h b/sys/src/cmd/gopher/libpanel/panel.h new file mode 100644 index 000000000..5d5134c28 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/panel.h @@ -0,0 +1,203 @@ +//#pragma src "/sys/src/libpanel" +//#pragma lib "libpanel.a" +typedef struct Scroll Scroll; +typedef struct Panel Panel; /* a Graphical User Interface element */ +typedef struct Rtext Rtext; /* formattable text */ +typedef void Icon; /* Always used as Icon * -- Image or char */ +typedef struct Idol Idol; /* A picture/text combo */ +struct Scroll{ + Point pos, size; +}; +struct Rtext{ + int flags; /* responds to hits? text selection? */ + void *user; /* user data */ + int space; /* how much space before, if no break */ + int indent; /* how much space before, after a break */ + int voff; /* vertical offset (for subscripts and superscripts) */ + Image *b; /* what to display, if nonzero */ + Panel *p; /* what to display, if nonzero and b==0 */ + Font *font; /* font in which to draw text */ + char *text; /* what to display, if b==0 and p==0 */ + Rtext *next; /* next piece */ + /* private below */ + Rtext *nextline; /* links line to line */ + Rtext *last; /* last, for append */ + Rectangle r; /* where to draw, if origin were Pt(0,0) */ + int topy; /* y coord of top of line */ + int wid; /* not including space */ +}; +struct Panel{ + Point ipad, pad; /* extra space inside and outside */ + Point fixedsize; /* size of Panel, if FIXED */ + int user; /* available for user */ + void *userp; /* available for user */ + Rectangle r; /* where the Panel goes */ + /* private below */ + Panel *next; /* It's a list! */ + Panel *child, *echild, *parent; /* No, it's a tree! */ + Image *b; /* where we're drawn */ + int flags; /* position flags, see below */ + char *kind; /* what kind of panel? */ + int state; /* for hitting & drawing purposes */ + Point size; /* space for this Panel */ + Point sizereq; /* size requested by this Panel */ + Point childreq; /* total size needed by children */ + Panel *lastmouse; /* who got the last mouse event? */ + Panel *scrollee; /* pointer to scrolled window */ + Panel *xscroller, *yscroller; /* pointers to scroll bars */ + Scroll scr; /* scroll data */ + void *data; /* kind-specific data */ + void (*draw)(Panel *); /* draw panel and children */ + int (*pri)(Panel *, Point); /* priority for hitting */ + int (*hit)(Panel *, Mouse *); /* process mouse event */ + void (*type)(Panel *, Rune); /* process keyboard event */ + Point (*getsize)(Panel *, Point); /* return size, given child size */ + void (*childspace)(Panel *, Point *, Point *); /* child ul & size given our size */ + void (*scroll)(Panel *, int, int, int, int); /* scroll bar to scrollee */ + void (*setscrollbar)(Panel *, int, int, int); /* scrollee to scroll bar */ + void (*free)(Panel *); /* free fields of data when done */ + char* (*snarf)(Panel *); /* snarf text from panel */ + void (*paste)(Panel *, char *); /* paste text into panel */ +}; +/* + * Panel flags + */ +#define PACK 0x0007 /* which side of the parent is the Panel attached to? */ +#define PACKN 0x0000 +#define PACKE 0x0001 +#define PACKS 0x0002 +#define PACKW 0x0003 +#define PACKCEN 0x0004 /* only used by pulldown */ +#define FILLX 0x0008 /* grow horizontally to fill the available space */ +#define FILLY 0x0010 /* grow vertically to fill the available space */ +#define PLACE 0x01e0 /* which side of its space should the Panel adhere to? */ +#define PLACECEN 0x0000 +#define PLACES 0x0020 +#define PLACEE 0x0040 +#define PLACEW 0x0060 +#define PLACEN 0x0080 +#define PLACENE 0x00a0 +#define PLACENW 0x00c0 +#define PLACESE 0x00e0 +#define PLACESW 0x0100 +#define EXPAND 0x0200 /* use up all extra space in the parent */ +#define FIXED 0x0c00 /* don't pass children's size requests through to parent */ +#define FIXEDX 0x0400 +#define FIXEDY 0x0800 +#define MAXX 0x1000 /* make x size as big as biggest sibling's */ +#define MAXY 0x2000 /* make y size as big as biggest sibling's */ +#define BITMAP 0x4000 /* text argument is a bitmap, not a string */ +#define NOBORDER 0x8000 +/* pldefs.h flags 0x08000-0x40000 */ +#define IGNORE 0x080000 /* ignore this panel totally */ +#define USERFL 0x100000 /* start of user flag */ + +/* + * An extra bit in Mouse.buttons + */ +#define OUT 8 /* Mouse.buttons bit, set when mouse leaves Panel */ +/* + * Priorities + */ +#define PRI_NORMAL 0 /* ordinary panels */ +#define PRI_POPUP 1 /* popup menus */ +#define PRI_SCROLLBAR 2 /* scroll bars */ + +/* Rtext.flags */ +#define PL_HOT 1 +#define PL_SEL 2 +#define PL_STR 4 +#define PL_HEAD 8 + +Panel *plkbfocus; /* the panel in keyboard focus */ + +int plinit(int); /* initialization */ +void plpack(Panel *, Rectangle); /* figure out where to put the Panel & children */ +void plmove(Panel *, Point); /* move an already-packed panel to a new location */ +void pldraw(Panel *, Image *); /* display the panel on the bitmap */ +void plfree(Panel *); /* give back space */ +void plgrabkb(Panel *); /* this Panel should receive keyboard events */ +void plkeyboard(Rune); /* send a keyboard event to the appropriate Panel */ +void plmouse(Panel *, Mouse *); /* send a Mouse event to a Panel tree */ +void plscroll(Panel *, Panel *, Panel *); /* link up scroll bars */ +char *plentryval(Panel *); /* entry delivers its value */ +void plsetbutton(Panel *, int); /* set or clear the mark on a button */ +void plsetslider(Panel *, int, int); /* set the value of a slider */ +Rune *pleget(Panel *); /* get the text from an edit window */ +int plelen(Panel *); /* get the length of the text from an edit window */ +void plegetsel(Panel *, int *, int *); /* get the selection from an edit window */ +void plepaste(Panel *, Rune *, int); /* paste in an edit window */ +void plesel(Panel *, int, int); /* set the selection in an edit window */ +void plescroll(Panel *, int); /* scroll an edit window */ +Scroll plgetscroll(Panel *); /* get scrolling information from panel */ +void plsetscroll(Panel *, Scroll); /* set scrolling information */ +void plplacelabel(Panel *, int); /* label placement */ + +/* + * Panel creation & reinitialization functions + */ +Panel *plbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int)); +Panel *plcanvas(Panel *pl, int, void (*)(Panel *), void (*)(Panel *pl, Mouse *)); +Panel *plcheckbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int)); +Panel *pledit(Panel *, int, Point, Rune *, int, void (*)(Panel *)); +Panel *plentry(Panel *pl, int, int, char *, void (*)(Panel *pl, char *)); +Panel *plframe(Panel *pl, int); +Panel *plgroup(Panel *pl, int); +Panel *plidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*)); +Panel *pllabel(Panel *pl, int, Icon *); +Panel *pllist(Panel *pl, int, char *(*)(Panel *, int), int, void(*)(Panel *pl, int, int)); +Panel *plmenu(Panel *pl, int, Icon **, int, void (*)(int, int)); +Panel *plmenubar(Panel *pl, int, int, Icon *, Panel *pl, Icon *, ...); +Panel *plmessage(Panel *pl, int, int, char *); +Panel *plpopup(Panel *pl, int, Panel *pl, Panel *pl, Panel *pl); +Panel *plpulldown(Panel *pl, int, Icon *, Panel *pl, int); +Panel *plradiobutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int)); +Panel *plscrollbar(Panel *plparent, int flags); +Panel *plslider(Panel *pl, int, Point, void(*)(Panel *pl, int, int, int)); +Panel *pltextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *)); +void plinitbutton(Panel *, int, Icon *, void (*)(Panel *, int)); +void plinitcanvas(Panel *, int, void (*)(Panel *), void (*)(Panel *, Mouse *)); +void plinitcheckbutton(Panel *, int, Icon *, void (*)(Panel *, int, int)); +void plinitedit(Panel *, int, Point, Rune *, int, void (*)(Panel *)); +void plinitentry(Panel *, int, int, char *, void (*)(Panel *, char *)); +void plinitframe(Panel *, int); +void plinitgroup(Panel *, int); +void plinitidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*)); +void plinitlabel(Panel *, int, Icon *); +void plinitlist(Panel *, int, char *(*)(Panel *, int), int, void(*)(Panel *, int, int)); +void plinitmenu(Panel *, int, Icon **, int, void (*)(int, int)); +void plinitmessage(Panel *, int, int, char *); +void plinitpopup(Panel *, int, Panel *, Panel *, Panel *); +void plinitpulldown(Panel *, int, Icon *, Panel *, int); +void plinitradiobutton(Panel *, int, Icon *, void (*)(Panel *, int, int)); +void plinitscrollbar(Panel *parent, int flags); +void plinitslider(Panel *, int, Point, void(*)(Panel *, int, int, int)); +void plinittextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *)); +/* + * Rtext constructors & destructor + */ +Rtext *plrtstr(Rtext **, int, int, int, Font *, char *, int, void *); +Rtext *plrtbitmap(Rtext **, int, int, int, Image *, int, void *); +Rtext *plrtpanel(Rtext **, int, int, int, Panel *, void *); +void plrtfree(Rtext *); +void plrtseltext(Rtext *, Rtext *, Rtext *); +char *plrtsnarftext(Rtext *); + +int plgetpostextview(Panel *); +void plsetpostextview(Panel *, int); + +/* + * Idols + */ +Idol *plmkidol(Idol**, Image*, Image*, char*, void*); +void plfreeidol(Idol*); +Point plidolsize(Idol*, Font*, int); +void *plidollistgetsel(Panel*); + +/* + * Snarf + */ +void plputsnarf(char *); +char *plgetsnarf(void); +void plsnarf(Panel *); /* snarf a panel */ +void plpaste(Panel *); /* paste a panel */ diff --git a/sys/src/cmd/gopher/libpanel/panel.pdf b/sys/src/cmd/gopher/libpanel/panel.pdf new file mode 100644 index 000000000..7c84c14e0 Binary files /dev/null and b/sys/src/cmd/gopher/libpanel/panel.pdf differ diff --git a/sys/src/cmd/gopher/libpanel/pldefs.h b/sys/src/cmd/gopher/libpanel/pldefs.h new file mode 100644 index 000000000..786b5e8e3 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/pldefs.h @@ -0,0 +1,105 @@ +/* + * Definitions for internal use only + */ +/* + * Variable-font text routines + * These could make a separate library. + */ +Point pl_rtfmt(Rtext *, int); +void pl_rtdraw(Image *, Rectangle, Rtext *, Point); +void pl_rtredraw(Image *, Rectangle, Rtext *, Point, Point, int); +Rtext *pl_rthit(Rtext *, Point, Point, Point); +#define HITME 0x08000 /* tells ptinpanel not to look at children */ +#define LEAF 0x10000 /* newpanel will refuse to attach children */ +#define INVIS 0x20000 /* don't draw this */ +#define REMOUSE 0x40000 /* send next mouse event here, even if not inside */ +/* + * States, also styles + */ +enum{ + UP, + DOWN1, + DOWN2, + DOWN3, + DOWN, + PASSIVE, + FRAME +}; +/* + * Scroll flags + */ +enum{ + SCROLLUP, + SCROLLDOWN, + SCROLLABSY, + SCROLLLEFT, + SCROLLRIGHT, + SCROLLABSX, +}; +/* + * Scrollbar, slider orientations + */ +enum{ + HORIZ, + VERT +}; +Panel *pl_newpanel(Panel *, int); /* make a new Panel, given parent & data size */ +void *pl_emalloc(int); /* allocate some space, exit on error */ +void *pl_erealloc(void*,int); /* reallocate some space, exit on error */ +void pl_print(Panel *); /* print a Panel tree */ +Panel *pl_ptinpanel(Point, Panel *); /* highest-priority subpanel containing point */ +/* + * Drawing primitives + */ +int pl_drawinit(int); +Rectangle pl_box(Image *, Rectangle, int); +Rectangle pl_boxf(Image *b, Rectangle r, int flags, int style); +Rectangle pl_outline(Image *, Rectangle, int); +Point pl_boxsize(Point, int); +void pl_interior(int, Point *, Point *); +void pl_drawicon(Image *, Rectangle, int, int, Icon *); +Rectangle pl_check(Image *, Rectangle, int); +Rectangle pl_radio(Image *, Rectangle, int); +int pl_ckwid(void); +void pl_sliderupd(Image *, Rectangle, int, int, int); +void pl_invis(Panel *, int); +Point pl_iconsize(int, Icon *); +void pl_highlight(Image *, Rectangle); +void pl_clr(Image *, Rectangle); +void pl_fill(Image *, Rectangle); +void pl_cpy(Image *, Point, Rectangle); + +/* + * Rune mangling functions + */ +int pl_idchar(int); +int pl_rune1st(int); +char *pl_nextrune(char *); +int pl_runewidth(Font *, char *); +/* + * Fixed-font Text-window routines + * These could be separated out into a separate library. + */ +typedef struct Textwin Textwin; +struct Textwin{ + Rune *text, *etext, *eslack; /* text, with some slack off the end */ + int top, bot; /* range of runes visible on screen */ + int sel0, sel1; /* selection */ + Point *loc, *eloc; /* ul corners of visible runes (+1 more at end!) */ + Image *b; /* bitmap the text is drawn in */ + Rectangle r; /* rectangle the text is drawn in */ + Font *font; /* font text is drawn in */ + int hgt; /* same as font->height */ + int tabstop; /* tab settings are every tabstop pixels */ + int mintab; /* the minimum size of a tab */ +}; +Textwin *twnew(Image *, Font *, Rune *, int); +void twfree(Textwin *); +void twhilite(Textwin *, int, int, int); +void twselect(Textwin *, Mouse *); +void twreplace(Textwin *, int, int, Rune *, int); +void twscroll(Textwin *, int); +int twpt2rune(Textwin *, Point); +void twreshape(Textwin *, Rectangle); +void twmove(Textwin *, Point); +void plemove(Panel *, Point); diff --git a/sys/src/cmd/gopher/libpanel/popup.c b/sys/src/cmd/gopher/libpanel/popup.c new file mode 100644 index 000000000..ea3bde177 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/popup.c @@ -0,0 +1,116 @@ +/* + * popup + * looks like a group, except diverts hits on certain buttons to + * panels that it temporarily pops up. + */ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Popup Popup; +struct Popup{ + Image *save; /* where to save what the popup covers */ + Panel *pop[3]; /* what to pop up */ +}; +void pl_drawpopup(Panel *p){ + USED(p); +} +int pl_hitpopup(Panel *g, Mouse *m){ + Panel *p; + Point d; + Popup *pp; + + pp=g->data; + if(g->state==UP){ + switch(m->buttons&7){ + case 0: p=g->child; break; + case 1: p=pp->pop[0]; g->state=DOWN1; break; + case 2: p=pp->pop[1]; g->state=DOWN2; break; + case 4: p=pp->pop[2]; g->state=DOWN3; break; + default: p=0; break; + } + if(p==0){ + p=g->child; + g->state=DOWN; + } + else if(g->state!=UP){ + plpack(p, screen->clipr); + if(p->lastmouse) + d=subpt(m->xy, divpt(addpt(p->lastmouse->r.min, + p->lastmouse->r.max), 2)); + else + d=subpt(m->xy, divpt(addpt(p->r.min, p->r.max), 2)); + if(p->r.min.x+d.xr.min.x) d.x=g->r.min.x-p->r.min.x; + if(p->r.max.x+d.x>g->r.max.x) d.x=g->r.max.x-p->r.max.x; + if(p->r.min.y+d.yr.min.y) d.y=g->r.min.y-p->r.min.y; + if(p->r.max.y+d.y>g->r.max.y) d.y=g->r.max.y-p->r.max.y; + plmove(p, d); + pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill); + if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min); + pl_invis(p, 0); + pldraw(p, g->b); + } + } + else{ + switch(g->state){ + default: SET(p); break; /* can't happen! */ + case DOWN1: p=pp->pop[0]; break; + case DOWN2: p=pp->pop[1]; break; + case DOWN3: p=pp->pop[2]; break; + case DOWN: p=g->child; break; + } + if((m->buttons&7)==0){ + if(g->state!=DOWN){ + if(pp->save!=0){ + draw(g->b, p->r, pp->save, 0, p->r.min); + freeimage(pp->save); + pp->save=0; + } + pl_invis(p, 1); + } + g->state=UP; + } + } + plmouse(p, m); + if((m->buttons&7)==0) + g->state=UP; + return (m->buttons&7)!=0; +} +void pl_typepopup(Panel *g, Rune c){ + USED(g, c); +} +Point pl_getsizepopup(Panel *g, Point children){ + USED(g); + return children; +} +void pl_childspacepopup(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +int pl_pripopup(Panel *, Point){ + return PRI_POPUP; +} +void plinitpopup(Panel *v, int flags, Panel *pop0, Panel *pop1, Panel *pop2){ + Popup *pp; + pp=v->data; + v->flags=flags; + v->pri=pl_pripopup; + v->state=UP; + v->draw=pl_drawpopup; + v->hit=pl_hitpopup; + v->type=pl_typepopup; + v->getsize=pl_getsizepopup; + v->childspace=pl_childspacepopup; + pp->pop[0]=pop0; + pp->pop[1]=pop1; + pp->pop[2]=pop2; + pp->save=0; + v->kind="popup"; +} +Panel *plpopup(Panel *parent, int flags, Panel *pop0, Panel *pop1, Panel *pop2){ + Panel *v; + v=pl_newpanel(parent, sizeof(Popup)); + plinitpopup(v, flags, pop0, pop1, pop2); + return v; +} diff --git a/sys/src/cmd/gopher/libpanel/print.c b/sys/src/cmd/gopher/libpanel/print.c new file mode 100644 index 000000000..a3bd70210 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/print.c @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +void pl_iprint(int indent, char *fmt, ...){ + char buf[8192]; + va_list arg; + memset(buf, '\t', indent); + va_start(arg, fmt); + write(1, buf, vsnprint(buf+indent, sizeof(buf)-indent, fmt, arg)); + va_end(arg); +} +void pl_ipprint(Panel *p, int n){ + Panel *c; + char *place, *stick; + pl_iprint(n, "%s (0x%.8x)\n", p->kind, p); + pl_iprint(n, " r=(%d %d, %d %d)\n", + p->r.min.x, p->r.min.y, p->r.max.x, p->r.max.y); + switch(p->flags&PACK){ + default: SET(place); break; + case PACKN: place="n"; break; + case PACKE: place="e"; break; + case PACKS: place="s"; break; + case PACKW: place="w"; break; + } + switch(p->flags&PLACE){ + default: SET(stick); break; + case PLACECEN: stick=""; break; + case PLACES: stick=" stick s"; break; + case PLACEE: stick=" stick e"; break; + case PLACEW: stick=" stick w"; break; + case PLACEN: stick=" stick n"; break; + case PLACENE: stick=" stick ne"; break; + case PLACENW: stick=" stick nw"; break; + case PLACESE: stick=" stick se"; break; + case PLACESW: stick=" stick sw"; break; + } + pl_iprint(n, " place %s%s%s%s%s%s\n", + place, + p->flags&FILLX?" fill x":"", + p->flags&FILLY?" fill y":"", + stick, + p->flags&EXPAND?" expand":"", + p->flags&FIXED?" fixed":""); + if(!eqpt(p->pad, Pt(0, 0))) pl_iprint(n, " pad=%d,%d)\n", p->pad.x, p->pad.y); + if(!eqpt(p->ipad, Pt(0, 0))) pl_iprint(n, " ipad=%d,%d)\n", p->ipad.x, p->ipad.y); + pl_iprint(n, " size=(%d,%d), sizereq=(%d,%d)\n", + p->size.x, p->size.y, p->sizereq.x, p->sizereq.y); + for(c=p->child;c;c=c->next) + pl_ipprint(c, n+1); +} +void pl_print(Panel *p){ + pl_ipprint(p, 0); +} diff --git a/sys/src/cmd/gopher/libpanel/pulldown.c b/sys/src/cmd/gopher/libpanel/pulldown.c new file mode 100644 index 000000000..3b05d2f97 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/pulldown.c @@ -0,0 +1,160 @@ +/* + * pulldown + * makes a button that pops up a panel when hit + */ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Pulldown Pulldown; +struct Pulldown{ + Icon *icon; /* button label */ + Panel *pull; /* Panel to pull down */ + int side; /* which side of the button to put the panel on */ + Image *save; /* where to save what we draw the panel on */ +}; +void pl_drawpulldown(Panel *p){ + pl_drawicon(p->b, pl_box(p->b, p->r, p->state), PLACECEN, + p->flags, ((Pulldown *)p->data)->icon); +} +int pl_hitpulldown(Panel *g, Mouse *m){ + int oldstate, passon; + Rectangle r; + Panel *p, *hitme; + Pulldown *pp; + pp=g->data; + oldstate=g->state; + p=pp->pull; + hitme=0; + switch(g->state){ + case UP: + if(!ptinrect(m->xy, g->r)) + g->state=UP; + else if(m->buttons&7){ + r=g->b->r; + p->flags&=~PLACE; + switch(pp->side){ + case PACKN: + r.min.x=g->r.min.x; + r.max.y=g->r.min.y; + p->flags|=PLACESW; + break; + case PACKS: + r.min.x=g->r.min.x; + r.min.y=g->r.max.y; + p->flags|=PLACENW; + break; + case PACKE: + r.min.x=g->r.max.x; + r.min.y=g->r.min.y; + p->flags|=PLACENW; + break; + case PACKW: + r.max.x=g->r.min.x; + r.min.y=g->r.min.y; + p->flags|=PLACENE; + break; + case PACKCEN: + r.min=g->r.min; + p->flags|=PLACENW; + break; + } + plpack(p, r); + pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill); + if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min); + pl_invis(p, 0); + pldraw(p, g->b); + g->state=DOWN; + } + break; + case DOWN: + if(!ptinrect(m->xy, g->r)){ + switch(pp->side){ + default: SET(passon); break; /* doesn't happen */ + case PACKN: passon=m->xy.yr.min.y; break; + case PACKS: passon=m->xy.y>=g->r.max.y; break; + case PACKE: passon=m->xy.x>=g->r.max.x; break; + case PACKW: passon=m->xy.xr.min.x; break; + case PACKCEN: passon=1; break; + } + if(passon){ + hitme=p; + if((m->buttons&7)==0) g->state=UP; + } + else g->state=UP; + } + else if((m->buttons&7)==0) g->state=UP; + else hitme=p; + if(g->state!=DOWN && pp->save){ + draw(g->b, p->r, pp->save, 0, p->r.min); + freeimage(pp->save); + pp->save=0; + pl_invis(p, 1); + hitme=p; + } + } + if(g->state!=oldstate) pldraw(g, g->b); + if(hitme) plmouse(hitme, m); + return g->state==DOWN; +} +void pl_typepulldown(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizepulldown(Panel *p, Point children){ + USED(p, children); + return pl_boxsize(pl_iconsize(p->flags, ((Pulldown *)p->data)->icon), p->state); +} +void pl_childspacepulldown(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +void plinitpulldown(Panel *v, int flags, Icon *icon, Panel *pullthis, int side){ + Pulldown *pp; + pp=v->data; + v->flags=flags|LEAF; + v->draw=pl_drawpulldown; + v->hit=pl_hitpulldown; + v->type=pl_typepulldown; + v->getsize=pl_getsizepulldown; + v->childspace=pl_childspacepulldown; + pp->pull=pullthis; + pp->side=side; + pp->icon=icon; + v->kind="pulldown"; +} +Panel *plpulldown(Panel *parent, int flags, Icon *icon, Panel *pullthis, int side){ + Panel *v; + v=pl_newpanel(parent, sizeof(Pulldown)); + v->state=UP; + ((Pulldown *)v->data)->save=0; + plinitpulldown(v, flags, icon, pullthis, side); + return v; +} +Panel *plmenubar(Panel *parent, int flags, int cflags, Icon *l1, Panel *m1, Icon *l2, ...){ + Panel *v; + va_list arg; + Icon *s; + int pulldir; + switch(cflags&PACK){ + default: + SET(pulldir); + break; + case PACKE: + case PACKW: + pulldir=PACKS; + break; + case PACKN: + case PACKS: + pulldir=PACKE; + break; + } + v=plgroup(parent, flags); + va_start(arg, cflags); + while((s=va_arg(arg, Icon *))!=0) + plpulldown(v, cflags, s, va_arg(arg, Panel *), pulldir); + va_end(arg); + USED(l1, m1, l2); + v->kind="menubar"; + return v; +} diff --git a/sys/src/cmd/gopher/libpanel/rtext.c b/sys/src/cmd/gopher/libpanel/rtext.c new file mode 100644 index 000000000..7e4d0f367 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/rtext.c @@ -0,0 +1,383 @@ +/* + * Rich text with images. + * Should there be an offset field, to do subscripts & kerning? + */ +#include +#include +#include +#include +#include +#include "pldefs.h" +#include "rtext.h" + +#define LEAD 4 /* extra space between lines */ +#define BORD 2 /* extra border for images */ + +static Image *head, *blue; + +Rtext *pl_rtnew(Rtext **t, int space, int indent, int voff, Image *b, Panel *p, Font *f, char *s, int flags, void *user){ + Rtext *new; + new=pl_emalloc(sizeof(Rtext)); + new->flags=flags; + new->user=user; + new->space=space; + new->indent=indent; + new->voff=voff; + new->b=b; + new->p=p; + new->font=f; + new->text=s; + new->next=0; + new->nextline=0; + new->r=Rect(0,0,0,0); + if(*t) + (*t)->last->next=new; + else + *t=new; + (*t)->last=new; + return new; +} +Rtext *plrtpanel(Rtext **t, int space, int indent, int voff, Panel *p, void *user){ + return pl_rtnew(t, space, indent, voff, 0, p, 0, 0, 1, user); +} +Rtext *plrtstr(Rtext **t, int space, int indent, int voff, Font *f, char *s, int flags, void *user){ + return pl_rtnew(t, space, indent, voff, 0, 0, f, s, flags, user); +} +Rtext *plrtbitmap(Rtext **t, int space, int indent, int voff, Image *b, int flags, void *user){ + return pl_rtnew(t, space, indent, voff, b, 0, 0, 0, flags, user); +} +void plrtfree(Rtext *t){ + Rtext *next; + while(t){ + next=t->next; + free(t); + t=next; + } +} +int pl_tabmin, pl_tabsize; +void pltabsize(int min, int size){ + pl_tabmin=min; + pl_tabsize=size; +} +int pl_space(int space, int pos, int indent){ + if(space>=0) return space; + switch(PL_OP(space)){ + default: + return 0; + case PL_TAB: + return ((pos-indent+pl_tabmin)/pl_tabsize+PL_ARG(space))*pl_tabsize+indent-pos; + } +} +/* + * initialize rectangles & nextlines of text starting at t, + * galley width is wid. Returns the total width/height of the text + */ +Point pl_rtfmt(Rtext *t, int wid){ + Rtext *tp, *eline; + int ascent, descent, x, space, a, d, w, topy, indent, maxwid; + Point p; + + p=Pt(0,0); + eline=t; + maxwid=0; + while(t){ + ascent=0; + descent=0; + indent=space=pl_space(t->indent, 0, 0); + x=0; + tp=t; + for(;;){ + if(tp->b){ + a=tp->b->r.max.y-tp->b->r.min.y+BORD; + d=BORD; + w=tp->b->repl?wid-x:tp->b->r.max.x-tp->b->r.min.x+BORD*2; + } + else if(tp->p){ + /* what if plpack fails? */ + plpack(tp->p, Rect(0,0,wid,wid)); + plmove(tp->p, subpt(Pt(0,0), tp->p->r.min)); + a=tp->p->r.max.y-tp->p->r.min.y; + d=0; + w=tp->p->r.max.x-tp->p->r.min.x; + } + else{ + a=tp->font->ascent; + d=tp->font->height-a; + w=tp->wid=stringwidth(tp->font, tp->text); + } + a-=tp->voff,d+=tp->voff; + if(x+w+space>wid) break; + if(a>ascent) ascent=a; + if(d>descent) descent=d; + x+=w+space; + tp=tp->next; + if(tp==0){ + eline=0; + break; + } + space=pl_space(tp->space, x, indent); + if(space) eline=tp; + } + if(eline==t){ /* No progress! Force fit the first block! */ + if(tp==t){ + if(a>ascent) ascent=a; + if(d>descent) descent=d; + eline=tp->next; + }else + eline=tp; + } + topy=p.y; + p.y+=ascent; + p.x=indent=pl_space(t->indent, 0, 0); + for(;;){ + t->topy=topy; + t->r.min.x=p.x; + p.y+=t->voff; + if(t->b){ + t->r.max.y=p.y+BORD; + t->r.min.y=p.y-(t->b->r.max.y-t->b->r.min.y)-BORD; + p.x+=t->b->repl?wid-p.x:(t->b->r.max.x-t->b->r.min.x)+BORD*2; + } + else if(t->p){ + t->r.max.y=p.y; + t->r.min.y=p.y-t->p->r.max.y; + p.x+=t->p->r.max.x; + } + else{ + t->r.min.y=p.y-t->font->ascent; + t->r.max.y=t->r.min.y+t->font->height; + p.x+=t->wid; + } + p.y-=t->voff; + t->r.max.x=p.x; + t->nextline=eline; + t=t->next; + if(t==eline) break; + p.x+=pl_space(t->space, p.x, indent); + } + if(p.x>maxwid) maxwid=p.x; + p.y+=descent+LEAD; + } + return Pt(maxwid, p.y); +} + +/* + * If we draw the text in a backup bitmap and copy it onto the screen, + * the bitmap pointers in all the subpanels point to the wrong bitmap. + * This code fixes them. + */ +void pl_stuffbitmap(Panel *p, Image *b){ + p->b=b; + for(p=p->child;p;p=p->next) + pl_stuffbitmap(p, b); +} + +void pl_rtdraw(Image *b, Rectangle r, Rtext *t, Point offs){ + static Image *backup; + Point lp, sp; + Rectangle dr; + Image *bb; + + bb = b; + if(backup==0 || backup->chan!=b->chan || rectinrect(r, backup->r)==0){ + freeimage(backup); + backup=allocimage(display, bb->r, bb->chan, 0, DNofill); + } + if(backup) + b=backup; + pl_clr(b, r); + lp=ZP; + sp=ZP; + offs=subpt(r.min, offs); + for(;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){ + dr=rectaddpt(t->r, offs); + if(dr.max.y>r.min.y + && dr.min.yr.min.x + && dr.min.xb){ + draw(b, insetrect(dr, BORD), t->b, 0, t->b->r.min); + if(t->flags&PL_HOT) border(b, dr, 1, display->black, ZP); + if(t->flags&PL_STR) { + line(b, Pt(dr.min.x, dr.min.y), Pt(dr.max.x, dr.max.y), + Endsquare, Endsquare, 0, + display->black, ZP); + line(b, Pt(dr.min.x, dr.max.y), Pt(dr.max.x, dr.min.y), + Endsquare, Endsquare, 0, + display->black, ZP); + } + if(t->flags&PL_SEL) + pl_highlight(b, dr); + } + else if(t->p){ + plmove(t->p, subpt(dr.min, t->p->r.min)); + pldraw(t->p, b); + if(b!=bb) + pl_stuffbitmap(t->p, bb); + } + else{ + if(t->flags&PL_HEAD){ + if(head==nil) + head=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF); + string(b, dr.min, head, ZP, t->font, t->text); + }else if(t->flags&PL_HOT){ + if(blue==nil) + blue=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF); + string(b, dr.min, blue, ZP, t->font, t->text); + }else + string(b, dr.min, display->black, ZP, t->font, t->text); + if(t->flags&PL_SEL) + pl_highlight(b, dr); + if(t->flags&PL_STR){ + int y = dr.max.y - t->font->height/2; + if(sp.y != y) + sp = Pt(dr.min.x, y); + line(b, sp, Pt(dr.max.x, y), + Endsquare, Endsquare, 0, + display->black, ZP); + sp = Pt(dr.max.x, y); + } else + sp = ZP; + /*if(t->flags&PL_HOT){ + int y = dr.max.y - 1; + if(lp.y != y) + lp = Pt(dr.min.x, y); + line(b, lp, Pt(dr.max.x, y), + Endsquare, Endsquare, 0, + display->black, ZP); + lp = Pt(dr.max.x, y); + } else*/ + lp = ZP; + continue; + } + lp = ZP; + sp = ZP; + } + } + if(b!=bb) + draw(bb, r, b, 0, r.min); +} +/* + * Reposition text already drawn in the window. + * We just move the pixels and update the positions of any + * enclosed panels + */ +void pl_reposition(Rtext *t, Image *b, Point p, Rectangle r){ + Point offs; + pl_cpy(b, p, r); + offs=subpt(p, r.min); + for(;t;t=t->next) + if(!eqrect(t->r, Rect(0,0,0,0)) && !t->b && t->p) + plmove(t->p, offs); +} +/* + * Rectangle r of Image b contains an image of Rtext t, offset by oldoffs. + * Redraw the text to have offset yoffs. + */ +void pl_rtredraw(Image *b, Rectangle r, Rtext *t, Point offs, Point oldoffs, int dir){ + int d, size; + + if(dir==VERT){ + d=oldoffs.y-offs.y; + size=r.max.y-r.min.y; + if(d>=size || -d>=size) /* move more than screenful */ + pl_rtdraw(b, r, t, offs); + else if(d<0){ /* down */ + pl_reposition(t, b, r.min, + Rect(r.min.x, r.min.y-d, r.max.x, r.max.y)); + pl_rtdraw(b, Rect(r.min.x, r.max.y+d, r.max.x, r.max.y), + t, Pt(offs.x, offs.y+size+d)); + } + else if(d>0){ /* up */ + pl_reposition(t, b, Pt(r.min.x, r.min.y+d), + Rect(r.min.x, r.min.y, r.max.x, r.max.y-d)); + pl_rtdraw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+d), + t, offs); + } + }else{ /* dir==HORIZ */ + d=oldoffs.x-offs.x; + size=r.max.x-r.min.x; + if(d>=size || -d>=size) /* move more than screenful */ + pl_rtdraw(b, r, t, offs); + else if(d<0){ /* right */ + pl_reposition(t, b, r.min, + Rect(r.min.x-d, r.min.y, r.max.x, r.max.y)); + pl_rtdraw(b, Rect(r.max.x+d, r.min.y, r.max.x, r.max.y), + t, Pt(offs.x+size+d, offs.y)); + } + else if(d>0){ /* left */ + pl_reposition(t, b, Pt(r.min.x+d, r.min.y), + Rect(r.min.x, r.min.y, r.max.x-d, r.max.y)); + pl_rtdraw(b, Rect(r.min.x, r.min.y, r.min.x+d, r.max.y), + t, offs); + } + } +} +Rtext *pl_rthit(Rtext *t, Point offs, Point p, Point ul){ + Rectangle r; + Point lp; + if(t==0) return 0; + p.x+=offs.x-ul.x; + p.y+=offs.y-ul.y; + while(t->nextline && t->nextline->topy<=p.y) t=t->nextline; + lp=ZP; + for(;t!=0;t=t->next){ + if(t->topy>p.y) return 0; + r = t->r; + if((t->flags&PL_HOT) != 0 && t->b == nil && t->p == nil){ + if(lp.y == r.max.y && lp.x < r.min.x) + r.min.x=lp.x; + lp=r.max; + } else + lp=ZP; + if(ptinrect(p, r)) return t; + } + return 0; +} + +void plrtseltext(Rtext *t, Rtext *s, Rtext *e){ + while(t){ + t->flags &= ~PL_SEL; + t = t->next; + } + if(s==0 || e==0) + return; + for(t=s; t!=0 && t!=e; t=t->next) + ; + if(t==e){ + for(t=s; t!=e; t=t->next) + t->flags |= PL_SEL; + }else{ + for(t=e; t!=s; t=t->next) + t->flags |= PL_SEL; + } + t->flags |= PL_SEL; +} + +char *plrtsnarftext(Rtext *w){ + char *b, *p, *e, *t; + int n; + + b=p=e=0; + for(; w; w = w->next){ + if((w->flags&PL_SEL)==0 || w->text==0) + continue; + n = strlen(w->text)+64; + if(p+n >= e){ + n = (p+n+64)-b; + t = pl_erealloc(b, n); + p = t+(p-b); + e = t+n; + b = t; + } + if(w->space == 0) + p += sprint(p, "%s", w->text); + else if(w->space > 0) + p += sprint(p, " %s", w->text); + else if(PL_OP(w->space) == PL_TAB) + p += sprint(p, "\t%s", w->text); + if(w->nextline == w->next) + p += sprint(p, "\n"); + } + return b; +} diff --git a/sys/src/cmd/gopher/libpanel/rtext.h b/sys/src/cmd/gopher/libpanel/rtext.h new file mode 100644 index 000000000..78a4b0ce9 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/rtext.h @@ -0,0 +1,11 @@ +/* + * Rtext definitions + */ +#define PL_NOPBIT 4 +#define PL_NARGBIT 12 +#define PL_ARGMASK ((1< +#include +#include +#include +#include +Panel *root, *list; +char *genlist(Panel *, int which){ + static char buf[7]; + if(which<0 || 26<=which) return 0; + sprint(buf, "item %c", which+'a'); + return buf; +} +void hitgen(Panel *p, int buttons, int sel){ + USED(p, buttons, sel); +} +void ereshaped(Rectangle r){ + screen.r=r; + r=inset(r, 4); + plpack(root, r); + bitblt(&screen, screen.r.min, &screen, screen.r, Zero); + pldraw(root, &screen); +} +void done(Panel *p, int buttons){ + USED(p, buttons); + bitblt(&screen, screen.r.min, &screen, screen.r, Zero); + exits(0); +} +Panel *msg; +void message(char *s, ...){ + char buf[1024], *out; + va_list arg; + va_start(arg, s); + out = doprint(buf, buf+sizeof(buf), s, arg); + va_end(arg); + *out='\0'; + plinitlabel(msg, PACKN|FILLX, buf); + pldraw(msg, &screen); +} +Scroll s; +void save(Panel *p, int buttons){ + USED(p, buttons); + s=plgetscroll(list); + message("save %d %d %d %d", s); +} +void revert(Panel *p, int buttons){ + USED(p, buttons); + plsetscroll(list, s, &screen); + message("revert %d %d %d %d", s); +} +void main(void){ + Panel *g; + binit(0,0,0); + einit(Emouse); + plinit(screen.ldepth); + root=plgroup(0, 0); + g=plgroup(root, PACKN|EXPAND); + list=pllist(g, PACKE|EXPAND, genlist, 8, hitgen); + plscroll(list, 0, plscrollbar(g, PACKW)); + msg=pllabel(root, PACKN|FILLX, ""); + plbutton(root, PACKW, "save", save); + plbutton(root, PACKW, "revert", revert); + plbutton(root, PACKE, "done", done); + ereshaped(screen.r); + for(;;) plmouse(root, emouse(), &screen); +} diff --git a/sys/src/cmd/gopher/libpanel/scroll.c b/sys/src/cmd/gopher/libpanel/scroll.c new file mode 100644 index 000000000..2c8489e5b --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/scroll.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +void plscroll(Panel *scrollee, Panel *xscroller, Panel *yscroller){ + scrollee->xscroller=xscroller; + scrollee->yscroller=yscroller; + if(xscroller) xscroller->scrollee=scrollee; + if(yscroller) yscroller->scrollee=scrollee; +} +Scroll plgetscroll(Panel *p){ + return p->scr; +} +void plsetscroll(Panel *p, Scroll s){ + if(p->scroll){ + if(s.size.x) p->scroll(p, HORIZ, 2, s.pos.x, s.size.x); + if(s.size.y) p->scroll(p, VERT, 2, s.pos.y, s.size.y); + } +} diff --git a/sys/src/cmd/gopher/libpanel/scrollbar.c b/sys/src/cmd/gopher/libpanel/scrollbar.c new file mode 100644 index 000000000..d538f6879 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/scrollbar.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Scrollbar Scrollbar; +struct Scrollbar{ + int dir; /* HORIZ or VERT */ + int lo, hi; /* setting, in screen coordinates */ + int buttons; /* saved mouse buttons for transmittal to scrollee */ + Rectangle interior; + Point minsize; +}; +#define SBWID 8 /* should come from draw.c? */ +void pl_drawscrollbar(Panel *p){ + Scrollbar *sp; + sp=p->data; + sp->interior=pl_outline(p->b, p->r, p->state); + pl_sliderupd(p->b, sp->interior, sp->dir, sp->lo, sp->hi); +} +int pl_hitscrollbar(Panel *g, Mouse *m){ + int oldstate, pos, len, dy; + Point ul, size; + Scrollbar *sp; + sp=g->data; + ul=g->r.min; + size=subpt(g->r.max, g->r.min); + pl_interior(g->state, &ul, &size); + oldstate=g->state; + if(!(g->flags & USERFL) && (m->buttons&OUT || !ptinrect(m->xy, g->r))){ + m->buttons&=~OUT; + g->state=UP; + goto out; + } + if(sp->dir==HORIZ){ + pos=m->xy.x-ul.x; + len=size.x; + } + else{ + pos=m->xy.y-ul.y; + len=size.y; + } + if(pos<0) pos=0; + else if(pos>len) pos=len; + if(m->buttons&7){ + g->state=DOWN; + sp->buttons=m->buttons; + switch(m->buttons){ + case 1: + dy=pos*(sp->hi-sp->lo)/len; + pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo-dy, + sp->hi-dy); + break; + case 2: + if(g->scrollee && g->scrollee->scroll) + g->scrollee->scroll(g->scrollee, sp->dir, + m->buttons, pos, len); + break; + case 4: + dy=pos*(sp->hi-sp->lo)/len; + pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo+dy, + sp->hi+dy); + break; + } + } + else{ + if(!(sp->buttons&2) && g->state==DOWN && g->scrollee && g->scrollee->scroll) + g->scrollee->scroll(g->scrollee, sp->dir, sp->buttons, + pos, len); + g->state=UP; + } +out: + if(oldstate!=g->state) pldraw(g, g->b); + return g->state==DOWN; +} +void pl_typescrollbar(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizescrollbar(Panel *p, Point children){ + USED(children); + return pl_boxsize(((Scrollbar *)p->data)->minsize, p->state); +} +void pl_childspacescrollbar(Panel *p, Point *ul, Point *size){ + USED(p, ul, size); +} +/* + * Arguments lo, hi and len are in the scrollee's natural coordinates + */ +void pl_setscrollbarscrollbar(Panel *p, int lo, int hi, int len){ + Point ul, size; + int mylen; + Scrollbar *sp; + sp=p->data; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + mylen=sp->dir==HORIZ?size.x:size.y; + if(len==0) len=1; + sp->lo=lo*mylen/len; + sp->hi=hi*mylen/len; + if(sp->lo<0) sp->lo=0; + if(sp->lo>=mylen) sp->hi=mylen-1; + if(sp->hi<=sp->lo) sp->hi=sp->lo+1; + if(sp->hi>mylen) sp->hi=mylen; + pldraw(p, p->b); +} +int pl_priscrollbar(Panel *, Point){ + return PRI_SCROLLBAR; +} +void plinitscrollbar(Panel *v, int flags){ + Scrollbar *sp; + sp=v->data; + v->flags=flags|LEAF; + v->pri=pl_priscrollbar; + v->state=UP; + v->draw=pl_drawscrollbar; + v->hit=pl_hitscrollbar; + v->type=pl_typescrollbar; + v->getsize=pl_getsizescrollbar; + v->childspace=pl_childspacescrollbar; + v->setscrollbar=pl_setscrollbarscrollbar; + switch(flags&PACK){ + case PACKN: + case PACKS: + sp->dir=HORIZ; + sp->minsize=Pt(0, SBWID); + v->flags|=FILLX; + break; + case PACKE: + case PACKW: + sp->dir=VERT; + sp->minsize=Pt(SBWID, 0); + v->flags|=FILLY; + break; + } + sp->lo=0; + sp->hi=0; + v->kind="scrollbar"; +} +Panel *plscrollbar(Panel *parent, int flags){ + Panel *v; + v=pl_newpanel(parent, sizeof(Scrollbar)); + plinitscrollbar(v, flags); + return v; +} diff --git a/sys/src/cmd/gopher/libpanel/slider.c b/sys/src/cmd/gopher/libpanel/slider.c new file mode 100644 index 000000000..150a7feff --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/slider.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +typedef struct Slider Slider; +struct Slider{ + int dir; /* HORIZ or VERT */ + int val; /* setting, in screen coordinates */ + Point minsize; + void (*hit)(Panel *, int, int, int); /* call back to user when slider changes */ + int buttons; +}; +void pl_drawslider(Panel *p){ + Rectangle r; + Slider *sp; + sp=p->data; + r=pl_box(p->b, p->r, UP); + switch(sp->dir){ + case HORIZ: pl_sliderupd(p->b, r, sp->dir, 0, sp->val); break; + case VERT: pl_sliderupd(p->b, r, sp->dir, r.max.y-sp->val, r.max.y); break; + } +} +int pl_hitslider(Panel *p, Mouse *m){ + int oldstate, oldval, len; + Point ul, size; + Slider *sp; + sp=p->data; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + oldstate=p->state; + oldval=sp->val; + SET(len); + if(m->buttons&OUT) + p->state=UP; + else if(m->buttons&7){ + p->state=DOWN; + sp->buttons=m->buttons; + if(sp->dir==HORIZ){ + sp->val=m->xy.x-ul.x; + len=size.x; + } + else{ + sp->val=ul.y+size.y-m->xy.y; + len=size.y; + } + if(sp->val<0) sp->val=0; + else if(sp->val>len) sp->val=len; + } + else /* mouse inside, but no buttons down */ + p->state=UP; + if(oldval!=sp->val || oldstate!=p->state) pldraw(p, p->b); + if(oldval!=sp->val && sp->hit) sp->hit(p, sp->buttons, sp->val, len); + return 0; +} +void pl_typeslider(Panel *p, Rune c){ + USED(p, c); +} +Point pl_getsizeslider(Panel *p, Point children){ + USED(children); + return pl_boxsize(((Slider *)p->data)->minsize, p->state); +} +void pl_childspaceslider(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +void plinitslider(Panel *v, int flags, Point size, void (*hit)(Panel *, int, int, int)){ + Slider *sp; + sp=v->data; + v->r=Rect(0,0,size.x,size.y); + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawslider; + v->hit=pl_hitslider; + v->type=pl_typeslider; + v->getsize=pl_getsizeslider; + v->childspace=pl_childspaceslider; + sp->minsize=size; + sp->dir=size.x>size.y?HORIZ:VERT; + sp->hit=hit; + v->kind="slider"; +} +Panel *plslider(Panel *parent, int flags, Point size, void (*hit)(Panel *, int, int, int)){ + Panel *p; + p=pl_newpanel(parent, sizeof(Slider)); + plinitslider(p, flags, size, hit); + return p; +} +void plsetslider(Panel *p, int value, int range){ + Slider *sp; + sp=p->data; + if(value<0) value=0; + else if(value>range) value=range; + if(sp->dir==HORIZ) sp->val=value*(p->r.max.x-p->r.min.x)/range; + else sp->val=value*(p->r.max.y-p->r.min.y)/range; +} diff --git a/sys/src/cmd/gopher/libpanel/snarf.c b/sys/src/cmd/gopher/libpanel/snarf.c new file mode 100644 index 000000000..b11cb36f7 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/snarf.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" + +void plputsnarf(char *s){ + int fd; + + if(s==0 || *s=='\0') + return; + if((fd=open("/dev/snarf", OWRITE|OTRUNC))>=0){ + write(fd, s, strlen(s)); + close(fd); + } +} +char *plgetsnarf(void){ + int fd, n, r; + char *s; + + if((fd=open("/dev/snarf", OREAD))<0) + return nil; + n=0; + s=nil; + for(;;){ + s=pl_erealloc(s, n+1024); + if((r = read(fd, s+n, 1024)) <= 0) + break; + n += r; + } + close(fd); + if(n <= 0){ + free(s); + return nil; + } + s[n] = '\0'; + return s; +} +void plsnarf(Panel *p){ + char *s; + + if(p==0 || p->snarf==0) + return; + s=p->snarf(p); + plputsnarf(s); + free(s); +} +void plpaste(Panel *p){ + char *s; + + if(p==0 || p->paste==0) + return; + if(s=plgetsnarf()){ + p->paste(p, s); + free(s); + } +} diff --git a/sys/src/cmd/gopher/libpanel/textview.c b/sys/src/cmd/gopher/libpanel/textview.c new file mode 100644 index 000000000..6d561660e --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/textview.c @@ -0,0 +1,250 @@ +/* + * Fonted text viewer, calls out to code in rtext.c + * + * Should redo this to copy the already-visible parts on scrolling & only + * update the newly appearing stuff -- then the offscreen assembly bitmap can go away. + */ +#include +#include +#include +#include +#include +#include "pldefs.h" + +typedef struct Textview Textview; +struct Textview{ + void (*hit)(Panel *, int, Rtext *); /* call back to user on hit */ + Rtext *text; /* text */ + Point offs; /* offset of left/top of screen */ + Rtext *hitword; /* text to hilite */ + Rtext *hitfirst; /* first word in range select */ + int twid; /* text width (visible) */ + int thgt; /* text height (total) */ + int maxwid; /* width of longest line */ + Point minsize; /* smallest acceptible window size */ + int buttons; +}; + +void pl_setscrpos(Panel *p, Textview *tp, Rectangle r){ + Panel *sb; + int lo, hi; + + lo=tp->offs.y; + hi=lo+r.max.y-r.min.y; /* wrong? */ + sb=p->yscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, lo, hi, tp->thgt); + lo=tp->offs.x; + hi=lo+r.max.x-r.min.x; + sb=p->xscroller; + if(sb && sb->setscrollbar) + sb->setscrollbar(sb, lo, hi, tp->maxwid); +} +void pl_drawtextview(Panel *p){ + int twid; + Rectangle r; + Textview *tp; + Point size; + + tp=p->data; + r=pl_outline(p->b, p->r, UP); + twid=r.max.x-r.min.x; + if(twid!=tp->twid){ + tp->twid=twid; + size=pl_rtfmt(tp->text, tp->twid); + p->scr.size.x=tp->maxwid=size.x; + p->scr.size.y=tp->thgt=size.y; + } + p->scr.pos = tp->offs; + pl_rtdraw(p->b, r, tp->text, tp->offs); + pl_setscrpos(p, tp, r); +} +/* + * If t is a panel word, pass the mouse event on to it + */ +void pl_passon(Rtext *t, Mouse *m){ + if(t && t->b==0 && t->p!=0) + plmouse(t->p, m); +} +int pl_hittextview(Panel *p, Mouse *m){ + Rtext *oldhitword, *oldhitfirst; + int hitme, oldstate; + Point ul, size; + Textview *tp; + + tp=p->data; + hitme=0; + oldstate=p->state; + oldhitword=tp->hitword; + oldhitfirst=tp->hitfirst; + if(oldhitword==oldhitfirst) + pl_passon(oldhitword, m); + if(m->buttons&OUT) + p->state=UP; + else if(m->buttons&7){ + p->state=DOWN; + tp->buttons=m->buttons; + if(oldhitword==0 || oldhitword->p==0 || (oldhitword->p->flags&REMOUSE)==0){ + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + tp->hitword=pl_rthit(tp->text, tp->offs, m->xy, ul); + if(tp->hitword==0) + if(oldhitword!=0 && oldstate==DOWN) + tp->hitword=oldhitword; + else + tp->hitfirst=0; + if(tp->hitword!=0 && oldstate!=DOWN) + tp->hitfirst=tp->hitword; + } + } + else{ + if(p->state==DOWN) hitme=1; + p->state=UP; + } + if(tp->hitfirst!=oldhitfirst || tp->hitword!=oldhitword){ + plrtseltext(tp->text, tp->hitword, tp->hitfirst); + pl_drawtextview(p); + if(tp->hitword==tp->hitfirst) + pl_passon(tp->hitword, m); + } + if(hitme && tp->hit && tp->hitword!=0 && tp->hitword==tp->hitfirst){ + plrtseltext(tp->text, 0, 0); + pl_drawtextview(p); + tp->hit(p, tp->buttons, tp->hitword); + tp->hitword=0; + tp->hitfirst=0; + } + return 0; +} +void pl_scrolltextview(Panel *p, int dir, int buttons, int num, int den){ + int xoffs, yoffs; + Point ul, size; + Textview *tp; + Rectangle r; + + tp=p->data; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + if(dir==VERT){ + switch(buttons){ + default: + SET(yoffs); + break; + case 1: /* left -- top moves to pointer */ + yoffs=(vlong)tp->offs.y-num*size.y/den; + if(yoffs<0) yoffs=0; + break; + case 2: /* middle -- absolute index of file */ + yoffs=(vlong)tp->thgt*num/den; + break; + case 4: /* right -- line pointed at moves to top */ + yoffs=tp->offs.y+(vlong)num*size.y/den; + if(yoffs>tp->thgt) yoffs=tp->thgt; + break; + } + if(yoffs!=tp->offs.y){ + r=pl_outline(p->b, p->r, p->state); + pl_rtredraw(p->b, r, tp->text, + Pt(tp->offs.x, yoffs), tp->offs, dir); + p->scr.pos.y=tp->offs.y=yoffs; + pl_setscrpos(p, tp, r); + } + }else{ /* dir==HORIZ */ + switch(buttons){ + default: + SET(xoffs); + break; + case 1: /* left */ + xoffs=(vlong)tp->offs.x-num*size.x/den; + if(xoffs<0) xoffs=0; + break; + case 2: /* middle */ + xoffs=(vlong)tp->maxwid*num/den; + break; + case 4: /* right */ + xoffs=tp->offs.x+(vlong)num*size.x/den; + if(xoffs>tp->maxwid) xoffs=tp->maxwid; + break; + } + if(xoffs!=tp->offs.x){ + r=pl_outline(p->b, p->r, p->state); + pl_rtredraw(p->b, r, tp->text, + Pt(xoffs, tp->offs.y), tp->offs, dir); + p->scr.pos.x=tp->offs.x=xoffs; + pl_setscrpos(p, tp, r); + } + } +} +void pl_typetextview(Panel *g, Rune c){ + USED(g, c); +} +Point pl_getsizetextview(Panel *p, Point children){ + USED(children); + return pl_boxsize(((Textview *)p->data)->minsize, p->state); +} +void pl_childspacetextview(Panel *g, Point *ul, Point *size){ + USED(g, ul, size); +} +/* + * Priority depends on what thing inside the panel we're pointing at. + */ +int pl_pritextview(Panel *p, Point xy){ + Point ul, size; + Textview *tp; + Rtext *h; + tp=p->data; + ul=p->r.min; + size=subpt(p->r.max, p->r.min); + pl_interior(p->state, &ul, &size); + h=pl_rthit(tp->text, tp->offs, xy, ul); + if(h && h->b==0 && h->p!=0){ + p=pl_ptinpanel(xy, h->p); + if(p) return p->pri(p, xy); + } + return PRI_NORMAL; +} + +char* pl_snarftextview(Panel *p){ + return plrtsnarftext(((Textview *)p->data)->text); +} + +void plinittextview(Panel *v, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){ + Textview *tp; + tp=v->data; + v->flags=flags|LEAF; + v->state=UP; + v->draw=pl_drawtextview; + v->hit=pl_hittextview; + v->type=pl_typetextview; + v->getsize=pl_getsizetextview; + v->childspace=pl_childspacetextview; + v->kind="textview"; + v->pri=pl_pritextview; + tp->hit=hit; + tp->minsize=minsize; + tp->text=t; + tp->offs=ZP; + tp->hitfirst=0; + tp->hitword=0; + v->scroll=pl_scrolltextview; + v->snarf=pl_snarftextview; + tp->twid=-1; + tp->maxwid=0; + v->scr.pos=Pt(0,0); + v->scr.size=Pt(0,1); +} +Panel *pltextview(Panel *parent, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){ + Panel *v; + v=pl_newpanel(parent, sizeof(Textview)); + plinittextview(v, flags, minsize, t, hit); + return v; +} +int plgetpostextview(Panel *p){ + return ((Textview *)p->data)->offs.y; +} +void plsetpostextview(Panel *p, int yoffs){ + ((Textview *)p->data)->offs.y=yoffs; + pldraw(p, p->b); +} diff --git a/sys/src/cmd/gopher/libpanel/textwin.c b/sys/src/cmd/gopher/libpanel/textwin.c new file mode 100644 index 000000000..cf49b1e46 --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/textwin.c @@ -0,0 +1,474 @@ +/* + * Text windows + * void twhilite(Textwin *t, int sel0, int sel1, int on) + * hilite (on=1) or unhilite (on=0) a range of characters + * void twselect(Textwin *t, Mouse *m) + * set t->sel0, t->sel1 from mouse input. + * Also hilites selection. + * Caller should first unhilite previous selection. + * void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins) + * Replace the given range of characters with the given insertion. + * Caller should unhilite selection while this is called. + * void twscroll(Textwin *t, int top) + * Character with index top moves to the top line of the screen. + * int twpt2rune(Textwin *t, Point p) + * which character is displayed at point p? + * void twreshape(Textwin *t, Rectangle r) + * save r and redraw the text + * Textwin *twnew(Bitmap *b, Font *f, Rune *text, int ntext) + * create a new text window + * void twfree(Textwin *t) + * get rid of a surplus Textwin + */ +#include +#include +#include +#include +#include +#include "pldefs.h" + +#define SLACK 100 + +/* + * Is text at point a before or after that at point b? + */ +int tw_before(Textwin *t, Point a, Point b){ + return a.yhgt && a.xloc+(t->bot-t->top); + for(lp=t->loc;lp!=el;lp++) + if(tw_before(t, p, *lp)){ + if(lp==t->loc) return t->top; + return lp-t->loc+t->top-1; + } + return t->bot; +} +/* + * Return ul corner of the character with the given index + */ +Point tw_rune2pt(Textwin *t, int i){ + if(itop) return t->r.min; + if(i>t->bot) return t->r.max; + return t->loc[i-t->top]; +} +/* + * Store p at t->loc[l], extending t->loc if necessary + */ +void tw_storeloc(Textwin *t, int l, Point p){ + int nloc; + if(l>=t->eloc-t->loc){ + nloc=l+SLACK; + t->loc=pl_erealloc(t->loc, nloc*sizeof(Point)); + t->eloc=t->loc+nloc; + } + t->loc[l]=p; +} +/* + * Set the locations at which the given runes should appear. + * Returns the index of the first rune not set, which might not + * be last because we reached the bottom of the window. + * + * N.B. this zaps the loc of r[last], so that value should be saved first, + * if it's important. + */ +int tw_setloc(Textwin *t, int first, int last, Point ul){ + Rune *r, *er; + int x, dt, lp; + char buf[UTFmax+1]; + er=t->text+last; + for(r=t->text+first,lp=first-t->top;r!=er && ul.y+t->hgt<=t->r.max.y;r++,lp++){ + tw_storeloc(t, lp, ul); + switch(*r){ + case '\n': + ul.x=t->r.min.x; + ul.y+=t->hgt; + break; + case '\t': + x=ul.x-t->r.min.x+t->mintab+t->tabstop; + x-=x%t->tabstop; + ul.x=x+t->r.min.x; + if(ul.x>t->r.max.x){ + ul.x=t->r.min.x; + ul.y+=t->hgt; + tw_storeloc(t, lp, ul); + if(ul.y+t->hgt>t->r.max.y) return r-t->text; + ul.x+=+t->tabstop; + } + break; + default: + buf[runetochar(buf, r)]='\0'; + dt=stringwidth(t->font, buf); + ul.x+=dt; + if(ul.x>t->r.max.x){ + ul.x=t->r.min.x; + ul.y+=t->hgt; + tw_storeloc(t, lp, ul); + if(ul.y+t->hgt>t->r.max.y) return r-t->text; + ul.x+=dt; + } + break; + } + } + tw_storeloc(t, lp, ul); + return r-t->text; +} +/* + * Draw the given runes at their locations. + * Bug -- saving up multiple characters would + * reduce the number of calls to string, + * and probably make this a lot faster. + */ +void tw_draw(Textwin *t, int first, int last){ + Rune *r, *er; + Point *lp, ul, ur; + char buf[UTFmax+1]; + if(firsttop) first=t->top; + if(last>t->bot) last=t->bot; + if(last<=first) return; + er=t->text+last; + for(r=t->text+first,lp=t->loc+(first-t->top);r!=er;r++,lp++){ + if(lp->y+t->hgt>t->r.max.y){ + fprint(2, "chr %C, index %ld of %d, loc %d %d, off bottom\n", + *r, lp-t->loc, t->bot-t->top, lp->x, lp->y); + return; + } + switch(*r){ + case '\n': + ur=*lp; + break; + case '\t': + ur=*lp; + if(lp[1].y!=lp[0].y) + ul=Pt(t->r.min.x, lp[1].y); + else + ul=*lp; + pl_clr(t->b, Rpt(ul, Pt(lp[1].x, ul.y+t->hgt))); + break; + default: + buf[runetochar(buf, r)]='\0'; + /***/ pl_clr(t->b, Rpt(*lp, addpt(*lp, stringsize(t->font, buf)))); + ur=string(t->b, *lp, display->black, ZP, t->font, buf); + break; + } + if(lp[1].y!=lp[0].y) + /***/ pl_clr(t->b, Rpt(ur, Pt(t->r.max.x, ur.y+t->hgt))); + } +} +/* + * Hilight the characters with tops between ul and ur + */ +void tw_hilitep(Textwin *t, Point ul, Point ur){ + Point swap; + int y; + if(tw_before(t, ur, ul)){ swap=ul; ul=ur; ur=swap;} + y=ul.y+t->hgt; + if(y>t->r.max.y) y=t->r.max.y; + if(ul.y==ur.y) + pl_highlight(t->b, Rpt(ul, Pt(ur.x, y))); + else{ + pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, y))); + ul=Pt(t->r.min.x, y); + pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, ur.y))); + ul=Pt(t->r.min.x, ur.y); + y=ur.y+t->hgt; + if(y>t->r.max.y) y=t->r.max.y; + pl_highlight(t->b, Rpt(ul, Pt(ur.x, y))); + } +} +/* + * Hilite/unhilite the given range of characters + */ +void twhilite(Textwin *t, int sel0, int sel1, int on){ + Point ul, ur; + int swap, y; + if(sel1top || t->bottop) sel0=t->top; + if(sel1>t->bot) sel1=t->bot; + if(!on){ + if(sel1==sel0){ + ul=t->loc[sel0-t->top]; + y=ul.y+t->hgt; + if(y>t->r.max.y) y=t->r.max.y; + pl_clr(t->b, Rpt(ul, Pt(ul.x+1, y))); + }else + tw_draw(t, sel0, sel1); + return; + } + ul=t->loc[sel0-t->top]; + if(sel1==sel0) + ur=addpt(ul, Pt(1, 0)); + else + ur=t->loc[sel1-t->top]; + tw_hilitep(t, ul, ur); +} +/* + * Set t->sel[01] from mouse input. + * Also hilites the selection. + * Caller should unhilite the previous + * selection before calling this. + */ +void twselect(Textwin *t, Mouse *m){ + int sel0, sel1, newsel; + Point p0, p1, newp; + sel0=sel1=twpt2rune(t, m->xy); + p0=tw_rune2pt(t, sel0); + p1=addpt(p0, Pt(1, 0)); + twhilite(t, sel0, sel1, 1); + for(;;){ + if(display->bufp > display->buf) + flushimage(display, 1); + *m=emouse(); + if((m->buttons&7)!=1) break; + newsel=twpt2rune(t, m->xy); + newp=tw_rune2pt(t, newsel); + if(eqpt(newp, p0)) newp=addpt(newp, Pt(1, 0)); + if(!eqpt(newp, p1)){ + if((sel0<=sel1 && sel1sel0=sel0; + t->sel1=sel1; + } + else{ + t->sel0=sel1; + t->sel1=sel0; + } +} +/* + * Clear the area following the last displayed character + */ +void tw_clrend(Textwin *t){ + Point ul; + int y; + ul=t->loc[t->bot-t->top]; + y=ul.y+t->hgt; + if(y>t->r.max.y) y=t->r.max.y; + pl_clr(t->b, Rpt(ul, Pt(t->r.max.x, y))); + ul=Pt(t->r.min.x, y); + pl_clr(t->b, Rpt(ul, t->r.max)); +} +/* + * Move part of a line of text, truncating the source or padding + * the destination on the right if necessary. + */ +void tw_moverect(Textwin *t, Point uld, Point urd, Point uls, Point urs){ + int sw, dw, d; + if(urs.y!=uls.y) urs=Pt(t->r.max.x, uls.y); + if(urd.y!=uld.y) urd=Pt(t->r.max.x, uld.y); + sw=uls.x-urs.x; + dw=uld.x-urd.x; + if(dw>sw){ + d=dw-sw; + pl_clr(t->b, Rect(urd.x-d, urd.y, urd.x, urd.y+t->hgt)); + dw=sw; + } + pl_cpy(t->b, uld, Rpt(uls, Pt(uls.x+dw, uls.y+t->hgt))); +} +/* + * Move a block of characters up or to the left: + * Identify contiguous runs of characters whose width doesn't change, and + * move them in one bitblt per run. + * If we get to a point where source and destination are x-aligned, + * they will remain x-aligned for the rest of the block. + * Then, if they are y-aligned, they're already in the right place. + * Otherwise, we can move them in three bitblts; one if all the + * remaining characters are on one line. + */ +void tw_moveup(Textwin *t, Point *dp, Point *sp, Point *esp){ + Point uld, uls; /* upper left of destination/source */ + int y; + while(sp!=esp && sp->x!=dp->x){ + uld=*dp; + uls=*sp; + while(sp!=esp && sp->y==uls.y && dp->y==uld.y && sp->x-uls.x==dp->x-uld.x){ + sp++; + dp++; + } + tw_moverect(t, uld, *dp, uls, *sp); + } + if(sp==esp || esp->y==dp->y) return; + if(esp->y==sp->y){ /* one line only */ + pl_cpy(t->b, *dp, Rpt(*sp, Pt(esp->x, sp->y+t->hgt))); + return; + } + y=sp->y+t->hgt; + pl_cpy(t->b, *dp, Rpt(*sp, Pt(t->r.max.x, y))); + pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt), + Rect(t->r.min.x, y, t->r.max.x, esp->y)); + y=dp->y+esp->y-sp->y; + pl_cpy(t->b, Pt(t->r.min.x, y), + Rect(t->r.min.x, esp->y, esp->x, esp->y+t->hgt)); +} +/* + * Same as above, but moving down and in reverse order, so as not to overwrite stuff + * not moved yet. + */ +void tw_movedn(Textwin *t, Point *dp, Point *bsp, Point *esp){ + Point *sp, urs, urd; + int dy; + dp+=esp-bsp; + sp=esp; + dy=dp->y-sp->y; + while(sp!=bsp && dp[-1].x==sp[-1].x){ + --dp; + --sp; + } + if(dy!=0){ + if(sp->y==esp->y) + pl_cpy(t->b, *dp, Rect(sp->x, sp->y, esp->x, esp->y+t->hgt)); + else{ + pl_cpy(t->b, Pt(t->r.min.x, sp->x+dy), + Rect(t->r.min.x, sp->y, esp->x, esp->y+t->hgt)); + pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt), + Rect(t->r.min.x, sp->y+t->hgt, t->r.max.x, esp->y)); + pl_cpy(t->b, *dp, + Rect(sp->x, sp->y, t->r.max.x, sp->y+t->hgt)); + } + } + while(sp!=bsp){ + urd=*dp; + urs=*sp; + while(sp!=bsp && sp[-1].y==sp[0].y && dp[-1].y==dp[0].y + && sp[-1].x-sp[0].x==dp[-1].x-dp[0].x){ + --sp; + --dp; + } + tw_moverect(t, *dp, urd, *sp, urs); + } +} +/* + * Move the given range of characters, already drawn on + * the given textwin, to the given location. + * Start and end must both index characters that are initially on-screen. + */ +void tw_relocate(Textwin *t, int first, int last, Point dst){ + Point *srcloc; + int nbyte; + if(firsttop || lastbotloc[first-t->top], nbyte); + tw_setloc(t, first, last, dst); + if(tw_before(t, dst, srcloc[0])) + tw_moveup(t, t->loc+first-t->top, srcloc, srcloc+(last-first)); + else + tw_movedn(t, t->loc+first-t->top, srcloc, srcloc+(last-first)); +} +/* + * Replace the runes with indices from r0 to r1-1 with the text + * pointed to by text, and with length ntext. + * Open up a hole in t->text, t->loc. + * Insert new text, calculate their locs (save the extra loc that's overwritten first) + * (swap saved & overwritten locs) + * move tail. + * calc locs and draw new text after tail, if necessary. + * draw new text, if necessary + */ +void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins){ + int olen, nlen, tlen, dtop; + olen=t->etext-t->text; + nlen=olen+nins-(r1-r0); + tlen=t->eslack-t->text; + if(nlen>tlen){ + tlen=nlen+SLACK; + t->text=pl_erealloc(t->text, tlen*sizeof(Rune)); + t->eslack=t->text+tlen; + } + if(olen!=nlen) + memmove(t->text+r0+nins, t->text+r1, (olen-r1)*sizeof(Rune)); + if(nins!=0) /* ins can be 0 if nins==0 */ + memmove(t->text+r0, ins, nins*sizeof(Rune)); + t->etext=t->text+nlen; + if(r0>t->bot) /* insertion is completely below visible text */ + return; + if(r1top){ /* insertion is completely above visible text */ + dtop=nlen-olen; + t->top+=dtop; + t->bot+=dtop; + return; + } + if(1 || t->bot<=r0+nins){ /* no useful text on screen below r0 */ + if(r0<=t->top) /* no useful text above, either */ + t->top=r0; + t->bot=tw_setloc(t, r0, nlen, t->loc[r0-t->top]); + tw_draw(t, r0, t->bot); + tw_clrend(t); + return; + } + /* + * code for case where there is useful text below is missing (see `1 ||' above) + */ +} +/* + * This works but is stupid. + */ +void twscroll(Textwin *t, int top){ + while(top!=0 && t->text[top-1]!='\n') --top; + t->top=top; + t->bot=tw_setloc(t, top, t->etext-t->text, t->r.min); + tw_draw(t, t->top, t->bot); + tw_clrend(t); +} +void twreshape(Textwin *t, Rectangle r){ + t->r=r; + t->bot=tw_setloc(t, t->top, t->etext-t->text, t->r.min); + tw_draw(t, t->top, t->bot); + tw_clrend(t); +} +Textwin *twnew(Image *b, Font *f, Rune *text, int ntext){ + Textwin *t; + t=pl_emalloc(sizeof(Textwin)); + t->text=pl_emalloc((ntext+SLACK)*sizeof(Rune)); + t->loc=pl_emalloc(SLACK*sizeof(Point)); + t->eloc=t->loc+SLACK; + t->etext=t->text+ntext; + t->eslack=t->etext+SLACK; + if(ntext) memmove(t->text, text, ntext*sizeof(Rune)); + t->top=0; + t->bot=0; + t->sel0=0; + t->sel1=0; + t->b=b; + t->font=f; + t->hgt=f->height; + t->mintab=stringwidth(f, "0"); + t->tabstop=8*t->mintab; + return t; +} +void twfree(Textwin *t){ + free(t->loc); + free(t->text); + free(t); +} +/* + * Correct the character locations in a textwin after the panel is moved. + * This horrid hack would not be necessary if loc values were relative + * to the panel, rather than absolute. + */ +void twmove(Textwin *t, Point d){ + Point *lp; + t->r = rectaddpt(t->r, d); + for(lp=t->loc; lpeloc; lp++) + *lp = addpt(*lp, d); +} diff --git a/sys/src/cmd/gopher/libpanel/utf.c b/sys/src/cmd/gopher/libpanel/utf.c new file mode 100644 index 000000000..30cb221cb --- /dev/null +++ b/sys/src/cmd/gopher/libpanel/utf.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include +#include "pldefs.h" +/* + * This is the same definition that 8½ uses + */ +int pl_idchar(int c){ + if(c<=' ' + || 0x7F<=c && c<=0xA0 + || utfrune("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c)) + return 0; + return 1; +} +int pl_rune1st(int c){ + return (c&0xc0)!=0x80; +} +char *pl_nextrune(char *s){ + do s++; while(!pl_rune1st(*s)); + return s; +} +int pl_runewidth(Font *f, char *s){ + char r[4], *t; + t=r; + do *t++=*s++; while(!pl_rune1st(*s)); + *t='\0'; + return stringwidth(f, r); +} diff --git a/sys/src/cmd/gopher/mkfile b/sys/src/cmd/gopher/mkfile new file mode 100644 index 000000000..014bcf78b --- /dev/null +++ b/sys/src/cmd/gopher/mkfile @@ -0,0 +1,19 @@ +