├── gopher.png ├── libpanel ├── panel.pdf ├── init.c ├── rtext.h ├── mkfile ├── scroll.c ├── utf.c ├── group.c ├── frame.c ├── snarf.c ├── event.c ├── canvas.c ├── label.c ├── scrltest.c ├── print.c ├── slider.c ├── message.c ├── mem.c ├── popup.c ├── pldefs.h ├── scrollbar.c ├── pulldown.c ├── entry.c ├── pack.c ├── button.c ├── list.c ├── textview.c ├── edit.c ├── panel.h ├── rtext.c ├── draw.c └── textwin.c ├── mkfile ├── dat.h ├── README.md ├── icons.h └── gopher.c /gopher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telephil9/gopher/HEAD/gopher.png -------------------------------------------------------------------------------- /libpanel/panel.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/telephil9/gopher/HEAD/libpanel/panel.pdf -------------------------------------------------------------------------------- /libpanel/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | /* 8 | * Just a wrapper for all the initialization routines 9 | */ 10 | int plinit(int ldepth){ 11 | if(!pl_drawinit(ldepth)) return 0; 12 | return 1; 13 | } 14 | -------------------------------------------------------------------------------- /mkfile: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | void plscroll(Panel *scrollee, Panel *xscroller, Panel *yscroller){ 8 | scrollee->xscroller=xscroller; 9 | scrollee->yscroller=yscroller; 10 | if(xscroller) xscroller->scrollee=scrollee; 11 | if(yscroller) yscroller->scrollee=scrollee; 12 | } 13 | Scroll plgetscroll(Panel *p){ 14 | return p->scr; 15 | } 16 | void plsetscroll(Panel *p, Scroll s){ 17 | if(p->scroll){ 18 | if(s.size.x) p->scroll(p, HORIZ, 2, s.pos.x, s.size.x); 19 | if(s.size.y) p->scroll(p, VERT, 2, s.pos.y, s.size.y); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /libpanel/utf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | /* 8 | * This is the same definition that 8½ uses 9 | */ 10 | int pl_idchar(int c){ 11 | if(c<=' ' 12 | || 0x7F<=c && c<=0xA0 13 | || utfrune("!\"#$%&'()*+,-./:;<=>?@`[\\]^{|}~", c)) 14 | return 0; 15 | return 1; 16 | } 17 | int pl_rune1st(int c){ 18 | return (c&0xc0)!=0x80; 19 | } 20 | char *pl_nextrune(char *s){ 21 | do s++; while(!pl_rune1st(*s)); 22 | return s; 23 | } 24 | int pl_runewidth(Font *f, char *s){ 25 | char r[4], *t; 26 | t=r; 27 | do *t++=*s++; while(!pl_rune1st(*s)); 28 | *t='\0'; 29 | return stringwidth(f, r); 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gopher 2 | ======= 3 | A mostly functional gopher browser for plan9. 4 | 5 | ![gopher](gopher.png) 6 | 7 | Most gopher item types are handled: 8 | - Text and submenu items are displayed within the browser 9 | - Images and documents are opened through page(1) 10 | - HTML items are sent to the plumber(4) 11 | - Binary items (bin, dos, uuencoded files and sound) are downloaded to disk 12 | - Other types are not handled (e.g. telnet) 13 | 14 | Following keyboard shortcuts are available: 15 | - b: previous page in history 16 | - n: next page in history 17 | - q: quit 18 | 19 | This has not been thoroughly tested so many bugs are just waiting to be found. 20 | 21 | Usage: 22 | ------ 23 | Install with ``mk install`` 24 | Run with ``gopher [address]`` 25 | 26 | -------------------------------------------------------------------------------- /libpanel/group.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | void pl_drawgroup(Panel *p){ 8 | USED(p); 9 | } 10 | int pl_hitgroup(Panel *p, Mouse *m){ 11 | USED(p, m); 12 | return 0; 13 | } 14 | void pl_typegroup(Panel *p, Rune c){ 15 | USED(p, c); 16 | } 17 | Point pl_getsizegroup(Panel *p, Point children){ 18 | USED(p); 19 | return children; 20 | } 21 | void pl_childspacegroup(Panel *p, Point *ul, Point *size){ 22 | USED(p, ul, size); 23 | } 24 | void plinitgroup(Panel *v, int flags){ 25 | v->flags=flags; 26 | v->draw=pl_drawgroup; 27 | v->hit=pl_hitgroup; 28 | v->type=pl_typegroup; 29 | v->getsize=pl_getsizegroup; 30 | v->childspace=pl_childspacegroup; 31 | v->kind="group"; 32 | } 33 | Panel *plgroup(Panel *parent, int flags){ 34 | Panel *p; 35 | p=pl_newpanel(parent, 0); 36 | plinitgroup(p, flags); 37 | return p; 38 | } 39 | -------------------------------------------------------------------------------- /libpanel/frame.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | void pl_drawframe(Panel *p){ 8 | pl_box(p->b, p->r, FRAME); 9 | } 10 | int pl_hitframe(Panel *p, Mouse *m){ 11 | USED(p, m); 12 | return 0; 13 | } 14 | void pl_typeframe(Panel *p, Rune c){ 15 | USED(p, c); 16 | } 17 | Point pl_getsizeframe(Panel *p, Point children){ 18 | USED(p); 19 | return pl_boxsize(children, FRAME); 20 | } 21 | void pl_childspaceframe(Panel *p, Point *ul, Point *size){ 22 | USED(p); 23 | pl_interior(FRAME, ul, size); 24 | } 25 | void plinitframe(Panel *v, int flags){ 26 | v->flags=flags; 27 | v->draw=pl_drawframe; 28 | v->hit=pl_hitframe; 29 | v->type=pl_typeframe; 30 | v->getsize=pl_getsizeframe; 31 | v->childspace=pl_childspaceframe; 32 | v->kind="frame"; 33 | } 34 | Panel *plframe(Panel *parent, int flags){ 35 | Panel *p; 36 | p=pl_newpanel(parent, 0); 37 | plinitframe(p, flags); 38 | return p; 39 | } 40 | -------------------------------------------------------------------------------- /libpanel/snarf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | 8 | void plputsnarf(char *s){ 9 | int fd; 10 | 11 | if(s==0 || *s=='\0') 12 | return; 13 | if((fd=open("/dev/snarf", OWRITE|OTRUNC))>=0){ 14 | write(fd, s, strlen(s)); 15 | close(fd); 16 | } 17 | } 18 | char *plgetsnarf(void){ 19 | int fd, n, r; 20 | char *s; 21 | 22 | if((fd=open("/dev/snarf", OREAD))<0) 23 | return nil; 24 | n=0; 25 | s=nil; 26 | for(;;){ 27 | s=pl_erealloc(s, n+1024); 28 | if((r = read(fd, s+n, 1024)) <= 0) 29 | break; 30 | n += r; 31 | } 32 | close(fd); 33 | if(n <= 0){ 34 | free(s); 35 | return nil; 36 | } 37 | s[n] = '\0'; 38 | return s; 39 | } 40 | void plsnarf(Panel *p){ 41 | char *s; 42 | 43 | if(p==0 || p->snarf==0) 44 | return; 45 | s=p->snarf(p); 46 | plputsnarf(s); 47 | free(s); 48 | } 49 | void plpaste(Panel *p){ 50 | char *s; 51 | 52 | if(p==0 || p->paste==0) 53 | return; 54 | if(s=plgetsnarf()){ 55 | p->paste(p, s); 56 | free(s); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /libpanel/event.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | 8 | void plgrabkb(Panel *g){ 9 | plkbfocus=g; 10 | } 11 | void plkeyboard(Rune c){ 12 | if(plkbfocus) 13 | plkbfocus->type(plkbfocus, c); 14 | } 15 | 16 | /* 17 | * Return the most leafward, highest priority panel containing p 18 | */ 19 | Panel *pl_ptinpanel(Point p, Panel *g){ 20 | Panel *v; 21 | for(;g;g=g->next) if(ptinrect(p, g->r)){ 22 | v=pl_ptinpanel(p, g->child); 23 | if(v && v->pri(v, p)>=g->pri(g, p)) return v; 24 | return g; 25 | } 26 | return 0; 27 | } 28 | void plmouse(Panel *g, Mouse *m){ 29 | Panel *hit, *last; 30 | if(g->flags&REMOUSE) 31 | hit=g->lastmouse; 32 | else{ 33 | hit=pl_ptinpanel(m->xy, g); 34 | last=g->lastmouse; 35 | if(last && last!=hit){ 36 | m->buttons|=OUT; 37 | last->hit(last, m); 38 | m->buttons&=~OUT; 39 | } 40 | } 41 | if(hit){ 42 | if(hit->hit(hit, m)) 43 | g->flags|=REMOUSE; 44 | else 45 | g->flags&=~REMOUSE; 46 | g->lastmouse=hit; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /libpanel/canvas.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Canvas Canvas; 8 | struct Canvas{ 9 | void (*draw)(Panel *); 10 | void (*hit)(Panel *, Mouse *); 11 | }; 12 | void pl_drawcanvas(Panel *p){ 13 | Canvas *c; 14 | c=p->data; 15 | if(c->draw) c->draw(p); 16 | } 17 | int pl_hitcanvas(Panel *p, Mouse *m){ 18 | Canvas *c; 19 | c=p->data; 20 | if(c->hit) c->hit(p, m); 21 | return 0; 22 | } 23 | void pl_typecanvas(Panel *p, Rune c){ 24 | USED(p, c); 25 | } 26 | Point pl_getsizecanvas(Panel *p, Point children){ 27 | USED(p, children); 28 | return Pt(0,0); 29 | } 30 | void pl_childspacecanvas(Panel *p, Point *ul, Point *size){ 31 | USED(p, ul, size); 32 | } 33 | void plinitcanvas(Panel *v, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){ 34 | Canvas *c; 35 | v->flags=flags|LEAF; 36 | v->draw=pl_drawcanvas; 37 | v->hit=pl_hitcanvas; 38 | v->type=pl_typecanvas; 39 | v->getsize=pl_getsizecanvas; 40 | v->childspace=pl_childspacecanvas; 41 | v->kind="canvas"; 42 | c=v->data; 43 | c->draw=draw; 44 | c->hit=hit; 45 | } 46 | Panel *plcanvas(Panel *parent, int flags, void (*draw)(Panel *), void (*hit)(Panel *, Mouse *)){ 47 | Panel *p; 48 | p=pl_newpanel(parent, sizeof(Canvas)); 49 | plinitcanvas(p, flags, draw, hit); 50 | return p; 51 | } 52 | -------------------------------------------------------------------------------- /libpanel/label.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Label Label; 8 | struct Label{ 9 | int placement; 10 | Icon *icon; 11 | }; 12 | void pl_drawlabel(Panel *p){ 13 | Label *l; 14 | l=p->data; 15 | pl_drawicon(p->b, pl_box(p->b, p->r, PASSIVE), l->placement, p->flags, l->icon); 16 | } 17 | int pl_hitlabel(Panel *p, Mouse *m){ 18 | USED(p, m); 19 | return 0; 20 | } 21 | void pl_typelabel(Panel *p, Rune c){ 22 | USED(p, c); 23 | } 24 | Point pl_getsizelabel(Panel *p, Point children){ 25 | USED(children); /* shouldn't have any children */ 26 | return pl_boxsize(pl_iconsize(p->flags, ((Label *)p->data)->icon), PASSIVE); 27 | } 28 | void pl_childspacelabel(Panel *g, Point *ul, Point *size){ 29 | USED(g, ul, size); 30 | } 31 | void plinitlabel(Panel *v, int flags, Icon *icon){ 32 | v->flags=flags|LEAF; 33 | ((Label *)(v->data))->icon=icon; 34 | v->draw=pl_drawlabel; 35 | v->hit=pl_hitlabel; 36 | v->type=pl_typelabel; 37 | v->getsize=pl_getsizelabel; 38 | v->childspace=pl_childspacelabel; 39 | v->kind="label"; 40 | } 41 | Panel *pllabel(Panel *parent, int flags, Icon *icon){ 42 | Panel *p; 43 | p=pl_newpanel(parent, sizeof(Label)); 44 | plinitlabel(p, flags, icon); 45 | plplacelabel(p, PLACECEN); 46 | return p; 47 | } 48 | void plplacelabel(Panel *p, int placement){ 49 | ((Label *)(p->data))->placement=placement; 50 | } 51 | -------------------------------------------------------------------------------- /libpanel/scrltest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | Panel *root, *list; 7 | char *genlist(Panel *, int which){ 8 | static char buf[7]; 9 | if(which<0 || 26<=which) return 0; 10 | sprint(buf, "item %c", which+'a'); 11 | return buf; 12 | } 13 | void hitgen(Panel *p, int buttons, int sel){ 14 | USED(p, buttons, sel); 15 | } 16 | void ereshaped(Rectangle r){ 17 | screen.r=r; 18 | r=inset(r, 4); 19 | plpack(root, r); 20 | bitblt(&screen, screen.r.min, &screen, screen.r, Zero); 21 | pldraw(root, &screen); 22 | } 23 | void done(Panel *p, int buttons){ 24 | USED(p, buttons); 25 | bitblt(&screen, screen.r.min, &screen, screen.r, Zero); 26 | exits(0); 27 | } 28 | Panel *msg; 29 | void message(char *s, ...){ 30 | char buf[1024], *out; 31 | va_list arg; 32 | va_start(arg, s); 33 | out = doprint(buf, buf+sizeof(buf), s, arg); 34 | va_end(arg); 35 | *out='\0'; 36 | plinitlabel(msg, PACKN|FILLX, buf); 37 | pldraw(msg, &screen); 38 | } 39 | Scroll s; 40 | void save(Panel *p, int buttons){ 41 | USED(p, buttons); 42 | s=plgetscroll(list); 43 | message("save %d %d %d %d", s); 44 | } 45 | void revert(Panel *p, int buttons){ 46 | USED(p, buttons); 47 | plsetscroll(list, s, &screen); 48 | message("revert %d %d %d %d", s); 49 | } 50 | void main(void){ 51 | Panel *g; 52 | binit(0,0,0); 53 | einit(Emouse); 54 | plinit(screen.ldepth); 55 | root=plgroup(0, 0); 56 | g=plgroup(root, PACKN|EXPAND); 57 | list=pllist(g, PACKE|EXPAND, genlist, 8, hitgen); 58 | plscroll(list, 0, plscrollbar(g, PACKW)); 59 | msg=pllabel(root, PACKN|FILLX, ""); 60 | plbutton(root, PACKW, "save", save); 61 | plbutton(root, PACKW, "revert", revert); 62 | plbutton(root, PACKE, "done", done); 63 | ereshaped(screen.r); 64 | for(;;) plmouse(root, emouse(), &screen); 65 | } 66 | -------------------------------------------------------------------------------- /libpanel/print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | void pl_iprint(int indent, char *fmt, ...){ 8 | char buf[8192]; 9 | va_list arg; 10 | memset(buf, '\t', indent); 11 | va_start(arg, fmt); 12 | write(1, buf, vsnprint(buf+indent, sizeof(buf)-indent, fmt, arg)); 13 | va_end(arg); 14 | } 15 | void pl_ipprint(Panel *p, int n){ 16 | Panel *c; 17 | char *place, *stick; 18 | pl_iprint(n, "%s (0x%.8x)\n", p->kind, p); 19 | pl_iprint(n, " r=(%d %d, %d %d)\n", 20 | p->r.min.x, p->r.min.y, p->r.max.x, p->r.max.y); 21 | switch(p->flags&PACK){ 22 | default: SET(place); break; 23 | case PACKN: place="n"; break; 24 | case PACKE: place="e"; break; 25 | case PACKS: place="s"; break; 26 | case PACKW: place="w"; break; 27 | } 28 | switch(p->flags&PLACE){ 29 | default: SET(stick); break; 30 | case PLACECEN: stick=""; break; 31 | case PLACES: stick=" stick s"; break; 32 | case PLACEE: stick=" stick e"; break; 33 | case PLACEW: stick=" stick w"; break; 34 | case PLACEN: stick=" stick n"; break; 35 | case PLACENE: stick=" stick ne"; break; 36 | case PLACENW: stick=" stick nw"; break; 37 | case PLACESE: stick=" stick se"; break; 38 | case PLACESW: stick=" stick sw"; break; 39 | } 40 | pl_iprint(n, " place %s%s%s%s%s%s\n", 41 | place, 42 | p->flags&FILLX?" fill x":"", 43 | p->flags&FILLY?" fill y":"", 44 | stick, 45 | p->flags&EXPAND?" expand":"", 46 | p->flags&FIXED?" fixed":""); 47 | if(!eqpt(p->pad, Pt(0, 0))) pl_iprint(n, " pad=%d,%d)\n", p->pad.x, p->pad.y); 48 | if(!eqpt(p->ipad, Pt(0, 0))) pl_iprint(n, " ipad=%d,%d)\n", p->ipad.x, p->ipad.y); 49 | pl_iprint(n, " size=(%d,%d), sizereq=(%d,%d)\n", 50 | p->size.x, p->size.y, p->sizereq.x, p->sizereq.y); 51 | for(c=p->child;c;c=c->next) 52 | pl_ipprint(c, n+1); 53 | } 54 | void pl_print(Panel *p){ 55 | pl_ipprint(p, 0); 56 | } 57 | -------------------------------------------------------------------------------- /libpanel/slider.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Slider Slider; 8 | struct Slider{ 9 | int dir; /* HORIZ or VERT */ 10 | int val; /* setting, in screen coordinates */ 11 | Point minsize; 12 | void (*hit)(Panel *, int, int, int); /* call back to user when slider changes */ 13 | int buttons; 14 | }; 15 | void pl_drawslider(Panel *p){ 16 | Rectangle r; 17 | Slider *sp; 18 | sp=p->data; 19 | r=pl_box(p->b, p->r, UP); 20 | switch(sp->dir){ 21 | case HORIZ: pl_sliderupd(p->b, r, sp->dir, 0, sp->val); break; 22 | case VERT: pl_sliderupd(p->b, r, sp->dir, r.max.y-sp->val, r.max.y); break; 23 | } 24 | } 25 | int pl_hitslider(Panel *p, Mouse *m){ 26 | int oldstate, oldval, len; 27 | Point ul, size; 28 | Slider *sp; 29 | sp=p->data; 30 | ul=p->r.min; 31 | size=subpt(p->r.max, p->r.min); 32 | pl_interior(p->state, &ul, &size); 33 | oldstate=p->state; 34 | oldval=sp->val; 35 | SET(len); 36 | if(m->buttons&OUT) 37 | p->state=UP; 38 | else if(m->buttons&7){ 39 | p->state=DOWN; 40 | sp->buttons=m->buttons; 41 | if(sp->dir==HORIZ){ 42 | sp->val=m->xy.x-ul.x; 43 | len=size.x; 44 | } 45 | else{ 46 | sp->val=ul.y+size.y-m->xy.y; 47 | len=size.y; 48 | } 49 | if(sp->val<0) sp->val=0; 50 | else if(sp->val>len) sp->val=len; 51 | } 52 | else /* mouse inside, but no buttons down */ 53 | p->state=UP; 54 | if(oldval!=sp->val || oldstate!=p->state) pldraw(p, p->b); 55 | if(oldval!=sp->val && sp->hit) sp->hit(p, sp->buttons, sp->val, len); 56 | return 0; 57 | } 58 | void pl_typeslider(Panel *p, Rune c){ 59 | USED(p, c); 60 | } 61 | Point pl_getsizeslider(Panel *p, Point children){ 62 | USED(children); 63 | return pl_boxsize(((Slider *)p->data)->minsize, p->state); 64 | } 65 | void pl_childspaceslider(Panel *g, Point *ul, Point *size){ 66 | USED(g, ul, size); 67 | } 68 | void plinitslider(Panel *v, int flags, Point size, void (*hit)(Panel *, int, int, int)){ 69 | Slider *sp; 70 | sp=v->data; 71 | v->r=Rect(0,0,size.x,size.y); 72 | v->flags=flags|LEAF; 73 | v->state=UP; 74 | v->draw=pl_drawslider; 75 | v->hit=pl_hitslider; 76 | v->type=pl_typeslider; 77 | v->getsize=pl_getsizeslider; 78 | v->childspace=pl_childspaceslider; 79 | sp->minsize=size; 80 | sp->dir=size.x>size.y?HORIZ:VERT; 81 | sp->hit=hit; 82 | v->kind="slider"; 83 | } 84 | Panel *plslider(Panel *parent, int flags, Point size, void (*hit)(Panel *, int, int, int)){ 85 | Panel *p; 86 | p=pl_newpanel(parent, sizeof(Slider)); 87 | plinitslider(p, flags, size, hit); 88 | return p; 89 | } 90 | void plsetslider(Panel *p, int value, int range){ 91 | Slider *sp; 92 | sp=p->data; 93 | if(value<0) value=0; 94 | else if(value>range) value=range; 95 | if(sp->dir==HORIZ) sp->val=value*(p->r.max.x-p->r.min.x)/range; 96 | else sp->val=value*(p->r.max.y-p->r.min.y)/range; 97 | } 98 | -------------------------------------------------------------------------------- /libpanel/message.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Message Message; 8 | struct Message{ 9 | char *text; 10 | Point minsize; 11 | }; 12 | void pl_textmsg(Image *b, Rectangle r, Font *f, char *s){ 13 | char *start, *end; /* of line */ 14 | Point where; 15 | int lwid, c, wid; 16 | where=r.min; 17 | wid=r.max.x-r.min.x; 18 | do{ 19 | start=s; 20 | lwid=0; 21 | end=s; 22 | do{ 23 | for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); 24 | if(lwid>wid) break; 25 | end=s; 26 | for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); 27 | }while(*s!='\0'); 28 | if(end==start) /* can't even fit one word on line! */ 29 | end=s; 30 | c=*end; 31 | *end='\0'; 32 | string(b, where, display->black, ZP, f, start); 33 | *end=c; 34 | where.y+=font->height; 35 | s=end; 36 | while(*s==' ') s=pl_nextrune(s); 37 | }while(*s!='\0'); 38 | } 39 | Point pl_foldsize(Font *f, char *s, int wid){ 40 | char *start, *end; /* of line */ 41 | Point size; 42 | int lwid, ewid; 43 | size=Pt(0,0); 44 | do{ 45 | start=s; 46 | lwid=0; 47 | end=s; 48 | ewid=lwid; 49 | do{ 50 | for(;*s!=' ' && *s!='\0';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); 51 | if(lwid>wid) break; 52 | end=s; 53 | ewid=lwid; 54 | for(;*s==' ';s=pl_nextrune(s)) lwid+=pl_runewidth(f, s); 55 | }while(*s!='\0'); 56 | if(end==start){ /* can't even fit one word on line! */ 57 | ewid=lwid; 58 | end=s; 59 | } 60 | if(ewid>size.x) size.x=ewid; 61 | size.y+=font->height; 62 | s=end; 63 | while(*s==' ') s=pl_nextrune(s); 64 | }while(*s!='\0'); 65 | return size; 66 | } 67 | void pl_drawmessage(Panel *p){ 68 | pl_textmsg(p->b, pl_box(p->b, p->r, PASSIVE), font, ((Message *)p->data)->text); 69 | } 70 | int pl_hitmessage(Panel *g, Mouse *m){ 71 | USED(g, m); 72 | return 0; 73 | } 74 | void pl_typemessage(Panel *g, Rune c){ 75 | USED(g, c); 76 | } 77 | Point pl_getsizemessage(Panel *p, Point children){ 78 | Message *mp; 79 | USED(children); 80 | mp=p->data; 81 | return pl_boxsize(pl_foldsize(font, mp->text, mp->minsize.x), PASSIVE); 82 | } 83 | void pl_childspacemessage(Panel *p, Point *ul, Point *size){ 84 | USED(p, ul, size); 85 | } 86 | void plinitmessage(Panel *v, int flags, int wid, char *msg){ 87 | Message *mp; 88 | mp=v->data; 89 | v->flags=flags|LEAF; 90 | v->draw=pl_drawmessage; 91 | v->hit=pl_hitmessage; 92 | v->type=pl_typemessage; 93 | v->getsize=pl_getsizemessage; 94 | v->childspace=pl_childspacemessage; 95 | mp->text=msg; 96 | mp->minsize=Pt(wid, font->height); 97 | v->kind="message"; 98 | } 99 | Panel *plmessage(Panel *parent, int flags, int wid, char *msg){ 100 | Panel *v; 101 | v=pl_newpanel(parent, sizeof(Message)); 102 | plinitmessage(v, flags, wid, msg); 103 | return v; 104 | } 105 | -------------------------------------------------------------------------------- /libpanel/mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | void *pl_emalloc(int n){ 8 | void *v; 9 | v=mallocz(n, 1); 10 | if(v==0){ 11 | fprint(2, "Can't malloc!\n"); 12 | exits("no mem"); 13 | } 14 | setmalloctag(v, getcallerpc(&n)); 15 | return v; 16 | } 17 | void *pl_erealloc(void *v, int n) 18 | { 19 | v=realloc(v, n); 20 | if(v==0){ 21 | fprint(2, "Can't realloc!\n"); 22 | exits("no mem"); 23 | } 24 | setrealloctag(v, getcallerpc(&v)); 25 | return v; 26 | } 27 | void pl_unexpected(Panel *g, char *rou){ 28 | fprint(2, "%s called unexpectedly (%s %#p)\n", rou, g->kind, g); 29 | abort(); 30 | } 31 | void pl_drawerror(Panel *g){ 32 | pl_unexpected(g, "draw"); 33 | } 34 | int pl_hiterror(Panel *g, Mouse *m){ 35 | USED(m); 36 | pl_unexpected(g, "hit"); 37 | return 0; 38 | } 39 | void pl_typeerror(Panel *g, Rune c){ 40 | USED(c); 41 | pl_unexpected(g, "type"); 42 | } 43 | Point pl_getsizeerror(Panel *g, Point childsize){ 44 | pl_unexpected(g, "getsize"); 45 | return childsize; 46 | } 47 | void pl_childspaceerror(Panel *g, Point *ul, Point *size){ 48 | USED(ul, size); 49 | pl_unexpected(g, "childspace"); 50 | } 51 | void pl_scrollerror(Panel *g, int dir, int button, int num, int den){ 52 | USED(dir, button, num, den); 53 | pl_unexpected(g, "scroll"); 54 | } 55 | void pl_setscrollbarerror(Panel *g, int top, int bot, int den){ 56 | USED(top, bot, den); 57 | pl_unexpected(g, "setscrollbar"); 58 | } 59 | int pl_prinormal(Panel *, Point){ 60 | return PRI_NORMAL; 61 | } 62 | Panel *pl_newpanel(Panel *parent, int ndata){ 63 | Panel *v; 64 | if(parent && parent->flags&LEAF){ 65 | fprint(2, "newpanel: can't create child of %s %#p\n", parent->kind, parent); 66 | exits("bad newpanel"); 67 | } 68 | v=pl_emalloc(sizeof(Panel)); 69 | v->r=Rect(0,0,0,0); 70 | v->flags=0; 71 | v->ipad=Pt(0,0); 72 | v->pad=Pt(0,0); 73 | v->size=Pt(0,0); 74 | v->sizereq=Pt(0,0); 75 | v->lastmouse=0; 76 | v->next=0; 77 | v->child=0; 78 | v->echild=0; 79 | v->b=0; 80 | v->pri=pl_prinormal; 81 | v->scrollee=0; 82 | v->xscroller=0; 83 | v->yscroller=0; 84 | v->parent=parent; 85 | v->scr.pos=Pt(0,0); 86 | v->scr.size=Pt(0,0); 87 | if(parent){ 88 | if(parent->child==0) 89 | parent->child=v; 90 | else 91 | parent->echild->next=v; 92 | parent->echild=v; 93 | } 94 | v->draw=pl_drawerror; 95 | v->hit=pl_hiterror; 96 | v->type=pl_typeerror; 97 | v->getsize=pl_getsizeerror; 98 | v->childspace=pl_childspaceerror; 99 | v->scroll=pl_scrollerror; 100 | v->setscrollbar=pl_setscrollbarerror; 101 | v->free=0; 102 | v->snarf=0; 103 | v->paste=0; 104 | if(ndata) 105 | v->data=pl_emalloc(ndata); 106 | else 107 | v->data=0; 108 | return v; 109 | } 110 | void plfree(Panel *p){ 111 | Panel *cp, *ncp; 112 | if(p==0) 113 | return; 114 | if(p==plkbfocus) 115 | plkbfocus=0; 116 | for(cp=p->child;cp;cp=ncp){ 117 | ncp=cp->next; 118 | plfree(cp); 119 | } 120 | if(p->free) p->free(p); 121 | if(p->data) free(p->data); 122 | free(p); 123 | } 124 | -------------------------------------------------------------------------------- /libpanel/popup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * popup 3 | * looks like a group, except diverts hits on certain buttons to 4 | * panels that it temporarily pops up. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "pldefs.h" 12 | typedef struct Popup Popup; 13 | struct Popup{ 14 | Image *save; /* where to save what the popup covers */ 15 | Panel *pop[3]; /* what to pop up */ 16 | }; 17 | void pl_drawpopup(Panel *p){ 18 | USED(p); 19 | } 20 | int pl_hitpopup(Panel *g, Mouse *m){ 21 | Panel *p; 22 | Point d; 23 | Popup *pp; 24 | 25 | pp=g->data; 26 | if(g->state==UP){ 27 | switch(m->buttons&7){ 28 | case 0: p=g->child; break; 29 | case 1: p=pp->pop[0]; g->state=DOWN1; break; 30 | case 2: p=pp->pop[1]; g->state=DOWN2; break; 31 | case 4: p=pp->pop[2]; g->state=DOWN3; break; 32 | default: p=0; break; 33 | } 34 | if(p==0){ 35 | p=g->child; 36 | g->state=DOWN; 37 | } 38 | else if(g->state!=UP){ 39 | plpack(p, screen->clipr); 40 | if(p->lastmouse) 41 | d=subpt(m->xy, divpt(addpt(p->lastmouse->r.min, 42 | p->lastmouse->r.max), 2)); 43 | else 44 | d=subpt(m->xy, divpt(addpt(p->r.min, p->r.max), 2)); 45 | if(p->r.min.x+d.xr.min.x) d.x=g->r.min.x-p->r.min.x; 46 | if(p->r.max.x+d.x>g->r.max.x) d.x=g->r.max.x-p->r.max.x; 47 | if(p->r.min.y+d.yr.min.y) d.y=g->r.min.y-p->r.min.y; 48 | if(p->r.max.y+d.y>g->r.max.y) d.y=g->r.max.y-p->r.max.y; 49 | plmove(p, d); 50 | pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill); 51 | if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min); 52 | pl_invis(p, 0); 53 | pldraw(p, g->b); 54 | } 55 | } 56 | else{ 57 | switch(g->state){ 58 | default: SET(p); break; /* can't happen! */ 59 | case DOWN1: p=pp->pop[0]; break; 60 | case DOWN2: p=pp->pop[1]; break; 61 | case DOWN3: p=pp->pop[2]; break; 62 | case DOWN: p=g->child; break; 63 | } 64 | if((m->buttons&7)==0){ 65 | if(g->state!=DOWN){ 66 | if(pp->save!=0){ 67 | draw(g->b, p->r, pp->save, 0, p->r.min); 68 | freeimage(pp->save); 69 | pp->save=0; 70 | } 71 | pl_invis(p, 1); 72 | } 73 | g->state=UP; 74 | } 75 | } 76 | plmouse(p, m); 77 | if((m->buttons&7)==0) 78 | g->state=UP; 79 | return (m->buttons&7)!=0; 80 | } 81 | void pl_typepopup(Panel *g, Rune c){ 82 | USED(g, c); 83 | } 84 | Point pl_getsizepopup(Panel *g, Point children){ 85 | USED(g); 86 | return children; 87 | } 88 | void pl_childspacepopup(Panel *g, Point *ul, Point *size){ 89 | USED(g, ul, size); 90 | } 91 | int pl_pripopup(Panel *, Point){ 92 | return PRI_POPUP; 93 | } 94 | void plinitpopup(Panel *v, int flags, Panel *pop0, Panel *pop1, Panel *pop2){ 95 | Popup *pp; 96 | pp=v->data; 97 | v->flags=flags; 98 | v->pri=pl_pripopup; 99 | v->state=UP; 100 | v->draw=pl_drawpopup; 101 | v->hit=pl_hitpopup; 102 | v->type=pl_typepopup; 103 | v->getsize=pl_getsizepopup; 104 | v->childspace=pl_childspacepopup; 105 | pp->pop[0]=pop0; 106 | pp->pop[1]=pop1; 107 | pp->pop[2]=pop2; 108 | pp->save=0; 109 | v->kind="popup"; 110 | } 111 | Panel *plpopup(Panel *parent, int flags, Panel *pop0, Panel *pop1, Panel *pop2){ 112 | Panel *v; 113 | v=pl_newpanel(parent, sizeof(Popup)); 114 | plinitpopup(v, flags, pop0, pop1, pop2); 115 | return v; 116 | } 117 | -------------------------------------------------------------------------------- /libpanel/pldefs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Definitions for internal use only 3 | */ 4 | /* 5 | * Variable-font text routines 6 | * These could make a separate library. 7 | */ 8 | Point pl_rtfmt(Rtext *, int); 9 | void pl_rtdraw(Image *, Rectangle, Rtext *, Point); 10 | void pl_rtredraw(Image *, Rectangle, Rtext *, Point, Point, int); 11 | Rtext *pl_rthit(Rtext *, Point, Point, Point); 12 | #define HITME 0x08000 /* tells ptinpanel not to look at children */ 13 | #define LEAF 0x10000 /* newpanel will refuse to attach children */ 14 | #define INVIS 0x20000 /* don't draw this */ 15 | #define REMOUSE 0x40000 /* send next mouse event here, even if not inside */ 16 | /* 17 | * States, also styles 18 | */ 19 | enum{ 20 | UP, 21 | DOWN1, 22 | DOWN2, 23 | DOWN3, 24 | DOWN, 25 | PASSIVE, 26 | FRAME 27 | }; 28 | /* 29 | * Scroll flags 30 | */ 31 | enum{ 32 | SCROLLUP, 33 | SCROLLDOWN, 34 | SCROLLABSY, 35 | SCROLLLEFT, 36 | SCROLLRIGHT, 37 | SCROLLABSX, 38 | }; 39 | /* 40 | * Scrollbar, slider orientations 41 | */ 42 | enum{ 43 | HORIZ, 44 | VERT 45 | }; 46 | Panel *pl_newpanel(Panel *, int); /* make a new Panel, given parent & data size */ 47 | void *pl_emalloc(int); /* allocate some space, exit on error */ 48 | void *pl_erealloc(void*,int); /* reallocate some space, exit on error */ 49 | void pl_print(Panel *); /* print a Panel tree */ 50 | Panel *pl_ptinpanel(Point, Panel *); /* highest-priority subpanel containing point */ 51 | /* 52 | * Drawing primitives 53 | */ 54 | int pl_drawinit(int); 55 | Rectangle pl_box(Image *, Rectangle, int); 56 | Rectangle pl_boxf(Image *b, Rectangle r, int flags, int style); 57 | Rectangle pl_outline(Image *, Rectangle, int); 58 | Point pl_boxsize(Point, int); 59 | void pl_interior(int, Point *, Point *); 60 | void pl_drawicon(Image *, Rectangle, int, int, Icon *); 61 | Rectangle pl_check(Image *, Rectangle, int); 62 | Rectangle pl_radio(Image *, Rectangle, int); 63 | int pl_ckwid(void); 64 | void pl_sliderupd(Image *, Rectangle, int, int, int); 65 | void pl_invis(Panel *, int); 66 | Point pl_iconsize(int, Icon *); 67 | void pl_highlight(Image *, Rectangle); 68 | void pl_clr(Image *, Rectangle); 69 | void pl_fill(Image *, Rectangle); 70 | void pl_cpy(Image *, Point, Rectangle); 71 | 72 | /* 73 | * Rune mangling functions 74 | */ 75 | int pl_idchar(int); 76 | int pl_rune1st(int); 77 | char *pl_nextrune(char *); 78 | int pl_runewidth(Font *, char *); 79 | /* 80 | * Fixed-font Text-window routines 81 | * These could be separated out into a separate library. 82 | */ 83 | typedef struct Textwin Textwin; 84 | struct Textwin{ 85 | Rune *text, *etext, *eslack; /* text, with some slack off the end */ 86 | int top, bot; /* range of runes visible on screen */ 87 | int sel0, sel1; /* selection */ 88 | Point *loc, *eloc; /* ul corners of visible runes (+1 more at end!) */ 89 | Image *b; /* bitmap the text is drawn in */ 90 | Rectangle r; /* rectangle the text is drawn in */ 91 | Font *font; /* font text is drawn in */ 92 | int hgt; /* same as font->height */ 93 | int tabstop; /* tab settings are every tabstop pixels */ 94 | int mintab; /* the minimum size of a tab */ 95 | }; 96 | Textwin *twnew(Image *, Font *, Rune *, int); 97 | void twfree(Textwin *); 98 | void twhilite(Textwin *, int, int, int); 99 | void twselect(Textwin *, Mouse *); 100 | void twreplace(Textwin *, int, int, Rune *, int); 101 | void twscroll(Textwin *, int); 102 | int twpt2rune(Textwin *, Point); 103 | void twreshape(Textwin *, Rectangle); 104 | void twmove(Textwin *, Point); 105 | void plemove(Panel *, Point); 106 | -------------------------------------------------------------------------------- /libpanel/scrollbar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Scrollbar Scrollbar; 8 | struct Scrollbar{ 9 | int dir; /* HORIZ or VERT */ 10 | int lo, hi; /* setting, in screen coordinates */ 11 | int buttons; /* saved mouse buttons for transmittal to scrollee */ 12 | Rectangle interior; 13 | Point minsize; 14 | }; 15 | #define SBWID 8 /* should come from draw.c? */ 16 | void pl_drawscrollbar(Panel *p){ 17 | Scrollbar *sp; 18 | sp=p->data; 19 | sp->interior=pl_outline(p->b, p->r, p->state); 20 | pl_sliderupd(p->b, sp->interior, sp->dir, sp->lo, sp->hi); 21 | } 22 | int pl_hitscrollbar(Panel *g, Mouse *m){ 23 | int oldstate, pos, len, dy; 24 | Point ul, size; 25 | Scrollbar *sp; 26 | sp=g->data; 27 | ul=g->r.min; 28 | size=subpt(g->r.max, g->r.min); 29 | pl_interior(g->state, &ul, &size); 30 | oldstate=g->state; 31 | if(!(g->flags & USERFL) && (m->buttons&OUT || !ptinrect(m->xy, g->r))){ 32 | m->buttons&=~OUT; 33 | g->state=UP; 34 | goto out; 35 | } 36 | if(sp->dir==HORIZ){ 37 | pos=m->xy.x-ul.x; 38 | len=size.x; 39 | } 40 | else{ 41 | pos=m->xy.y-ul.y; 42 | len=size.y; 43 | } 44 | if(pos<0) pos=0; 45 | else if(pos>len) pos=len; 46 | if(m->buttons&7){ 47 | g->state=DOWN; 48 | sp->buttons=m->buttons; 49 | switch(m->buttons){ 50 | case 1: 51 | dy=pos*(sp->hi-sp->lo)/len; 52 | pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo-dy, 53 | sp->hi-dy); 54 | break; 55 | case 2: 56 | if(g->scrollee && g->scrollee->scroll) 57 | g->scrollee->scroll(g->scrollee, sp->dir, 58 | m->buttons, pos, len); 59 | break; 60 | case 4: 61 | dy=pos*(sp->hi-sp->lo)/len; 62 | pl_sliderupd(g->b, sp->interior, sp->dir, sp->lo+dy, 63 | sp->hi+dy); 64 | break; 65 | } 66 | } 67 | else{ 68 | if(!(sp->buttons&2) && g->state==DOWN && g->scrollee && g->scrollee->scroll) 69 | g->scrollee->scroll(g->scrollee, sp->dir, sp->buttons, 70 | pos, len); 71 | g->state=UP; 72 | } 73 | out: 74 | if(oldstate!=g->state) pldraw(g, g->b); 75 | return g->state==DOWN; 76 | } 77 | void pl_typescrollbar(Panel *p, Rune c){ 78 | USED(p, c); 79 | } 80 | Point pl_getsizescrollbar(Panel *p, Point children){ 81 | USED(children); 82 | return pl_boxsize(((Scrollbar *)p->data)->minsize, p->state); 83 | } 84 | void pl_childspacescrollbar(Panel *p, Point *ul, Point *size){ 85 | USED(p, ul, size); 86 | } 87 | /* 88 | * Arguments lo, hi and len are in the scrollee's natural coordinates 89 | */ 90 | void pl_setscrollbarscrollbar(Panel *p, int lo, int hi, int len){ 91 | Point ul, size; 92 | int mylen; 93 | Scrollbar *sp; 94 | sp=p->data; 95 | ul=p->r.min; 96 | size=subpt(p->r.max, p->r.min); 97 | pl_interior(p->state, &ul, &size); 98 | mylen=sp->dir==HORIZ?size.x:size.y; 99 | if(len==0) len=1; 100 | sp->lo=lo*mylen/len; 101 | sp->hi=hi*mylen/len; 102 | if(sp->lo<0) sp->lo=0; 103 | if(sp->lo>=mylen) sp->hi=mylen-1; 104 | if(sp->hi<=sp->lo) sp->hi=sp->lo+1; 105 | if(sp->hi>mylen) sp->hi=mylen; 106 | pldraw(p, p->b); 107 | } 108 | int pl_priscrollbar(Panel *, Point){ 109 | return PRI_SCROLLBAR; 110 | } 111 | void plinitscrollbar(Panel *v, int flags){ 112 | Scrollbar *sp; 113 | sp=v->data; 114 | v->flags=flags|LEAF; 115 | v->pri=pl_priscrollbar; 116 | v->state=UP; 117 | v->draw=pl_drawscrollbar; 118 | v->hit=pl_hitscrollbar; 119 | v->type=pl_typescrollbar; 120 | v->getsize=pl_getsizescrollbar; 121 | v->childspace=pl_childspacescrollbar; 122 | v->setscrollbar=pl_setscrollbarscrollbar; 123 | switch(flags&PACK){ 124 | case PACKN: 125 | case PACKS: 126 | sp->dir=HORIZ; 127 | sp->minsize=Pt(0, SBWID); 128 | v->flags|=FILLX; 129 | break; 130 | case PACKE: 131 | case PACKW: 132 | sp->dir=VERT; 133 | sp->minsize=Pt(SBWID, 0); 134 | v->flags|=FILLY; 135 | break; 136 | } 137 | sp->lo=0; 138 | sp->hi=0; 139 | v->kind="scrollbar"; 140 | } 141 | Panel *plscrollbar(Panel *parent, int flags){ 142 | Panel *v; 143 | v=pl_newpanel(parent, sizeof(Scrollbar)); 144 | plinitscrollbar(v, flags); 145 | return v; 146 | } 147 | -------------------------------------------------------------------------------- /libpanel/pulldown.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pulldown 3 | * makes a button that pops up a panel when hit 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "pldefs.h" 11 | typedef struct Pulldown Pulldown; 12 | struct Pulldown{ 13 | Icon *icon; /* button label */ 14 | Panel *pull; /* Panel to pull down */ 15 | int side; /* which side of the button to put the panel on */ 16 | Image *save; /* where to save what we draw the panel on */ 17 | }; 18 | void pl_drawpulldown(Panel *p){ 19 | pl_drawicon(p->b, pl_box(p->b, p->r, p->state), PLACECEN, 20 | p->flags, ((Pulldown *)p->data)->icon); 21 | } 22 | int pl_hitpulldown(Panel *g, Mouse *m){ 23 | int oldstate, passon; 24 | Rectangle r; 25 | Panel *p, *hitme; 26 | Pulldown *pp; 27 | pp=g->data; 28 | oldstate=g->state; 29 | p=pp->pull; 30 | hitme=0; 31 | switch(g->state){ 32 | case UP: 33 | if(!ptinrect(m->xy, g->r)) 34 | g->state=UP; 35 | else if(m->buttons&7){ 36 | r=g->b->r; 37 | p->flags&=~PLACE; 38 | switch(pp->side){ 39 | case PACKN: 40 | r.min.x=g->r.min.x; 41 | r.max.y=g->r.min.y; 42 | p->flags|=PLACESW; 43 | break; 44 | case PACKS: 45 | r.min.x=g->r.min.x; 46 | r.min.y=g->r.max.y; 47 | p->flags|=PLACENW; 48 | break; 49 | case PACKE: 50 | r.min.x=g->r.max.x; 51 | r.min.y=g->r.min.y; 52 | p->flags|=PLACENW; 53 | break; 54 | case PACKW: 55 | r.max.x=g->r.min.x; 56 | r.min.y=g->r.min.y; 57 | p->flags|=PLACENE; 58 | break; 59 | case PACKCEN: 60 | r.min=g->r.min; 61 | p->flags|=PLACENW; 62 | break; 63 | } 64 | plpack(p, r); 65 | pp->save=allocimage(display, p->r, g->b->chan, 0, DNofill); 66 | if(pp->save!=0) draw(pp->save, p->r, g->b, 0, p->r.min); 67 | pl_invis(p, 0); 68 | pldraw(p, g->b); 69 | g->state=DOWN; 70 | } 71 | break; 72 | case DOWN: 73 | if(!ptinrect(m->xy, g->r)){ 74 | switch(pp->side){ 75 | default: SET(passon); break; /* doesn't happen */ 76 | case PACKN: passon=m->xy.yr.min.y; break; 77 | case PACKS: passon=m->xy.y>=g->r.max.y; break; 78 | case PACKE: passon=m->xy.x>=g->r.max.x; break; 79 | case PACKW: passon=m->xy.xr.min.x; break; 80 | case PACKCEN: passon=1; break; 81 | } 82 | if(passon){ 83 | hitme=p; 84 | if((m->buttons&7)==0) g->state=UP; 85 | } 86 | else g->state=UP; 87 | } 88 | else if((m->buttons&7)==0) g->state=UP; 89 | else hitme=p; 90 | if(g->state!=DOWN && pp->save){ 91 | draw(g->b, p->r, pp->save, 0, p->r.min); 92 | freeimage(pp->save); 93 | pp->save=0; 94 | pl_invis(p, 1); 95 | hitme=p; 96 | } 97 | } 98 | if(g->state!=oldstate) pldraw(g, g->b); 99 | if(hitme) plmouse(hitme, m); 100 | return g->state==DOWN; 101 | } 102 | void pl_typepulldown(Panel *p, Rune c){ 103 | USED(p, c); 104 | } 105 | Point pl_getsizepulldown(Panel *p, Point children){ 106 | USED(p, children); 107 | return pl_boxsize(pl_iconsize(p->flags, ((Pulldown *)p->data)->icon), p->state); 108 | } 109 | void pl_childspacepulldown(Panel *p, Point *ul, Point *size){ 110 | USED(p, ul, size); 111 | } 112 | void plinitpulldown(Panel *v, int flags, Icon *icon, Panel *pullthis, int side){ 113 | Pulldown *pp; 114 | pp=v->data; 115 | v->flags=flags|LEAF; 116 | v->draw=pl_drawpulldown; 117 | v->hit=pl_hitpulldown; 118 | v->type=pl_typepulldown; 119 | v->getsize=pl_getsizepulldown; 120 | v->childspace=pl_childspacepulldown; 121 | pp->pull=pullthis; 122 | pp->side=side; 123 | pp->icon=icon; 124 | v->kind="pulldown"; 125 | } 126 | Panel *plpulldown(Panel *parent, int flags, Icon *icon, Panel *pullthis, int side){ 127 | Panel *v; 128 | v=pl_newpanel(parent, sizeof(Pulldown)); 129 | v->state=UP; 130 | ((Pulldown *)v->data)->save=0; 131 | plinitpulldown(v, flags, icon, pullthis, side); 132 | return v; 133 | } 134 | Panel *plmenubar(Panel *parent, int flags, int cflags, Icon *l1, Panel *m1, Icon *l2, ...){ 135 | Panel *v; 136 | va_list arg; 137 | Icon *s; 138 | int pulldir; 139 | switch(cflags&PACK){ 140 | default: 141 | SET(pulldir); 142 | break; 143 | case PACKE: 144 | case PACKW: 145 | pulldir=PACKS; 146 | break; 147 | case PACKN: 148 | case PACKS: 149 | pulldir=PACKE; 150 | break; 151 | } 152 | v=plgroup(parent, flags); 153 | va_start(arg, cflags); 154 | while((s=va_arg(arg, Icon *))!=0) 155 | plpulldown(v, cflags, s, va_arg(arg, Panel *), pulldir); 156 | va_end(arg); 157 | USED(l1, m1, l2); 158 | v->kind="menubar"; 159 | return v; 160 | } 161 | -------------------------------------------------------------------------------- /libpanel/entry.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | #include 8 | 9 | typedef struct Entry Entry; 10 | struct Entry{ 11 | char *entry; 12 | char *entp; 13 | char *eent; 14 | void (*hit)(Panel *, char *); 15 | Point minsize; 16 | }; 17 | #define SLACK 7 /* enough for one extra rune and ◀ and a nul */ 18 | char *pl_snarfentry(Panel *p){ 19 | Entry *ep; 20 | int n; 21 | 22 | if(p->flags&USERFL) /* no snarfing from password entry */ 23 | return nil; 24 | ep=p->data; 25 | n=utfnlen(ep->entry, ep->entp-ep->entry); 26 | if(n<1) return nil; 27 | return smprint("%.*s", n, ep->entry); 28 | } 29 | void pl_pasteentry(Panel *p, char *s){ 30 | Entry *ep; 31 | char *e; 32 | int n, m; 33 | 34 | ep=p->data; 35 | n=ep->entp-ep->entry; 36 | m=strlen(s); 37 | e=pl_erealloc(ep->entry,n+m+SLACK); 38 | ep->entry=e; 39 | e+=n; 40 | strncpy(e, s, m); 41 | e+=m; 42 | *e='\0'; 43 | ep->entp=ep->eent=e; 44 | pldraw(p, p->b); 45 | } 46 | void pl_drawentry(Panel *p){ 47 | Rectangle r; 48 | Entry *ep; 49 | char *s; 50 | 51 | ep=p->data; 52 | r=pl_box(p->b, p->r, p->state); 53 | s=ep->entry; 54 | if(p->flags & USERFL){ 55 | char *p; 56 | s=strdup(s); 57 | for(p=s; *p; p++) 58 | *p='*'; 59 | } 60 | if(stringwidth(font, s)<=r.max.x-r.min.x) 61 | pl_drawicon(p->b, r, PLACEW, 0, s); 62 | else 63 | pl_drawicon(p->b, r, PLACEE, 0, s); 64 | if(s != ep->entry) 65 | free(s); 66 | } 67 | int pl_hitentry(Panel *p, Mouse *m){ 68 | if((m->buttons&7)==1){ 69 | plgrabkb(p); 70 | 71 | p->state=DOWN; 72 | pldraw(p, p->b); 73 | while(m->buttons&1){ 74 | int old; 75 | old=m->buttons; 76 | if(display->bufp > display->buf) 77 | flushimage(display, 1); 78 | *m=emouse(); 79 | if((old&7)==1){ 80 | if((m->buttons&7)==3){ 81 | Entry *ep; 82 | 83 | plsnarf(p); 84 | 85 | /* cut */ 86 | ep=p->data; 87 | ep->entp=ep->entry; 88 | *ep->entp='\0'; 89 | pldraw(p, p->b); 90 | } 91 | if((m->buttons&7)==5) 92 | plpaste(p); 93 | } 94 | } 95 | p->state=UP; 96 | pldraw(p, p->b); 97 | } 98 | return 0; 99 | } 100 | void pl_typeentry(Panel *p, Rune c){ 101 | int n; 102 | Entry *ep; 103 | ep=p->data; 104 | switch(c){ 105 | case '\n': 106 | case '\r': 107 | *ep->entp='\0'; 108 | if(ep->hit) ep->hit(p, ep->entry); 109 | return; 110 | case Kesc: 111 | plsnarf(p); 112 | /* no break */ 113 | case Kdel: /* clear */ 114 | case Knack: /* ^U: erase line */ 115 | ep->entp=ep->entry; 116 | *ep->entp='\0'; 117 | break; 118 | case Kbs: /* ^H: erase character */ 119 | while(ep->entp!=ep->entry && !pl_rune1st(ep->entp[-1])) *--ep->entp='\0'; 120 | if(ep->entp!=ep->entry) *--ep->entp='\0'; 121 | break; 122 | case Ketb: /* ^W: erase word */ 123 | while(ep->entp!=ep->entry && !pl_idchar(ep->entp[-1])) 124 | --ep->entp; 125 | while(ep->entp!=ep->entry && pl_idchar(ep->entp[-1])) 126 | --ep->entp; 127 | *ep->entp='\0'; 128 | break; 129 | default: 130 | if(c < 0x20 || (c & 0xFF00) == KF || (c & 0xFF00) == Spec) 131 | break; 132 | ep->entp+=runetochar(ep->entp, &c); 133 | if(ep->entp>ep->eent){ 134 | n=ep->entp-ep->entry; 135 | ep->entry=pl_erealloc(ep->entry, n+100+SLACK); 136 | ep->entp=ep->entry+n; 137 | ep->eent=ep->entp+100; 138 | } 139 | *ep->entp='\0'; 140 | break; 141 | } 142 | pldraw(p, p->b); 143 | } 144 | Point pl_getsizeentry(Panel *p, Point children){ 145 | USED(children); 146 | return pl_boxsize(((Entry *)p->data)->minsize, p->state); 147 | } 148 | void pl_childspaceentry(Panel *p, Point *ul, Point *size){ 149 | USED(p, ul, size); 150 | } 151 | void pl_freeentry(Panel *p){ 152 | Entry *ep; 153 | ep = p->data; 154 | free(ep->entry); 155 | ep->entry = ep->eent = ep->entp = 0; 156 | } 157 | void plinitentry(Panel *v, int flags, int wid, char *str, void (*hit)(Panel *, char *)){ 158 | int elen; 159 | Entry *ep; 160 | ep=v->data; 161 | v->flags=flags|LEAF; 162 | v->state=UP; 163 | v->draw=pl_drawentry; 164 | v->hit=pl_hitentry; 165 | v->type=pl_typeentry; 166 | v->getsize=pl_getsizeentry; 167 | v->childspace=pl_childspaceentry; 168 | ep->minsize=Pt(wid, font->height); 169 | v->free=pl_freeentry; 170 | v->snarf=pl_snarfentry; 171 | v->paste=pl_pasteentry; 172 | elen=100; 173 | if(str) elen+=strlen(str); 174 | ep->entry=pl_erealloc(ep->entry, elen+SLACK); 175 | ep->eent=ep->entry+elen; 176 | strecpy(ep->entry, ep->eent, str ? str : ""); 177 | ep->entp=ep->entry+strlen(ep->entry); 178 | ep->hit=hit; 179 | v->kind="entry"; 180 | } 181 | Panel *plentry(Panel *parent, int flags, int wid, char *str, void (*hit)(Panel *, char *)){ 182 | Panel *v; 183 | v=pl_newpanel(parent, sizeof(Entry)); 184 | plinitentry(v, flags, wid, str, hit); 185 | return v; 186 | } 187 | char *plentryval(Panel *p){ 188 | Entry *ep; 189 | ep=p->data; 190 | *ep->entp='\0'; 191 | return ep->entry; 192 | } 193 | -------------------------------------------------------------------------------- /libpanel/pack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | int pl_max(int a, int b){ 8 | return a>b?a:b; 9 | } 10 | Point pl_sizesibs(Panel *p){ 11 | Point s; 12 | if(p==0) return Pt(0,0); 13 | s=pl_sizesibs(p->next); 14 | switch(p->flags&PACK){ 15 | case PACKN: 16 | case PACKS: 17 | s.x=pl_max(s.x, p->sizereq.x); 18 | s.y+=p->sizereq.y; 19 | break; 20 | case PACKE: 21 | case PACKW: 22 | s.x+=p->sizereq.x; 23 | s.y=pl_max(s.y, p->sizereq.y); 24 | break; 25 | } 26 | return s; 27 | } 28 | /* 29 | * Compute the requested size of p and its descendants. 30 | */ 31 | void pl_sizereq(Panel *p){ 32 | Panel *cp; 33 | Point maxsize; 34 | maxsize=Pt(0,0); 35 | for(cp=p->child;cp;cp=cp->next){ 36 | pl_sizereq(cp); 37 | if(cp->sizereq.x>maxsize.x) maxsize.x=cp->sizereq.x; 38 | if(cp->sizereq.y>maxsize.y) maxsize.y=cp->sizereq.y; 39 | } 40 | for(cp=p->child;cp;cp=cp->next){ 41 | if(cp->flags&MAXX) cp->sizereq.x=maxsize.x; 42 | if(cp->flags&MAXY) cp->sizereq.y=maxsize.y; 43 | } 44 | p->childreq=pl_sizesibs(p->child); 45 | p->sizereq=addpt(addpt(p->getsize(p, p->childreq), p->ipad), p->pad); 46 | if(p->flags&FIXEDX) p->sizereq.x=p->fixedsize.x; 47 | if(p->flags&FIXEDY) p->sizereq.y=p->fixedsize.y; 48 | } 49 | Point pl_getshare(Panel *p){ 50 | Point share; 51 | if(p==0) return Pt(0,0); 52 | share=pl_getshare(p->next); 53 | if(p->flags&EXPAND) switch(p->flags&PACK){ 54 | case PACKN: 55 | case PACKS: 56 | if(share.x==0) share.x=1; 57 | share.y++; 58 | break; 59 | case PACKE: 60 | case PACKW: 61 | share.x++; 62 | if(share.y==0) share.y=1; 63 | break; 64 | } 65 | return share; 66 | } 67 | /* 68 | * Set the sizes and rectangles of p and its descendants, given their requested sizes. 69 | */ 70 | void pl_setrect(Panel *p, Point ul, Point avail){ 71 | Point space, newul, newspace, slack, share; 72 | int l; 73 | Panel *c; 74 | p->size=subpt(p->sizereq, p->pad); 75 | ul=addpt(ul, divpt(p->pad, 2)); 76 | avail=subpt(avail, p->pad); 77 | if(p->size.x>avail.x) 78 | p->size.x = avail.x; 79 | if(p->size.y>avail.y) 80 | p->size.y = avail.y; 81 | if(p->flags&(FILLX|EXPAND)) p->size.x=avail.x; 82 | if(p->flags&(FILLY|EXPAND)) p->size.y=avail.y; 83 | switch(p->flags&PLACE){ 84 | case PLACECEN: ul.x+=(avail.x-p->size.x)/2; ul.y+=(avail.y-p->size.y)/2; break; 85 | case PLACES: ul.x+=(avail.x-p->size.x)/2; ul.y+= avail.y-p->size.y ; break; 86 | case PLACEE: ul.x+= avail.x-p->size.x ; ul.y+=(avail.y-p->size.y)/2; break; 87 | case PLACEW: ul.y+=(avail.y-p->size.y)/2; break; 88 | case PLACEN: ul.x+=(avail.x-p->size.x)/2; break; 89 | case PLACENE: ul.x+= avail.x-p->size.x ; break; 90 | case PLACENW: break; 91 | case PLACESE: ul.x+= avail.x-p->size.x ; ul.y+= avail.y-p->size.y ; break; 92 | case PLACESW: ul.y+= avail.y-p->size.y ; break; 93 | } 94 | p->r=Rpt(ul, addpt(ul, p->size)); 95 | space=p->size; 96 | p->childspace(p, &ul, &space); 97 | slack=subpt(space, p->childreq); 98 | share=pl_getshare(p->child); 99 | for(c=p->child;c;c=c->next){ 100 | if(c->flags&IGNORE) continue; 101 | if(c->flags&EXPAND){ 102 | switch(c->flags&PACK){ 103 | case PACKN: 104 | case PACKS: 105 | c->sizereq.x+=slack.x; 106 | l=slack.y/share.y; 107 | c->sizereq.y+=l; 108 | slack.y-=l; 109 | --share.y; 110 | break; 111 | case PACKE: 112 | case PACKW: 113 | l=slack.x/share.x; 114 | c->sizereq.x+=l; 115 | slack.x-=l; 116 | --share.x; 117 | c->sizereq.y+=slack.y; 118 | break; 119 | } 120 | } 121 | switch(c->flags&PACK){ 122 | case PACKN: 123 | newul=Pt(ul.x, ul.y+c->sizereq.y); 124 | newspace=Pt(space.x, space.y-c->sizereq.y); 125 | pl_setrect(c, ul, Pt(space.x, c->sizereq.y)); 126 | break; 127 | case PACKW: 128 | newul=Pt(ul.x+c->sizereq.x, ul.y); 129 | newspace=Pt(space.x-c->sizereq.x, space.y); 130 | pl_setrect(c, ul, Pt(c->sizereq.x, space.y)); 131 | break; 132 | case PACKS: 133 | newul=ul; 134 | newspace=Pt(space.x, space.y-c->sizereq.y); 135 | pl_setrect(c, Pt(ul.x, ul.y+space.y-c->sizereq.y), 136 | Pt(space.x, c->sizereq.y)); 137 | break; 138 | case PACKE: 139 | newul=ul; 140 | newspace=Pt(space.x-c->sizereq.x, space.y); 141 | pl_setrect(c, Pt(ul.x+space.x-c->sizereq.x, ul.y), 142 | Pt(c->sizereq.x, space.y)); 143 | break; 144 | } 145 | ul=newul; 146 | space=newspace; 147 | } 148 | } 149 | void plpack(Panel *p, Rectangle where){ 150 | pl_sizereq(p); 151 | pl_setrect(p, where.min, subpt(where.max, where.min)); 152 | } 153 | /* 154 | * move an already-packed panel so that p->r=raddp(p->r, d) 155 | */ 156 | void plmove(Panel *p, Point d){ 157 | if(strcmp(p->kind, "edit") == 0) /* sorry */ 158 | plemove(p, d); 159 | p->r=rectaddpt(p->r, d); 160 | for(p=p->child;p;p=p->next) plmove(p, d); 161 | } 162 | -------------------------------------------------------------------------------- /libpanel/button.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct Button Button; 8 | struct Button{ 9 | int btype; /* button type */ 10 | Icon *icon; /* what to write on the button */ 11 | int check; /* for check/radio buttons */ 12 | void (*hit)(Panel *, int, int); /* call back user code on check/radio hit */ 13 | void (*menuhit)(int, int); /* call back user code on menu item hit */ 14 | void (*pl_buttonhit)(Panel *, int); /* call back user code on button hit */ 15 | int index; /* arg to menuhit */ 16 | int buttons; 17 | }; 18 | /* 19 | * Button types 20 | */ 21 | #define BUTTON 1 22 | #define CHECK 2 23 | #define RADIO 3 24 | void pl_drawbutton(Panel *p){ 25 | Rectangle r; 26 | Button *bp; 27 | bp=p->data; 28 | r=pl_boxf(p->b, p->r, p->flags, p->state); 29 | switch(bp->btype){ 30 | case CHECK: 31 | r=pl_check(p->b, r, bp->check); 32 | break; 33 | case RADIO: 34 | r=pl_radio(p->b, r, bp->check); 35 | break; 36 | } 37 | pl_drawicon(p->b, r, PLACECEN, p->flags, bp->icon); 38 | } 39 | int pl_hitbutton(Panel *p, Mouse *m){ 40 | int oldstate, hitme; 41 | Panel *sib; 42 | Button *bp; 43 | bp=p->data; 44 | oldstate=p->state; 45 | if(m->buttons&OUT){ 46 | hitme=0; 47 | p->state=UP; 48 | } 49 | else if(m->buttons&7){ 50 | hitme=0; 51 | p->state=DOWN; 52 | bp->buttons=m->buttons; 53 | } 54 | else{ /* mouse inside, but no buttons down */ 55 | hitme=p->state==DOWN; 56 | p->state=UP; 57 | } 58 | if(hitme) switch(bp->btype){ 59 | case CHECK: 60 | if(hitme) bp->check=!bp->check; 61 | break; 62 | case RADIO: 63 | if(bp->check) bp->check=0; 64 | else{ 65 | if(p->parent){ 66 | for(sib=p->parent->child;sib;sib=sib->next){ 67 | if(sib->hit==pl_hitbutton 68 | && ((Button *)sib->data)->btype==RADIO 69 | && ((Button *)sib->data)->check){ 70 | ((Button *)sib->data)->check=0; 71 | pldraw(sib, p->b); 72 | } 73 | } 74 | } 75 | bp->check=1; 76 | } 77 | break; 78 | } 79 | if(hitme || oldstate!=p->state) pldraw(p, p->b); 80 | if(hitme && bp->hit){ 81 | bp->hit(p, bp->buttons, bp->check); 82 | p->state=UP; 83 | } 84 | return 0; 85 | } 86 | void pl_typebutton(Panel *g, Rune c){ 87 | USED(g, c); 88 | } 89 | Point pl_getsizebutton(Panel *p, Point children){ 90 | Point s; 91 | int ckw; 92 | Button *bp; 93 | USED(children); /* shouldn't have any children */ 94 | bp=p->data; 95 | s=pl_iconsize(p->flags, bp->icon); 96 | if(bp->btype!=BUTTON){ 97 | ckw=pl_ckwid(); 98 | if(s.ystate); 105 | } 106 | void pl_childspacebutton(Panel *g, Point *ul, Point *size){ 107 | USED(g, ul, size); 108 | } 109 | void pl_initbtype(Panel *v, int flags, Icon *icon, void (*hit)(Panel *, int, int), int btype){ 110 | Button *bp; 111 | bp=v->data; 112 | v->flags=flags|LEAF; 113 | v->state=UP; 114 | v->draw=pl_drawbutton; 115 | v->hit=pl_hitbutton; 116 | v->type=pl_typebutton; 117 | v->getsize=pl_getsizebutton; 118 | v->childspace=pl_childspacebutton; 119 | bp->btype=btype; 120 | bp->check=0; 121 | bp->hit=hit; 122 | bp->icon=icon; 123 | switch(btype){ 124 | case BUTTON: v->kind="button"; break; 125 | case CHECK: v->kind="checkbutton"; break; 126 | case RADIO: v->kind="radiobutton"; break; 127 | } 128 | } 129 | void pl_buttonhit(Panel *p, int buttons, int check){ 130 | USED(check); 131 | if(((Button *)p->data)->pl_buttonhit) ((Button *)p->data)->pl_buttonhit(p, buttons); 132 | } 133 | void plinitbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int)){ 134 | ((Button *)p->data)->pl_buttonhit=hit; 135 | pl_initbtype(p, flags, icon, pl_buttonhit, BUTTON); 136 | } 137 | void plinitcheckbutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ 138 | pl_initbtype(p, flags, icon, hit, CHECK); 139 | } 140 | void plinitradiobutton(Panel *p, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ 141 | pl_initbtype(p, flags, icon, hit, RADIO); 142 | } 143 | Panel *plbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int)){ 144 | Panel *p; 145 | p=pl_newpanel(parent, sizeof(Button)); 146 | plinitbutton(p, flags, icon, hit); 147 | return p; 148 | } 149 | Panel *plcheckbutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ 150 | Panel *p; 151 | p=pl_newpanel(parent, sizeof(Button)); 152 | plinitcheckbutton(p, flags, icon, hit); 153 | return p; 154 | } 155 | Panel *plradiobutton(Panel *parent, int flags, Icon *icon, void (*hit)(Panel *, int, int)){ 156 | Panel *p; 157 | p=pl_newpanel(parent, sizeof(Button)); 158 | plinitradiobutton(p, flags, icon, hit); 159 | return p; 160 | } 161 | void pl_hitmenu(Panel *p, int buttons){ 162 | void (*hit)(int, int); 163 | hit=((Button *)p->data)->menuhit; 164 | if(hit) hit(buttons, ((Button *)p->data)->index); 165 | } 166 | void plinitmenu(Panel *v, int flags, Icon **item, int cflags, void (*hit)(int, int)){ 167 | Panel *b; 168 | int i; 169 | v->flags=flags; 170 | v->kind="menu"; 171 | if(v->child){ 172 | plfree(v->child); 173 | v->child=0; 174 | } 175 | for(i=0;item[i];i++){ 176 | b=plbutton(v, cflags, item[i], pl_hitmenu); 177 | ((Button *)b->data)->menuhit=hit; 178 | ((Button *)b->data)->index=i; 179 | } 180 | } 181 | Panel *plmenu(Panel *parent, int flags, Icon **item, int cflags, void (*hit)(int, int)){ 182 | Panel *v; 183 | v=plgroup(parent, flags); 184 | plinitmenu(v, flags, item, cflags, hit); 185 | return v; 186 | } 187 | void plsetbutton(Panel *p, int val){ 188 | ((Button *)p->data)->check=val; 189 | } 190 | -------------------------------------------------------------------------------- /libpanel/list.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | typedef struct List List; 8 | struct List{ 9 | void (*hit)(Panel *, int, int); /* call user back on hit */ 10 | char *(*gen)(Panel *, int); /* return text given index or 0 if out of range */ 11 | int lo; /* indices of first, last items displayed */ 12 | int sel; /* index of hilited item */ 13 | int len; /* # of items in list */ 14 | Rectangle listr; 15 | Point minsize; 16 | int buttons; 17 | }; 18 | #define MAXHGT 12 19 | void pl_listsel(Panel *p, int sel, int on){ 20 | List *lp; 21 | int hi; 22 | Rectangle r; 23 | lp=p->data; 24 | hi=lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height; 25 | if(lp->lo>=0 && lp->lo<=sel && sellen){ 26 | r=lp->listr; 27 | r.min.y+=(sel-lp->lo)*font->height; 28 | r.max.y=r.min.y+font->height; 29 | if(on) 30 | pl_highlight(p->b, r); 31 | else{ 32 | pl_fill(p->b, r); 33 | pl_drawicon(p->b, r, PLACEW, 0, lp->gen(p, sel)); 34 | } 35 | } 36 | } 37 | void pl_liststrings(Panel *p, int lo, int hi, Rectangle r){ 38 | Panel *sb; 39 | List *lp; 40 | char *s; 41 | int i; 42 | lp=p->data; 43 | for(i=lo;i!=hi && (s=lp->gen(p, i));i++){ 44 | r.max.y=r.min.y+font->height; 45 | pl_drawicon(p->b, r, PLACEW, 0, s); 46 | r.min.y+=font->height; 47 | } 48 | if(lo<=lp->sel && lp->selsel, 1); 49 | sb=p->yscroller; 50 | if(sb && sb->setscrollbar) 51 | sb->setscrollbar(sb, lp->lo, 52 | lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, lp->len); 53 | } 54 | void pl_drawlist(Panel *p){ 55 | List *lp; 56 | lp=p->data; 57 | lp->listr=pl_box(p->b, p->r, UP); 58 | pl_liststrings(p, lp->lo, lp->lo+(lp->listr.max.y-lp->listr.min.y)/font->height, 59 | lp->listr); 60 | } 61 | int pl_hitlist(Panel *p, Mouse *m){ 62 | int oldsel, hitme; 63 | Point ul, size; 64 | List *lp; 65 | lp=p->data; 66 | hitme=0; 67 | ul=p->r.min; 68 | size=subpt(p->r.max, p->r.min); 69 | pl_interior(p->state, &ul, &size); 70 | oldsel=lp->sel; 71 | if(m->buttons&OUT){ 72 | p->state=UP; 73 | if(m->buttons&~OUT) lp->sel=-1; 74 | } 75 | else if(p->state==DOWN || m->buttons&7){ 76 | lp->sel=(m->xy.y-ul.y)/font->height+lp->lo; 77 | if(m->buttons&7){ 78 | lp->buttons=m->buttons; 79 | p->state=DOWN; 80 | } 81 | else{ 82 | hitme=1; 83 | p->state=UP; 84 | } 85 | } 86 | if(oldsel!=lp->sel){ 87 | pl_listsel(p, oldsel, 0); 88 | pl_listsel(p, lp->sel, 1); 89 | } 90 | if(hitme && 0<=lp->sel && lp->sellen && lp->hit) 91 | lp->hit(p, lp->buttons, lp->sel); 92 | return 0; 93 | } 94 | void pl_scrolllist(Panel *p, int dir, int buttons, int val, int len){ 95 | Point ul, size; 96 | int nlist, oldlo, hi, nline, y; 97 | List *lp; 98 | Rectangle r; 99 | lp=p->data; 100 | ul=p->r.min; 101 | size=subpt(p->r.max, p->r.min); 102 | pl_interior(p->state, &ul, &size); 103 | nlist=size.y/font->height; 104 | oldlo=lp->lo; 105 | if(dir==VERT) switch(buttons){ 106 | case 1: lp->lo-=nlist*val/len; break; 107 | case 2: lp->lo=lp->len*val/len; break; 108 | case 4: lp->lo+=nlist*val/len; break; 109 | } 110 | if(lp->lo<0) lp->lo=0; 111 | if(lp->lo>=lp->len) lp->lo=lp->len-1; 112 | if(lp->lo==oldlo) return; 113 | p->scr.pos.y=lp->lo; 114 | r=lp->listr; 115 | nline=(r.max.y-r.min.y)/font->height; 116 | hi=lp->lo+nline; 117 | if(hi<=oldlo || lp->lo>=oldlo+nline){ 118 | pl_box(p->b, r, PASSIVE); 119 | pl_liststrings(p, lp->lo, hi, r); 120 | } 121 | else if(lp->lolo)*font->height; 123 | pl_cpy(p->b, Pt(r.min.x, y), 124 | Rect(r.min.x, r.min.y, r.max.x, r.min.y+(hi-oldlo)*font->height)); 125 | r.max.y=y; 126 | pl_box(p->b, r, PASSIVE); 127 | pl_liststrings(p, lp->lo, oldlo, r); 128 | } 129 | else{ 130 | pl_cpy(p->b, r.min, Rect(r.min.x, r.min.y+(lp->lo-oldlo)*font->height, 131 | r.max.x, r.max.y)); 132 | r.min.y=r.min.y+(oldlo+nline-lp->lo)*font->height; 133 | pl_box(p->b, r, PASSIVE); 134 | pl_liststrings(p, oldlo+nline, hi, r); 135 | } 136 | } 137 | void pl_typelist(Panel *g, Rune c){ 138 | USED(g, c); 139 | } 140 | Point pl_getsizelist(Panel *p, Point children){ 141 | USED(children); 142 | return pl_boxsize(((List *)p->data)->minsize, p->state); 143 | } 144 | void pl_childspacelist(Panel *g, Point *ul, Point *size){ 145 | USED(g, ul, size); 146 | } 147 | void plinitlist(Panel *v, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){ 148 | List *lp; 149 | int wid, max; 150 | char *str; 151 | lp=v->data; 152 | v->flags=flags|LEAF; 153 | v->state=UP; 154 | v->draw=pl_drawlist; 155 | v->hit=pl_hitlist; 156 | v->type=pl_typelist; 157 | v->getsize=pl_getsizelist; 158 | v->childspace=pl_childspacelist; 159 | lp->gen=gen; 160 | lp->hit=hit; 161 | max=0; 162 | for(lp->len=0;str=gen(v, lp->len);lp->len++){ 163 | wid=stringwidth(font, str); 164 | if(wid>max) max=wid; 165 | } 166 | if(flags&(FILLX|EXPAND)){ 167 | for(lp->len=0;gen(v, lp->len);lp->len++); 168 | lp->minsize=Pt(0, nlist*font->height); 169 | } 170 | else{ 171 | max=0; 172 | for(lp->len=0;str=gen(v, lp->len);lp->len++){ 173 | wid=stringwidth(font, str); 174 | if(wid>max) max=wid; 175 | } 176 | lp->minsize=Pt(max, nlist*font->height); 177 | } 178 | lp->sel=-1; 179 | lp->lo=0; 180 | v->scroll=pl_scrolllist; 181 | v->scr.pos=Pt(0,0); 182 | v->scr.size=Pt(0,lp->len); 183 | v->kind="list"; 184 | } 185 | Panel *pllist(Panel *parent, int flags, char *(*gen)(Panel *, int), int nlist, void (*hit)(Panel *, int, int)){ 186 | Panel *v; 187 | v=pl_newpanel(parent, sizeof(List)); 188 | plinitlist(v, flags, gen, nlist, hit); 189 | return v; 190 | } 191 | -------------------------------------------------------------------------------- /libpanel/textview.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Fonted text viewer, calls out to code in rtext.c 3 | * 4 | * Should redo this to copy the already-visible parts on scrolling & only 5 | * update the newly appearing stuff -- then the offscreen assembly bitmap can go away. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "pldefs.h" 13 | 14 | typedef struct Textview Textview; 15 | struct Textview{ 16 | void (*hit)(Panel *, int, Rtext *); /* call back to user on hit */ 17 | Rtext *text; /* text */ 18 | Point offs; /* offset of left/top of screen */ 19 | Rtext *hitword; /* text to hilite */ 20 | Rtext *hitfirst; /* first word in range select */ 21 | int twid; /* text width (visible) */ 22 | int thgt; /* text height (total) */ 23 | int maxwid; /* width of longest line */ 24 | Point minsize; /* smallest acceptible window size */ 25 | int buttons; 26 | }; 27 | 28 | void pl_setscrpos(Panel *p, Textview *tp, Rectangle r){ 29 | Panel *sb; 30 | int lo, hi; 31 | 32 | lo=tp->offs.y; 33 | hi=lo+r.max.y-r.min.y; /* wrong? */ 34 | sb=p->yscroller; 35 | if(sb && sb->setscrollbar) 36 | sb->setscrollbar(sb, lo, hi, tp->thgt); 37 | lo=tp->offs.x; 38 | hi=lo+r.max.x-r.min.x; 39 | sb=p->xscroller; 40 | if(sb && sb->setscrollbar) 41 | sb->setscrollbar(sb, lo, hi, tp->maxwid); 42 | } 43 | void pl_drawtextview(Panel *p){ 44 | int twid; 45 | Rectangle r; 46 | Textview *tp; 47 | Point size; 48 | 49 | tp=p->data; 50 | r=pl_outline(p->b, p->r, UP); 51 | twid=r.max.x-r.min.x; 52 | if(twid!=tp->twid){ 53 | tp->twid=twid; 54 | size=pl_rtfmt(tp->text, tp->twid); 55 | p->scr.size.x=tp->maxwid=size.x; 56 | p->scr.size.y=tp->thgt=size.y; 57 | } 58 | p->scr.pos = tp->offs; 59 | pl_rtdraw(p->b, r, tp->text, tp->offs); 60 | pl_setscrpos(p, tp, r); 61 | } 62 | /* 63 | * If t is a panel word, pass the mouse event on to it 64 | */ 65 | void pl_passon(Rtext *t, Mouse *m){ 66 | if(t && t->b==0 && t->p!=0) 67 | plmouse(t->p, m); 68 | } 69 | int pl_hittextview(Panel *p, Mouse *m){ 70 | Rtext *oldhitword, *oldhitfirst; 71 | int hitme, oldstate; 72 | Point ul, size; 73 | Textview *tp; 74 | 75 | tp=p->data; 76 | hitme=0; 77 | oldstate=p->state; 78 | oldhitword=tp->hitword; 79 | oldhitfirst=tp->hitfirst; 80 | if(oldhitword==oldhitfirst) 81 | pl_passon(oldhitword, m); 82 | if(m->buttons&OUT) 83 | p->state=UP; 84 | else if(m->buttons&7){ 85 | p->state=DOWN; 86 | tp->buttons=m->buttons; 87 | if(oldhitword==0 || oldhitword->p==0 || (oldhitword->p->flags&REMOUSE)==0){ 88 | ul=p->r.min; 89 | size=subpt(p->r.max, p->r.min); 90 | pl_interior(p->state, &ul, &size); 91 | tp->hitword=pl_rthit(tp->text, tp->offs, m->xy, ul); 92 | if(tp->hitword==0) 93 | if(oldhitword!=0 && oldstate==DOWN) 94 | tp->hitword=oldhitword; 95 | else 96 | tp->hitfirst=0; 97 | if(tp->hitword!=0 && oldstate!=DOWN) 98 | tp->hitfirst=tp->hitword; 99 | } 100 | } 101 | else{ 102 | if(p->state==DOWN) hitme=1; 103 | p->state=UP; 104 | } 105 | if(tp->hitfirst!=oldhitfirst || tp->hitword!=oldhitword){ 106 | plrtseltext(tp->text, tp->hitword, tp->hitfirst); 107 | pl_drawtextview(p); 108 | if(tp->hitword==tp->hitfirst) 109 | pl_passon(tp->hitword, m); 110 | } 111 | if(hitme && tp->hit && tp->hitword!=0 && tp->hitword==tp->hitfirst){ 112 | plrtseltext(tp->text, 0, 0); 113 | pl_drawtextview(p); 114 | tp->hit(p, tp->buttons, tp->hitword); 115 | tp->hitword=0; 116 | tp->hitfirst=0; 117 | } 118 | return 0; 119 | } 120 | void pl_scrolltextview(Panel *p, int dir, int buttons, int num, int den){ 121 | int xoffs, yoffs; 122 | Point ul, size; 123 | Textview *tp; 124 | Rectangle r; 125 | 126 | tp=p->data; 127 | ul=p->r.min; 128 | size=subpt(p->r.max, p->r.min); 129 | pl_interior(p->state, &ul, &size); 130 | if(dir==VERT){ 131 | switch(buttons){ 132 | default: 133 | SET(yoffs); 134 | break; 135 | case 1: /* left -- top moves to pointer */ 136 | yoffs=(vlong)tp->offs.y-num*size.y/den; 137 | if(yoffs<0) yoffs=0; 138 | break; 139 | case 2: /* middle -- absolute index of file */ 140 | yoffs=(vlong)tp->thgt*num/den; 141 | break; 142 | case 4: /* right -- line pointed at moves to top */ 143 | yoffs=tp->offs.y+(vlong)num*size.y/den; 144 | if(yoffs>tp->thgt) yoffs=tp->thgt; 145 | break; 146 | } 147 | if(yoffs!=tp->offs.y){ 148 | r=pl_outline(p->b, p->r, p->state); 149 | pl_rtredraw(p->b, r, tp->text, 150 | Pt(tp->offs.x, yoffs), tp->offs, dir); 151 | p->scr.pos.y=tp->offs.y=yoffs; 152 | pl_setscrpos(p, tp, r); 153 | } 154 | }else{ /* dir==HORIZ */ 155 | switch(buttons){ 156 | default: 157 | SET(xoffs); 158 | break; 159 | case 1: /* left */ 160 | xoffs=(vlong)tp->offs.x-num*size.x/den; 161 | if(xoffs<0) xoffs=0; 162 | break; 163 | case 2: /* middle */ 164 | xoffs=(vlong)tp->maxwid*num/den; 165 | break; 166 | case 4: /* right */ 167 | xoffs=tp->offs.x+(vlong)num*size.x/den; 168 | if(xoffs>tp->maxwid) xoffs=tp->maxwid; 169 | break; 170 | } 171 | if(xoffs!=tp->offs.x){ 172 | r=pl_outline(p->b, p->r, p->state); 173 | pl_rtredraw(p->b, r, tp->text, 174 | Pt(xoffs, tp->offs.y), tp->offs, dir); 175 | p->scr.pos.x=tp->offs.x=xoffs; 176 | pl_setscrpos(p, tp, r); 177 | } 178 | } 179 | } 180 | void pl_typetextview(Panel *g, Rune c){ 181 | USED(g, c); 182 | } 183 | Point pl_getsizetextview(Panel *p, Point children){ 184 | USED(children); 185 | return pl_boxsize(((Textview *)p->data)->minsize, p->state); 186 | } 187 | void pl_childspacetextview(Panel *g, Point *ul, Point *size){ 188 | USED(g, ul, size); 189 | } 190 | /* 191 | * Priority depends on what thing inside the panel we're pointing at. 192 | */ 193 | int pl_pritextview(Panel *p, Point xy){ 194 | Point ul, size; 195 | Textview *tp; 196 | Rtext *h; 197 | tp=p->data; 198 | ul=p->r.min; 199 | size=subpt(p->r.max, p->r.min); 200 | pl_interior(p->state, &ul, &size); 201 | h=pl_rthit(tp->text, tp->offs, xy, ul); 202 | if(h && h->b==0 && h->p!=0){ 203 | p=pl_ptinpanel(xy, h->p); 204 | if(p) return p->pri(p, xy); 205 | } 206 | return PRI_NORMAL; 207 | } 208 | 209 | char* pl_snarftextview(Panel *p){ 210 | return plrtsnarftext(((Textview *)p->data)->text); 211 | } 212 | 213 | void plinittextview(Panel *v, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){ 214 | Textview *tp; 215 | tp=v->data; 216 | v->flags=flags|LEAF; 217 | v->state=UP; 218 | v->draw=pl_drawtextview; 219 | v->hit=pl_hittextview; 220 | v->type=pl_typetextview; 221 | v->getsize=pl_getsizetextview; 222 | v->childspace=pl_childspacetextview; 223 | v->kind="textview"; 224 | v->pri=pl_pritextview; 225 | tp->hit=hit; 226 | tp->minsize=minsize; 227 | tp->text=t; 228 | tp->offs=ZP; 229 | tp->hitfirst=0; 230 | tp->hitword=0; 231 | v->scroll=pl_scrolltextview; 232 | v->snarf=pl_snarftextview; 233 | tp->twid=-1; 234 | tp->maxwid=0; 235 | v->scr.pos=Pt(0,0); 236 | v->scr.size=Pt(0,1); 237 | } 238 | Panel *pltextview(Panel *parent, int flags, Point minsize, Rtext *t, void (*hit)(Panel *, int, Rtext *)){ 239 | Panel *v; 240 | v=pl_newpanel(parent, sizeof(Textview)); 241 | plinittextview(v, flags, minsize, t, hit); 242 | return v; 243 | } 244 | int plgetpostextview(Panel *p){ 245 | return ((Textview *)p->data)->offs.y; 246 | } 247 | void plsetpostextview(Panel *p, int yoffs){ 248 | ((Textview *)p->data)->offs.y=yoffs; 249 | pldraw(p, p->b); 250 | } 251 | -------------------------------------------------------------------------------- /libpanel/edit.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Interface includes: 3 | * void plescroll(Panel *p, int top); 4 | * move the given character position onto the top line 5 | * void plegetsel(Panel *p, int *sel0, int *sel1); 6 | * read the selection back 7 | * int plelen(Panel *p); 8 | * read the length of the text back 9 | * Rune *pleget(Panel *p); 10 | * get a pointer to the text 11 | * void plesel(Panel *p, int sel0, int sel1); 12 | * set the selection -- adjusts hiliting 13 | * void plepaste(Panel *p, Rune *text, int ntext); 14 | * replace the selection with the given text 15 | */ 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "pldefs.h" 22 | #include 23 | 24 | typedef struct Edit Edit; 25 | struct Edit{ 26 | Point minsize; 27 | void (*hit)(Panel *); 28 | int sel0, sel1; 29 | Textwin *t; 30 | Rune *text; 31 | int ntext; 32 | }; 33 | void pl_drawedit(Panel *p){ 34 | Edit *ep; 35 | Panel *sb; 36 | ep=p->data; 37 | if(ep->t==0){ 38 | ep->t=twnew(p->b, font, ep->text, ep->ntext); 39 | if(ep->t==0){ 40 | fprint(2, "pl_drawedit: can't allocate\n"); 41 | exits("no mem"); 42 | } 43 | } 44 | ep->t->b=p->b; 45 | twreshape(ep->t, p->r); 46 | twhilite(ep->t, ep->sel0, ep->sel1, 1); 47 | sb=p->yscroller; 48 | if(sb && sb->setscrollbar) 49 | sb->setscrollbar(sb, ep->t->top, ep->t->bot, ep->t->etext-ep->t->text); 50 | } 51 | 52 | char *pl_snarfedit(Panel *p){ 53 | int s0, s1; 54 | Rune *t; 55 | t=pleget(p); 56 | plegetsel(p, &s0, &s1); 57 | if(t==0 || s0>=s1) 58 | return nil; 59 | return smprint("%.*S", s1-s0, t+s0); 60 | } 61 | void pl_pasteedit(Panel *p, char *s){ 62 | Rune *t; 63 | if(t=runesmprint("%s", s)){ 64 | plepaste(p, t, runestrlen(t)); 65 | free(t); 66 | } 67 | } 68 | 69 | /* 70 | * Should do double-clicks: 71 | * If ep->sel0==ep->sel1 on entry and the 72 | * call to twselect returns the same selection, then 73 | * expand selections (| marks possible selection points, ... is expanded selection) 74 | * <|...|> <> must nest 75 | * (|...|) () must nest 76 | * [|...|] [] must nest 77 | * {|...|} {} must nest 78 | * '|...|' no ' in ... 79 | * "|...|" no " in ... 80 | * \n|...|\n either newline may be the corresponding end of text 81 | * include the trailing newline in the selection 82 | * ...|I... I and ... are characters satisfying pl_idchar(I) 83 | * ...I| 84 | */ 85 | int pl_hitedit(Panel *p, Mouse *m){ 86 | Edit *ep; 87 | ep=p->data; 88 | if(ep->t && m->buttons&1){ 89 | plgrabkb(p); 90 | ep->t->b=p->b; 91 | twhilite(ep->t, ep->sel0, ep->sel1, 0); 92 | twselect(ep->t, m); 93 | ep->sel0=ep->t->sel0; 94 | ep->sel1=ep->t->sel1; 95 | if((m->buttons&7)==3){ 96 | plsnarf(p); 97 | plepaste(p, 0, 0); /* cut */ 98 | } 99 | else if((m->buttons&7)==5) 100 | plpaste(p); 101 | else if(ep->hit) 102 | (*ep->hit)(p); 103 | } 104 | return 0; 105 | } 106 | void pl_scrolledit(Panel *p, int dir, int buttons, int num, int den){ 107 | Edit *ep; 108 | Textwin *t; 109 | Panel *sb; 110 | int index, nline; 111 | if(dir!=VERT) return; 112 | ep=p->data; 113 | t=ep->t; 114 | if(t==0) return; 115 | t->b=p->b; 116 | switch(buttons){ 117 | default: 118 | return; 119 | case 1: /* top line moves to mouse position */ 120 | nline=(t->r.max.y-t->r.min.y)/t->hgt*num/den; 121 | index=t->top; 122 | while(index!=0 && nline!=0) 123 | if(t->text[--index]=='\n') --nline; 124 | break; 125 | case 2: /* absolute */ 126 | index=(t->etext-t->text)*num/den; 127 | break; 128 | case 4: /* mouse points at new top line */ 129 | index=twpt2rune(t, 130 | Pt(t->r.min.x, t->r.min.y+(t->r.max.y-t->r.min.y)*num/den)); 131 | break; 132 | } 133 | while(index!=0 && t->text[index-1]!='\n') --index; 134 | if(index!=t->top){ 135 | twhilite(ep->t, ep->sel0, ep->sel1, 0); 136 | twscroll(t, index); 137 | p->scr.pos.y=t->top; 138 | twhilite(ep->t, ep->sel0, ep->sel1, 1); 139 | sb=p->yscroller; 140 | if(sb && sb->setscrollbar) 141 | sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text); 142 | } 143 | } 144 | void pl_typeedit(Panel *p, Rune c){ 145 | Edit *ep; 146 | Textwin *t; 147 | int bot, scrolled; 148 | Panel *sb; 149 | ep=p->data; 150 | t=ep->t; 151 | if(t==0) return; 152 | t->b=p->b; 153 | twhilite(t, ep->sel0, ep->sel1, 0); 154 | switch(c){ 155 | case Kesc: 156 | plsnarf(p); 157 | plepaste(p, 0, 0); /* cut */ 158 | break; 159 | case Kdel: /* clear */ 160 | ep->sel0=0; 161 | ep->sel1=plelen(p); 162 | plepaste(p, 0, 0); /* cut */ 163 | break; 164 | case Kbs: /* ^H: erase character */ 165 | if(ep->sel0!=0) --ep->sel0; 166 | twreplace(t, ep->sel0, ep->sel1, 0, 0); 167 | break; 168 | case Knack: /* ^U: erase line */ 169 | while(ep->sel0!=0 && t->text[ep->sel0-1]!='\n') --ep->sel0; 170 | twreplace(t, ep->sel0, ep->sel1, 0, 0); 171 | break; 172 | case Ketb: /* ^W: erase word */ 173 | while(ep->sel0!=0 && !pl_idchar(t->text[ep->sel0-1])) --ep->sel0; 174 | while(ep->sel0!=0 && pl_idchar(t->text[ep->sel0-1])) --ep->sel0; 175 | twreplace(t, ep->sel0, ep->sel1, 0, 0); 176 | break; 177 | default: 178 | if((c & 0xFF00) == KF || (c & 0xFF00) == Spec) 179 | break; 180 | twreplace(t, ep->sel0, ep->sel1, &c, 1); 181 | ++ep->sel0; 182 | break; 183 | } 184 | ep->sel1=ep->sel0; 185 | /* 186 | * Scroll up until ep->sel0 is above t->bot. 187 | */ 188 | scrolled=0; 189 | do{ 190 | bot=t->bot; 191 | if(ep->sel0<=bot) break; 192 | twscroll(t, twpt2rune(t, Pt(t->r.min.x, t->r.min.y+font->height))); 193 | scrolled++; 194 | }while(bot!=t->bot); 195 | if(scrolled){ 196 | sb=p->yscroller; 197 | if(sb && sb->setscrollbar) 198 | sb->setscrollbar(sb, t->top, t->bot, t->etext-t->text); 199 | } 200 | twhilite(t, ep->sel0, ep->sel1, 1); 201 | } 202 | Point pl_getsizeedit(Panel *p, Point children){ 203 | USED(children); 204 | return pl_boxsize(((Edit *)p->data)->minsize, p->state); 205 | } 206 | void pl_childspaceedit(Panel *g, Point *ul, Point *size){ 207 | USED(g, ul, size); 208 | } 209 | void pl_freeedit(Panel *p){ 210 | Edit *ep; 211 | ep=p->data; 212 | if(ep->t) twfree(ep->t); 213 | ep->t=0; 214 | } 215 | void plinitedit(Panel *v, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){ 216 | Edit *ep; 217 | ep=v->data; 218 | v->flags=flags|LEAF; 219 | v->state=UP; 220 | v->draw=pl_drawedit; 221 | v->hit=pl_hitedit; 222 | v->type=pl_typeedit; 223 | v->getsize=pl_getsizeedit; 224 | v->childspace=pl_childspaceedit; 225 | v->free=pl_freeedit; 226 | v->snarf=pl_snarfedit; 227 | v->paste=pl_pasteedit; 228 | v->kind="edit"; 229 | ep->hit=hit; 230 | ep->minsize=minsize; 231 | ep->text=text; 232 | ep->ntext=ntext; 233 | if(ep->t!=0) twfree(ep->t); 234 | ep->t=0; 235 | ep->sel0=-1; 236 | ep->sel1=-1; 237 | v->scroll=pl_scrolledit; 238 | v->scr.pos=Pt(0,0); 239 | v->scr.size=Pt(ntext,0); 240 | } 241 | Panel *pledit(Panel *parent, int flags, Point minsize, Rune *text, int ntext, void (*hit)(Panel *)){ 242 | Panel *v; 243 | v=pl_newpanel(parent, sizeof(Edit)); 244 | ((Edit *)v->data)->t=0; 245 | plinitedit(v, flags, minsize, text, ntext, hit); 246 | return v; 247 | } 248 | void plescroll(Panel *p, int top){ 249 | Textwin *t; 250 | t=((Edit*)p->data)->t; 251 | if(t) twscroll(t, top); 252 | } 253 | void plegetsel(Panel *p, int *sel0, int *sel1){ 254 | Edit *ep; 255 | ep=p->data; 256 | *sel0=ep->sel0; 257 | *sel1=ep->sel1; 258 | } 259 | int plelen(Panel *p){ 260 | Textwin *t; 261 | t=((Edit*)p->data)->t; 262 | if(t==0) return 0; 263 | return t->etext-t->text; 264 | } 265 | Rune *pleget(Panel *p){ 266 | Textwin *t; 267 | t=((Edit*)p->data)->t; 268 | if(t==0) return 0; 269 | return t->text; 270 | } 271 | void plesel(Panel *p, int sel0, int sel1){ 272 | Edit *ep; 273 | ep=p->data; 274 | if(ep->t==0) return; 275 | ep->t->b=p->b; 276 | twhilite(ep->t, ep->sel0, ep->sel1, 0); 277 | ep->sel0=sel0; 278 | ep->sel1=sel1; 279 | twhilite(ep->t, ep->sel0, ep->sel1, 1); 280 | } 281 | void plepaste(Panel *p, Rune *text, int ntext){ 282 | Edit *ep; 283 | ep=p->data; 284 | if(ep->t==0) return; 285 | ep->t->b=p->b; 286 | twhilite(ep->t, ep->sel0, ep->sel1, 0); 287 | twreplace(ep->t, ep->sel0, ep->sel1, text, ntext); 288 | ep->sel1=ep->sel0+ntext; 289 | twhilite(ep->t, ep->sel0, ep->sel1, 1); 290 | p->scr.size.y=ep->t->etext-ep->t->text; 291 | p->scr.pos.y=ep->t->top; 292 | } 293 | void plemove(Panel *p, Point d){ 294 | Edit *ep; 295 | ep=p->data; 296 | if(ep->t && !eqpt(d, Pt(0,0))) twmove(ep->t, d); 297 | } 298 | -------------------------------------------------------------------------------- /libpanel/panel.h: -------------------------------------------------------------------------------- 1 | //#pragma src "/sys/src/libpanel" 2 | //#pragma lib "libpanel.a" 3 | typedef struct Scroll Scroll; 4 | typedef struct Panel Panel; /* a Graphical User Interface element */ 5 | typedef struct Rtext Rtext; /* formattable text */ 6 | typedef void Icon; /* Always used as Icon * -- Image or char */ 7 | typedef struct Idol Idol; /* A picture/text combo */ 8 | struct Scroll{ 9 | Point pos, size; 10 | }; 11 | struct Rtext{ 12 | int flags; /* responds to hits? text selection? */ 13 | void *user; /* user data */ 14 | int space; /* how much space before, if no break */ 15 | int indent; /* how much space before, after a break */ 16 | int voff; /* vertical offset (for subscripts and superscripts) */ 17 | Image *b; /* what to display, if nonzero */ 18 | Panel *p; /* what to display, if nonzero and b==0 */ 19 | Font *font; /* font in which to draw text */ 20 | char *text; /* what to display, if b==0 and p==0 */ 21 | Rtext *next; /* next piece */ 22 | /* private below */ 23 | Rtext *nextline; /* links line to line */ 24 | Rtext *last; /* last, for append */ 25 | Rectangle r; /* where to draw, if origin were Pt(0,0) */ 26 | int topy; /* y coord of top of line */ 27 | int wid; /* not including space */ 28 | }; 29 | struct Panel{ 30 | Point ipad, pad; /* extra space inside and outside */ 31 | Point fixedsize; /* size of Panel, if FIXED */ 32 | int user; /* available for user */ 33 | void *userp; /* available for user */ 34 | Rectangle r; /* where the Panel goes */ 35 | /* private below */ 36 | Panel *next; /* It's a list! */ 37 | Panel *child, *echild, *parent; /* No, it's a tree! */ 38 | Image *b; /* where we're drawn */ 39 | int flags; /* position flags, see below */ 40 | char *kind; /* what kind of panel? */ 41 | int state; /* for hitting & drawing purposes */ 42 | Point size; /* space for this Panel */ 43 | Point sizereq; /* size requested by this Panel */ 44 | Point childreq; /* total size needed by children */ 45 | Panel *lastmouse; /* who got the last mouse event? */ 46 | Panel *scrollee; /* pointer to scrolled window */ 47 | Panel *xscroller, *yscroller; /* pointers to scroll bars */ 48 | Scroll scr; /* scroll data */ 49 | void *data; /* kind-specific data */ 50 | void (*draw)(Panel *); /* draw panel and children */ 51 | int (*pri)(Panel *, Point); /* priority for hitting */ 52 | int (*hit)(Panel *, Mouse *); /* process mouse event */ 53 | void (*type)(Panel *, Rune); /* process keyboard event */ 54 | Point (*getsize)(Panel *, Point); /* return size, given child size */ 55 | void (*childspace)(Panel *, Point *, Point *); /* child ul & size given our size */ 56 | void (*scroll)(Panel *, int, int, int, int); /* scroll bar to scrollee */ 57 | void (*setscrollbar)(Panel *, int, int, int); /* scrollee to scroll bar */ 58 | void (*free)(Panel *); /* free fields of data when done */ 59 | char* (*snarf)(Panel *); /* snarf text from panel */ 60 | void (*paste)(Panel *, char *); /* paste text into panel */ 61 | }; 62 | /* 63 | * Panel flags 64 | */ 65 | #define PACK 0x0007 /* which side of the parent is the Panel attached to? */ 66 | #define PACKN 0x0000 67 | #define PACKE 0x0001 68 | #define PACKS 0x0002 69 | #define PACKW 0x0003 70 | #define PACKCEN 0x0004 /* only used by pulldown */ 71 | #define FILLX 0x0008 /* grow horizontally to fill the available space */ 72 | #define FILLY 0x0010 /* grow vertically to fill the available space */ 73 | #define PLACE 0x01e0 /* which side of its space should the Panel adhere to? */ 74 | #define PLACECEN 0x0000 75 | #define PLACES 0x0020 76 | #define PLACEE 0x0040 77 | #define PLACEW 0x0060 78 | #define PLACEN 0x0080 79 | #define PLACENE 0x00a0 80 | #define PLACENW 0x00c0 81 | #define PLACESE 0x00e0 82 | #define PLACESW 0x0100 83 | #define EXPAND 0x0200 /* use up all extra space in the parent */ 84 | #define FIXED 0x0c00 /* don't pass children's size requests through to parent */ 85 | #define FIXEDX 0x0400 86 | #define FIXEDY 0x0800 87 | #define MAXX 0x1000 /* make x size as big as biggest sibling's */ 88 | #define MAXY 0x2000 /* make y size as big as biggest sibling's */ 89 | #define BITMAP 0x4000 /* text argument is a bitmap, not a string */ 90 | #define NOBORDER 0x8000 91 | /* pldefs.h flags 0x08000-0x40000 */ 92 | #define IGNORE 0x080000 /* ignore this panel totally */ 93 | #define USERFL 0x100000 /* start of user flag */ 94 | 95 | /* 96 | * An extra bit in Mouse.buttons 97 | */ 98 | #define OUT 8 /* Mouse.buttons bit, set when mouse leaves Panel */ 99 | /* 100 | * Priorities 101 | */ 102 | #define PRI_NORMAL 0 /* ordinary panels */ 103 | #define PRI_POPUP 1 /* popup menus */ 104 | #define PRI_SCROLLBAR 2 /* scroll bars */ 105 | 106 | /* Rtext.flags */ 107 | #define PL_HOT 1 108 | #define PL_SEL 2 109 | #define PL_STR 4 110 | #define PL_HEAD 8 111 | 112 | Panel *plkbfocus; /* the panel in keyboard focus */ 113 | 114 | int plinit(int); /* initialization */ 115 | void plpack(Panel *, Rectangle); /* figure out where to put the Panel & children */ 116 | void plmove(Panel *, Point); /* move an already-packed panel to a new location */ 117 | void pldraw(Panel *, Image *); /* display the panel on the bitmap */ 118 | void plfree(Panel *); /* give back space */ 119 | void plgrabkb(Panel *); /* this Panel should receive keyboard events */ 120 | void plkeyboard(Rune); /* send a keyboard event to the appropriate Panel */ 121 | void plmouse(Panel *, Mouse *); /* send a Mouse event to a Panel tree */ 122 | void plscroll(Panel *, Panel *, Panel *); /* link up scroll bars */ 123 | char *plentryval(Panel *); /* entry delivers its value */ 124 | void plsetbutton(Panel *, int); /* set or clear the mark on a button */ 125 | void plsetslider(Panel *, int, int); /* set the value of a slider */ 126 | Rune *pleget(Panel *); /* get the text from an edit window */ 127 | int plelen(Panel *); /* get the length of the text from an edit window */ 128 | void plegetsel(Panel *, int *, int *); /* get the selection from an edit window */ 129 | void plepaste(Panel *, Rune *, int); /* paste in an edit window */ 130 | void plesel(Panel *, int, int); /* set the selection in an edit window */ 131 | void plescroll(Panel *, int); /* scroll an edit window */ 132 | Scroll plgetscroll(Panel *); /* get scrolling information from panel */ 133 | void plsetscroll(Panel *, Scroll); /* set scrolling information */ 134 | void plplacelabel(Panel *, int); /* label placement */ 135 | 136 | /* 137 | * Panel creation & reinitialization functions 138 | */ 139 | Panel *plbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int)); 140 | Panel *plcanvas(Panel *pl, int, void (*)(Panel *), void (*)(Panel *pl, Mouse *)); 141 | Panel *plcheckbutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int)); 142 | Panel *pledit(Panel *, int, Point, Rune *, int, void (*)(Panel *)); 143 | Panel *plentry(Panel *pl, int, int, char *, void (*)(Panel *pl, char *)); 144 | Panel *plframe(Panel *pl, int); 145 | Panel *plgroup(Panel *pl, int); 146 | Panel *plidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*)); 147 | Panel *pllabel(Panel *pl, int, Icon *); 148 | Panel *pllist(Panel *pl, int, char *(*)(Panel *, int), int, void(*)(Panel *pl, int, int)); 149 | Panel *plmenu(Panel *pl, int, Icon **, int, void (*)(int, int)); 150 | Panel *plmenubar(Panel *pl, int, int, Icon *, Panel *pl, Icon *, ...); 151 | Panel *plmessage(Panel *pl, int, int, char *); 152 | Panel *plpopup(Panel *pl, int, Panel *pl, Panel *pl, Panel *pl); 153 | Panel *plpulldown(Panel *pl, int, Icon *, Panel *pl, int); 154 | Panel *plradiobutton(Panel *pl, int, Icon *, void (*)(Panel *pl, int, int)); 155 | Panel *plscrollbar(Panel *plparent, int flags); 156 | Panel *plslider(Panel *pl, int, Point, void(*)(Panel *pl, int, int, int)); 157 | Panel *pltextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *)); 158 | void plinitbutton(Panel *, int, Icon *, void (*)(Panel *, int)); 159 | void plinitcanvas(Panel *, int, void (*)(Panel *), void (*)(Panel *, Mouse *)); 160 | void plinitcheckbutton(Panel *, int, Icon *, void (*)(Panel *, int, int)); 161 | void plinitedit(Panel *, int, Point, Rune *, int, void (*)(Panel *)); 162 | void plinitentry(Panel *, int, int, char *, void (*)(Panel *, char *)); 163 | void plinitframe(Panel *, int); 164 | void plinitgroup(Panel *, int); 165 | void plinitidollist(Panel*, int, Point, Font*, Idol*, void (*)(Panel*, int, void*)); 166 | void plinitlabel(Panel *, int, Icon *); 167 | void plinitlist(Panel *, int, char *(*)(Panel *, int), int, void(*)(Panel *, int, int)); 168 | void plinitmenu(Panel *, int, Icon **, int, void (*)(int, int)); 169 | void plinitmessage(Panel *, int, int, char *); 170 | void plinitpopup(Panel *, int, Panel *, Panel *, Panel *); 171 | void plinitpulldown(Panel *, int, Icon *, Panel *, int); 172 | void plinitradiobutton(Panel *, int, Icon *, void (*)(Panel *, int, int)); 173 | void plinitscrollbar(Panel *parent, int flags); 174 | void plinitslider(Panel *, int, Point, void(*)(Panel *, int, int, int)); 175 | void plinittextview(Panel *, int, Point, Rtext *, void (*)(Panel *, int, Rtext *)); 176 | /* 177 | * Rtext constructors & destructor 178 | */ 179 | Rtext *plrtstr(Rtext **, int, int, int, Font *, char *, int, void *); 180 | Rtext *plrtbitmap(Rtext **, int, int, int, Image *, int, void *); 181 | Rtext *plrtpanel(Rtext **, int, int, int, Panel *, void *); 182 | void plrtfree(Rtext *); 183 | void plrtseltext(Rtext *, Rtext *, Rtext *); 184 | char *plrtsnarftext(Rtext *); 185 | 186 | int plgetpostextview(Panel *); 187 | void plsetpostextview(Panel *, int); 188 | 189 | /* 190 | * Idols 191 | */ 192 | Idol *plmkidol(Idol**, Image*, Image*, char*, void*); 193 | void plfreeidol(Idol*); 194 | Point plidolsize(Idol*, Font*, int); 195 | void *plidollistgetsel(Panel*); 196 | 197 | /* 198 | * Snarf 199 | */ 200 | void plputsnarf(char *); 201 | char *plgetsnarf(void); 202 | void plsnarf(Panel *); /* snarf a panel */ 203 | void plpaste(Panel *); /* paste a panel */ 204 | -------------------------------------------------------------------------------- /libpanel/rtext.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Rich text with images. 3 | * Should there be an offset field, to do subscripts & kerning? 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "pldefs.h" 11 | #include "rtext.h" 12 | 13 | #define LEAD 4 /* extra space between lines */ 14 | #define BORD 2 /* extra border for images */ 15 | 16 | static Image *head, *blue; 17 | 18 | Rtext *pl_rtnew(Rtext **t, int space, int indent, int voff, Image *b, Panel *p, Font *f, char *s, int flags, void *user){ 19 | Rtext *new; 20 | new=pl_emalloc(sizeof(Rtext)); 21 | new->flags=flags; 22 | new->user=user; 23 | new->space=space; 24 | new->indent=indent; 25 | new->voff=voff; 26 | new->b=b; 27 | new->p=p; 28 | new->font=f; 29 | new->text=s; 30 | new->next=0; 31 | new->nextline=0; 32 | new->r=Rect(0,0,0,0); 33 | if(*t) 34 | (*t)->last->next=new; 35 | else 36 | *t=new; 37 | (*t)->last=new; 38 | return new; 39 | } 40 | Rtext *plrtpanel(Rtext **t, int space, int indent, int voff, Panel *p, void *user){ 41 | return pl_rtnew(t, space, indent, voff, 0, p, 0, 0, 1, user); 42 | } 43 | Rtext *plrtstr(Rtext **t, int space, int indent, int voff, Font *f, char *s, int flags, void *user){ 44 | return pl_rtnew(t, space, indent, voff, 0, 0, f, s, flags, user); 45 | } 46 | Rtext *plrtbitmap(Rtext **t, int space, int indent, int voff, Image *b, int flags, void *user){ 47 | return pl_rtnew(t, space, indent, voff, b, 0, 0, 0, flags, user); 48 | } 49 | void plrtfree(Rtext *t){ 50 | Rtext *next; 51 | while(t){ 52 | next=t->next; 53 | free(t); 54 | t=next; 55 | } 56 | } 57 | int pl_tabmin, pl_tabsize; 58 | void pltabsize(int min, int size){ 59 | pl_tabmin=min; 60 | pl_tabsize=size; 61 | } 62 | int pl_space(int space, int pos, int indent){ 63 | if(space>=0) return space; 64 | switch(PL_OP(space)){ 65 | default: 66 | return 0; 67 | case PL_TAB: 68 | return ((pos-indent+pl_tabmin)/pl_tabsize+PL_ARG(space))*pl_tabsize+indent-pos; 69 | } 70 | } 71 | /* 72 | * initialize rectangles & nextlines of text starting at t, 73 | * galley width is wid. Returns the total width/height of the text 74 | */ 75 | Point pl_rtfmt(Rtext *t, int wid){ 76 | Rtext *tp, *eline; 77 | int ascent, descent, x, space, a, d, w, topy, indent, maxwid; 78 | Point p; 79 | 80 | p=Pt(0,0); 81 | eline=t; 82 | maxwid=0; 83 | while(t){ 84 | ascent=0; 85 | descent=0; 86 | indent=space=pl_space(t->indent, 0, 0); 87 | x=0; 88 | tp=t; 89 | for(;;){ 90 | if(tp->b){ 91 | a=tp->b->r.max.y-tp->b->r.min.y+BORD; 92 | d=BORD; 93 | w=tp->b->repl?wid-x:tp->b->r.max.x-tp->b->r.min.x+BORD*2; 94 | } 95 | else if(tp->p){ 96 | /* what if plpack fails? */ 97 | plpack(tp->p, Rect(0,0,wid,wid)); 98 | plmove(tp->p, subpt(Pt(0,0), tp->p->r.min)); 99 | a=tp->p->r.max.y-tp->p->r.min.y; 100 | d=0; 101 | w=tp->p->r.max.x-tp->p->r.min.x; 102 | } 103 | else{ 104 | a=tp->font->ascent; 105 | d=tp->font->height-a; 106 | w=tp->wid=stringwidth(tp->font, tp->text); 107 | } 108 | a-=tp->voff,d+=tp->voff; 109 | if(x+w+space>wid) break; 110 | if(a>ascent) ascent=a; 111 | if(d>descent) descent=d; 112 | x+=w+space; 113 | tp=tp->next; 114 | if(tp==0){ 115 | eline=0; 116 | break; 117 | } 118 | space=pl_space(tp->space, x, indent); 119 | if(space) eline=tp; 120 | } 121 | if(eline==t){ /* No progress! Force fit the first block! */ 122 | if(tp==t){ 123 | if(a>ascent) ascent=a; 124 | if(d>descent) descent=d; 125 | eline=tp->next; 126 | }else 127 | eline=tp; 128 | } 129 | topy=p.y; 130 | p.y+=ascent; 131 | p.x=indent=pl_space(t->indent, 0, 0); 132 | for(;;){ 133 | t->topy=topy; 134 | t->r.min.x=p.x; 135 | p.y+=t->voff; 136 | if(t->b){ 137 | t->r.max.y=p.y+BORD; 138 | t->r.min.y=p.y-(t->b->r.max.y-t->b->r.min.y)-BORD; 139 | p.x+=t->b->repl?wid-p.x:(t->b->r.max.x-t->b->r.min.x)+BORD*2; 140 | } 141 | else if(t->p){ 142 | t->r.max.y=p.y; 143 | t->r.min.y=p.y-t->p->r.max.y; 144 | p.x+=t->p->r.max.x; 145 | } 146 | else{ 147 | t->r.min.y=p.y-t->font->ascent; 148 | t->r.max.y=t->r.min.y+t->font->height; 149 | p.x+=t->wid; 150 | } 151 | p.y-=t->voff; 152 | t->r.max.x=p.x; 153 | t->nextline=eline; 154 | t=t->next; 155 | if(t==eline) break; 156 | p.x+=pl_space(t->space, p.x, indent); 157 | } 158 | if(p.x>maxwid) maxwid=p.x; 159 | p.y+=descent+LEAD; 160 | } 161 | return Pt(maxwid, p.y); 162 | } 163 | 164 | /* 165 | * If we draw the text in a backup bitmap and copy it onto the screen, 166 | * the bitmap pointers in all the subpanels point to the wrong bitmap. 167 | * This code fixes them. 168 | */ 169 | void pl_stuffbitmap(Panel *p, Image *b){ 170 | p->b=b; 171 | for(p=p->child;p;p=p->next) 172 | pl_stuffbitmap(p, b); 173 | } 174 | 175 | void pl_rtdraw(Image *b, Rectangle r, Rtext *t, Point offs){ 176 | static Image *backup; 177 | Point lp, sp; 178 | Rectangle dr; 179 | Image *bb; 180 | 181 | bb = b; 182 | if(backup==0 || backup->chan!=b->chan || rectinrect(r, backup->r)==0){ 183 | freeimage(backup); 184 | backup=allocimage(display, bb->r, bb->chan, 0, DNofill); 185 | } 186 | if(backup) 187 | b=backup; 188 | pl_clr(b, r); 189 | lp=ZP; 190 | sp=ZP; 191 | offs=subpt(r.min, offs); 192 | for(;t;t=t->next) if(!eqrect(t->r, Rect(0,0,0,0))){ 193 | dr=rectaddpt(t->r, offs); 194 | if(dr.max.y>r.min.y 195 | && dr.min.yr.min.x 197 | && dr.min.xb){ 199 | draw(b, insetrect(dr, BORD), t->b, 0, t->b->r.min); 200 | if(t->flags&PL_HOT) border(b, dr, 1, display->black, ZP); 201 | if(t->flags&PL_STR) { 202 | line(b, Pt(dr.min.x, dr.min.y), Pt(dr.max.x, dr.max.y), 203 | Endsquare, Endsquare, 0, 204 | display->black, ZP); 205 | line(b, Pt(dr.min.x, dr.max.y), Pt(dr.max.x, dr.min.y), 206 | Endsquare, Endsquare, 0, 207 | display->black, ZP); 208 | } 209 | if(t->flags&PL_SEL) 210 | pl_highlight(b, dr); 211 | } 212 | else if(t->p){ 213 | plmove(t->p, subpt(dr.min, t->p->r.min)); 214 | pldraw(t->p, b); 215 | if(b!=bb) 216 | pl_stuffbitmap(t->p, bb); 217 | } 218 | else{ 219 | if(t->flags&PL_HEAD){ 220 | if(head==nil) 221 | head=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xAAAAAAFF); 222 | string(b, dr.min, head, ZP, t->font, t->text); 223 | }else if(t->flags&PL_HOT){ 224 | if(blue==nil) 225 | blue=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x0000FFFF); 226 | string(b, dr.min, blue, ZP, t->font, t->text); 227 | }else 228 | string(b, dr.min, display->black, ZP, t->font, t->text); 229 | if(t->flags&PL_SEL) 230 | pl_highlight(b, dr); 231 | if(t->flags&PL_STR){ 232 | int y = dr.max.y - t->font->height/2; 233 | if(sp.y != y) 234 | sp = Pt(dr.min.x, y); 235 | line(b, sp, Pt(dr.max.x, y), 236 | Endsquare, Endsquare, 0, 237 | display->black, ZP); 238 | sp = Pt(dr.max.x, y); 239 | } else 240 | sp = ZP; 241 | /*if(t->flags&PL_HOT){ 242 | int y = dr.max.y - 1; 243 | if(lp.y != y) 244 | lp = Pt(dr.min.x, y); 245 | line(b, lp, Pt(dr.max.x, y), 246 | Endsquare, Endsquare, 0, 247 | display->black, ZP); 248 | lp = Pt(dr.max.x, y); 249 | } else*/ 250 | lp = ZP; 251 | continue; 252 | } 253 | lp = ZP; 254 | sp = ZP; 255 | } 256 | } 257 | if(b!=bb) 258 | draw(bb, r, b, 0, r.min); 259 | } 260 | /* 261 | * Reposition text already drawn in the window. 262 | * We just move the pixels and update the positions of any 263 | * enclosed panels 264 | */ 265 | void pl_reposition(Rtext *t, Image *b, Point p, Rectangle r){ 266 | Point offs; 267 | pl_cpy(b, p, r); 268 | offs=subpt(p, r.min); 269 | for(;t;t=t->next) 270 | if(!eqrect(t->r, Rect(0,0,0,0)) && !t->b && t->p) 271 | plmove(t->p, offs); 272 | } 273 | /* 274 | * Rectangle r of Image b contains an image of Rtext t, offset by oldoffs. 275 | * Redraw the text to have offset yoffs. 276 | */ 277 | void pl_rtredraw(Image *b, Rectangle r, Rtext *t, Point offs, Point oldoffs, int dir){ 278 | int d, size; 279 | 280 | if(dir==VERT){ 281 | d=oldoffs.y-offs.y; 282 | size=r.max.y-r.min.y; 283 | if(d>=size || -d>=size) /* move more than screenful */ 284 | pl_rtdraw(b, r, t, offs); 285 | else if(d<0){ /* down */ 286 | pl_reposition(t, b, r.min, 287 | Rect(r.min.x, r.min.y-d, r.max.x, r.max.y)); 288 | pl_rtdraw(b, Rect(r.min.x, r.max.y+d, r.max.x, r.max.y), 289 | t, Pt(offs.x, offs.y+size+d)); 290 | } 291 | else if(d>0){ /* up */ 292 | pl_reposition(t, b, Pt(r.min.x, r.min.y+d), 293 | Rect(r.min.x, r.min.y, r.max.x, r.max.y-d)); 294 | pl_rtdraw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+d), 295 | t, offs); 296 | } 297 | }else{ /* dir==HORIZ */ 298 | d=oldoffs.x-offs.x; 299 | size=r.max.x-r.min.x; 300 | if(d>=size || -d>=size) /* move more than screenful */ 301 | pl_rtdraw(b, r, t, offs); 302 | else if(d<0){ /* right */ 303 | pl_reposition(t, b, r.min, 304 | Rect(r.min.x-d, r.min.y, r.max.x, r.max.y)); 305 | pl_rtdraw(b, Rect(r.max.x+d, r.min.y, r.max.x, r.max.y), 306 | t, Pt(offs.x+size+d, offs.y)); 307 | } 308 | else if(d>0){ /* left */ 309 | pl_reposition(t, b, Pt(r.min.x+d, r.min.y), 310 | Rect(r.min.x, r.min.y, r.max.x-d, r.max.y)); 311 | pl_rtdraw(b, Rect(r.min.x, r.min.y, r.min.x+d, r.max.y), 312 | t, offs); 313 | } 314 | } 315 | } 316 | Rtext *pl_rthit(Rtext *t, Point offs, Point p, Point ul){ 317 | Rectangle r; 318 | Point lp; 319 | if(t==0) return 0; 320 | p.x+=offs.x-ul.x; 321 | p.y+=offs.y-ul.y; 322 | while(t->nextline && t->nextline->topy<=p.y) t=t->nextline; 323 | lp=ZP; 324 | for(;t!=0;t=t->next){ 325 | if(t->topy>p.y) return 0; 326 | r = t->r; 327 | if((t->flags&PL_HOT) != 0 && t->b == nil && t->p == nil){ 328 | if(lp.y == r.max.y && lp.x < r.min.x) 329 | r.min.x=lp.x; 330 | lp=r.max; 331 | } else 332 | lp=ZP; 333 | if(ptinrect(p, r)) return t; 334 | } 335 | return 0; 336 | } 337 | 338 | void plrtseltext(Rtext *t, Rtext *s, Rtext *e){ 339 | while(t){ 340 | t->flags &= ~PL_SEL; 341 | t = t->next; 342 | } 343 | if(s==0 || e==0) 344 | return; 345 | for(t=s; t!=0 && t!=e; t=t->next) 346 | ; 347 | if(t==e){ 348 | for(t=s; t!=e; t=t->next) 349 | t->flags |= PL_SEL; 350 | }else{ 351 | for(t=e; t!=s; t=t->next) 352 | t->flags |= PL_SEL; 353 | } 354 | t->flags |= PL_SEL; 355 | } 356 | 357 | char *plrtsnarftext(Rtext *w){ 358 | char *b, *p, *e, *t; 359 | int n; 360 | 361 | b=p=e=0; 362 | for(; w; w = w->next){ 363 | if((w->flags&PL_SEL)==0 || w->text==0) 364 | continue; 365 | n = strlen(w->text)+64; 366 | if(p+n >= e){ 367 | n = (p+n+64)-b; 368 | t = pl_erealloc(b, n); 369 | p = t+(p-b); 370 | e = t+n; 371 | b = t; 372 | } 373 | if(w->space == 0) 374 | p += sprint(p, "%s", w->text); 375 | else if(w->space > 0) 376 | p += sprint(p, " %s", w->text); 377 | else if(PL_OP(w->space) == PL_TAB) 378 | p += sprint(p, "\t%s", w->text); 379 | if(w->nextline == w->next) 380 | p += sprint(p, "\n"); 381 | } 382 | return b; 383 | } 384 | -------------------------------------------------------------------------------- /libpanel/draw.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "pldefs.h" 7 | #define PWID 1 /* width of label border */ 8 | #define BWID 1 /* width of button relief */ 9 | #define FWID 2 /* width of frame relief */ 10 | #define SPACE 1 /* space inside relief of button or frame */ 11 | #define CKSIZE 3 /* size of check mark */ 12 | #define CKSPACE 2 /* space around check mark */ 13 | #define CKWID 1 /* width of frame around check mark */ 14 | #define CKINSET 1 /* space around check mark frame */ 15 | #define CKBORDER 2 /* space around X inside frame */ 16 | static int plldepth; 17 | static Image *pl_white, *pl_light, *pl_dark, *pl_black, *pl_hilit; 18 | int pl_drawinit(int ldepth){ 19 | plldepth=ldepth; 20 | pl_white=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0xFFFFFFFF); 21 | //pl_light=allocimagemix(display, DPalebluegreen, DWhite); 22 | pl_light=display->white; 23 | //pl_dark =allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue); 24 | pl_dark = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0X999999FF); 25 | pl_black=allocimage(display, Rect(0,0,1,1), screen->chan, 1, 0x000000FF); 26 | pl_hilit=allocimage(display, Rect(0,0,1,1), CHAN1(CAlpha,8), 1, 0x80); 27 | if(pl_white==0 || pl_light==0 || pl_black==0 || pl_dark==0) return 0; 28 | return 1; 29 | } 30 | void pl_relief(Image *b, Image *ul, Image *lr, Rectangle r, int wid){ 31 | int x, y; 32 | draw(b, Rect(r.min.x, r.max.y-wid, r.max.x, r.max.y), lr, 0, ZP); /* bottom */ 33 | draw(b, Rect(r.max.x-wid, r.min.y, r.max.x, r.max.y), lr, 0, ZP); /* right */ 34 | draw(b, Rect(r.min.x, r.min.y, r.min.x+wid, r.max.y), ul, 0, ZP); /* left */ 35 | draw(b, Rect(r.min.x, r.min.y, r.max.x, r.min.y+wid), ul, 0, ZP); /* top */ 36 | for(x=0;x!=wid;x++) for(y=wid-1-x;y!=wid;y++){ 37 | draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.max.x-wid, y+r.min.y)), lr, 0, ZP); 38 | draw(b, rectaddpt(Rect(0,0,1,1), Pt(x+r.min.x, y+r.max.y-wid)), lr, 0, ZP); 39 | } 40 | } 41 | Rectangle pl_boxoutline(Image *b, Rectangle r, int style, int fill){ 42 | if(plldepth==0) switch(style){ 43 | case UP: 44 | pl_relief(b, pl_black, pl_black, r, BWID); 45 | r=insetrect(r, BWID); 46 | if(fill) draw(b, r, pl_white, 0, ZP); 47 | else border(b, r, SPACE, pl_white, ZP); 48 | break; 49 | case DOWN: 50 | case DOWN1: 51 | case DOWN2: 52 | case DOWN3: 53 | pl_relief(b, pl_black, pl_black, r, BWID); 54 | r=insetrect(r, BWID); 55 | if(fill) draw(b, r, pl_black, 0, ZP); 56 | border(b, r, SPACE, pl_black, ZP); 57 | break; 58 | case PASSIVE: 59 | if(fill) draw(b, r, pl_white, 0, ZP); 60 | r=insetrect(r, PWID); 61 | if(!fill) border(b, r, SPACE, pl_white, ZP); 62 | break; 63 | case FRAME: 64 | pl_relief(b, pl_white, pl_black, r, FWID); 65 | r=insetrect(r, FWID); 66 | pl_relief(b, pl_black, pl_white, r, FWID); 67 | r=insetrect(r, FWID); 68 | if(fill) draw(b, r, pl_white, 0, ZP); 69 | else border(b, r, SPACE, pl_white, ZP); 70 | break; 71 | } 72 | else switch(style){ 73 | case UP: 74 | /* 75 | pl_relief(b, pl_white, pl_black, r, BWID); 76 | r=insetrect(r, BWID); 77 | if(fill) draw(b, r, pl_light, 0, ZP); 78 | else border(b, r, SPACE, pl_white, ZP); 79 | */ 80 | draw(b, r, pl_light, 0, ZP); 81 | border(b, r, 1, pl_black, ZP); 82 | break; 83 | case DOWN: 84 | case DOWN1: 85 | case DOWN2: 86 | case DOWN3: 87 | pl_relief(b, pl_black, pl_white, r, BWID); 88 | r=insetrect(r, BWID); 89 | if(fill) draw(b, r, pl_dark, 0, ZP); 90 | else border(b, r, SPACE, pl_black, ZP); 91 | break; 92 | case PASSIVE: 93 | if(fill) draw(b, r, pl_light, 0, ZP); 94 | r=insetrect(r, PWID); 95 | if(!fill) border(b, r, SPACE, pl_white, ZP); 96 | break; 97 | case FRAME: 98 | border(b, r, 1, pl_black, ZP); 99 | /* 100 | pl_relief(b, pl_white, pl_black, r, FWID); 101 | r=insetrect(r, FWID); 102 | pl_relief(b, pl_black, pl_white, r, FWID); 103 | r=insetrect(r, FWID); 104 | if(fill) draw(b, r, pl_light, 0, ZP); 105 | else border(b, r, SPACE, pl_white, ZP); 106 | */ 107 | break; 108 | } 109 | return insetrect(r, SPACE); 110 | } 111 | Rectangle pl_outline(Image *b, Rectangle r, int style){ 112 | return pl_boxoutline(b, r, style, 0); 113 | } 114 | Rectangle pl_box(Image *b, Rectangle r, int style){ 115 | return pl_boxoutline(b, r, style, 1); 116 | } 117 | Rectangle pl_boxoutlinef(Image *b, Rectangle r, int flags, int style, int fill){ 118 | switch(style){ 119 | case UP: 120 | draw(b, r, pl_light, 0, ZP); 121 | if(!(flags&NOBORDER)) 122 | border(b, r, 1, pl_black, ZP); 123 | break; 124 | case DOWN: 125 | case DOWN1: 126 | case DOWN2: 127 | case DOWN3: 128 | if(!(flags&NOBORDER)) 129 | pl_relief(b, pl_black, pl_white, r, BWID); 130 | r=insetrect(r, BWID); 131 | if(fill) draw(b, r, pl_dark, 0, ZP); 132 | else if(!(flags&NOBORDER)) border(b, r, SPACE, pl_black, ZP); 133 | break; 134 | case PASSIVE: 135 | if(fill) draw(b, r, pl_light, 0, ZP); 136 | r=insetrect(r, PWID); 137 | if(!fill) border(b, r, SPACE, pl_white, ZP); 138 | break; 139 | case FRAME: 140 | border(b, r, 1, pl_black, ZP); 141 | break; 142 | } 143 | return insetrect(r, SPACE); 144 | } 145 | Rectangle pl_outlinef(Image *b, Rectangle r, int flags, int style){ 146 | return pl_boxoutlinef(b, r, flags, style, 0); 147 | } 148 | Rectangle pl_boxf(Image *b, Rectangle r, int flags, int style){ 149 | return pl_boxoutlinef(b, r, flags, style, 1); 150 | } 151 | Point pl_boxsize(Point interior, int state){ 152 | switch(state){ 153 | case UP: 154 | case DOWN: 155 | case DOWN1: 156 | case DOWN2: 157 | case DOWN3: 158 | return addpt(interior, Pt(2*(BWID+SPACE), 2*(BWID+SPACE))); 159 | case PASSIVE: 160 | return addpt(interior, Pt(2*(PWID+SPACE), 2*(PWID+SPACE))); 161 | case FRAME: 162 | return addpt(interior, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE)); 163 | } 164 | return Pt(0, 0); 165 | } 166 | void pl_interior(int state, Point *ul, Point *size){ 167 | switch(state){ 168 | case UP: 169 | case DOWN: 170 | case DOWN1: 171 | case DOWN2: 172 | case DOWN3: 173 | *ul=addpt(*ul, Pt(BWID+SPACE, BWID+SPACE)); 174 | *size=subpt(*size, Pt(2*(BWID+SPACE), 2*(BWID+SPACE))); 175 | break; 176 | case PASSIVE: 177 | *ul=addpt(*ul, Pt(PWID+SPACE, PWID+SPACE)); 178 | *size=subpt(*size, Pt(2*(PWID+SPACE), 2*(PWID+SPACE))); 179 | break; 180 | case FRAME: 181 | *ul=addpt(*ul, Pt(2*FWID+SPACE, 2*FWID+SPACE)); 182 | *size=subpt(*size, Pt(4*FWID+2*SPACE, 4*FWID+2*SPACE)); 183 | } 184 | } 185 | 186 | void pl_drawicon(Image *b, Rectangle r, int stick, int flags, Icon *s){ 187 | Rectangle save; 188 | Point ul, offs; 189 | ul=r.min; 190 | offs=subpt(subpt(r.max, r.min), pl_iconsize(flags, s)); 191 | switch(stick){ 192 | case PLACENW: break; 193 | case PLACEN: ul.x+=offs.x/2; break; 194 | case PLACENE: ul.x+=offs.x; break; 195 | case PLACEW: ul.y+=offs.y/2; break; 196 | case PLACECEN: ul.x+=offs.x/2; ul.y+=offs.y/2; break; 197 | case PLACEE: ul.x+=offs.x; break; 198 | case PLACESW: ul.y+=offs.y; break; 199 | case PLACES: ul.x+=offs.x/2; ul.y+=offs.y; break; 200 | case PLACESE: ul.x+=offs.x; ul.y+=offs.y; break; 201 | } 202 | save=b->clipr; 203 | if(!rectclip(&r, save)) 204 | return; 205 | replclipr(b, b->repl, r); 206 | if(flags&BITMAP) draw(b, Rpt(ul, addpt(ul, pl_iconsize(flags, s))), s, 0, ZP); 207 | else string(b, ul, pl_black, ZP, font, s); 208 | replclipr(b, b->repl, save); 209 | } 210 | /* 211 | * Place a check mark at the left end of r. Return the unused space. 212 | * Caller must guarantee that r.max.x-r.min.x>=r.max.y-r.min.y! 213 | */ 214 | Rectangle pl_radio(Image *b, Rectangle r, int val){ 215 | Rectangle remainder; 216 | remainder=r; 217 | r.max.x=r.min.x+r.max.y-r.min.y; 218 | remainder.min.x=r.max.x; 219 | r=insetrect(r, CKINSET); 220 | if(plldepth==0) 221 | pl_relief(b, pl_black, pl_black, r, CKWID); 222 | else 223 | pl_relief(b, pl_black, pl_white, r, CKWID); 224 | r=insetrect(r, CKWID); 225 | if(plldepth==0) 226 | draw(b, r, pl_white, 0, ZP); 227 | else 228 | draw(b, r, pl_light, 0, ZP); 229 | if(val) draw(b, insetrect(r, CKSPACE), pl_black, 0, ZP); 230 | return remainder; 231 | } 232 | Rectangle pl_check(Image *b, Rectangle r, int val){ 233 | Rectangle remainder; 234 | remainder=r; 235 | r.max.x=r.min.x+r.max.y-r.min.y; 236 | remainder.min.x=r.max.x; 237 | r=insetrect(r, CKINSET); 238 | if(plldepth==0) 239 | pl_relief(b, pl_black, pl_black, r, CKWID); 240 | else 241 | pl_relief(b, pl_black, pl_white, r, CKWID); 242 | r=insetrect(r, CKWID); 243 | if(plldepth==0) 244 | draw(b, r, pl_white, 0, ZP); 245 | else 246 | draw(b, r, pl_light, 0, ZP); 247 | r=insetrect(r, CKBORDER); 248 | if(val){ 249 | line(b, Pt(r.min.x, r.min.y+1), Pt(r.max.x-1, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP); 250 | line(b, Pt(r.min.x, r.min.y ), Pt(r.max.x, r.max.y ), Endsquare, Endsquare, 0, pl_black, ZP); 251 | line(b, Pt(r.min.x+1, r.min.y ), Pt(r.max.x, r.max.y-1), Endsquare, Endsquare, 0, pl_black, ZP); 252 | 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); 253 | line(b, Pt(r.min.x, r.max.y-1), Pt(r.max.x, r.min.y-1), Endsquare, Endsquare, 0, pl_black, ZP); 254 | line(b, Pt(r.min.x+1, r.max.y-1), Pt(r.max.x, r.min.y ), Endsquare, Endsquare, 0, pl_black, ZP); 255 | } 256 | return remainder; 257 | } 258 | int pl_ckwid(void){ 259 | return 2*(CKINSET+CKSPACE+CKWID)+CKSIZE; 260 | } 261 | void pl_sliderupd(Image *b, Rectangle r1, int dir, int lo, int hi){ 262 | Rectangle r2, r3; 263 | r2=r1; 264 | r3=r1; 265 | if(lo<0) lo=0; 266 | if(hi<=lo) hi=lo+1; 267 | switch(dir){ 268 | case HORIZ: 269 | r1.max.x=r1.min.x+lo; 270 | r2.min.x=r1.max.x; 271 | r2.max.x=r1.min.x+hi; 272 | if(r2.max.x>r3.max.x) r2.max.x=r3.max.x; 273 | r3.min.x=r2.max.x; 274 | break; 275 | case VERT: 276 | r1.max.y=r1.min.y+lo; 277 | r2.min.y=r1.max.y; 278 | r2.max.y=r1.min.y+hi; 279 | if(r2.max.y>r3.max.y) r2.max.y=r3.max.y; 280 | r3.min.y=r2.max.y; 281 | break; 282 | } 283 | draw(b, r1, pl_light, 0, ZP); 284 | draw(b, r2, pl_dark, 0, ZP); 285 | draw(b, r3, pl_light, 0, ZP); 286 | } 287 | void pl_draw1(Panel *p, Image *b); 288 | void pl_drawall(Panel *p, Image *b){ 289 | if(p->flags&INVIS || p->flags&IGNORE) return; 290 | p->b=b; 291 | p->draw(p); 292 | for(p=p->child;p;p=p->next) pl_draw1(p, b); 293 | } 294 | void pl_draw1(Panel *p, Image *b){ 295 | if(b!=0) 296 | pl_drawall(p, b); 297 | } 298 | void pldraw(Panel *p, Image *b){ 299 | pl_draw1(p, b); 300 | } 301 | void pl_invis(Panel *p, int v){ 302 | for(;p;p=p->next){ 303 | if(v) p->flags|=INVIS; else p->flags&=~INVIS; 304 | pl_invis(p->child, v); 305 | } 306 | } 307 | Point pl_iconsize(int flags, Icon *p){ 308 | if(flags&BITMAP) return subpt(((Image *)p)->r.max, ((Image *)p)->r.min); 309 | return stringsize(font, (char *)p); 310 | } 311 | void pl_highlight(Image *b, Rectangle r){ 312 | draw(b, r, pl_dark, pl_hilit, ZP); 313 | } 314 | void pl_clr(Image *b, Rectangle r){ 315 | draw(b, r, display->white, 0, ZP); 316 | } 317 | void pl_fill(Image *b, Rectangle r){ 318 | draw(b, r, plldepth==0? pl_white : pl_light, 0, ZP); 319 | } 320 | void pl_cpy(Image *b, Point dst, Rectangle src){ 321 | draw(b, Rpt(dst, addpt(dst, subpt(src.max, src.min))), b, 0, src.min); 322 | } 323 | -------------------------------------------------------------------------------- /libpanel/textwin.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Text windows 3 | * void twhilite(Textwin *t, int sel0, int sel1, int on) 4 | * hilite (on=1) or unhilite (on=0) a range of characters 5 | * void twselect(Textwin *t, Mouse *m) 6 | * set t->sel0, t->sel1 from mouse input. 7 | * Also hilites selection. 8 | * Caller should first unhilite previous selection. 9 | * void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins) 10 | * Replace the given range of characters with the given insertion. 11 | * Caller should unhilite selection while this is called. 12 | * void twscroll(Textwin *t, int top) 13 | * Character with index top moves to the top line of the screen. 14 | * int twpt2rune(Textwin *t, Point p) 15 | * which character is displayed at point p? 16 | * void twreshape(Textwin *t, Rectangle r) 17 | * save r and redraw the text 18 | * Textwin *twnew(Bitmap *b, Font *f, Rune *text, int ntext) 19 | * create a new text window 20 | * void twfree(Textwin *t) 21 | * get rid of a surplus Textwin 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "pldefs.h" 29 | 30 | #define SLACK 100 31 | 32 | /* 33 | * Is text at point a before or after that at point b? 34 | */ 35 | int tw_before(Textwin *t, Point a, Point b){ 36 | return a.yhgt && a.xloc+(t->bot-t->top); 47 | for(lp=t->loc;lp!=el;lp++) 48 | if(tw_before(t, p, *lp)){ 49 | if(lp==t->loc) return t->top; 50 | return lp-t->loc+t->top-1; 51 | } 52 | return t->bot; 53 | } 54 | /* 55 | * Return ul corner of the character with the given index 56 | */ 57 | Point tw_rune2pt(Textwin *t, int i){ 58 | if(itop) return t->r.min; 59 | if(i>t->bot) return t->r.max; 60 | return t->loc[i-t->top]; 61 | } 62 | /* 63 | * Store p at t->loc[l], extending t->loc if necessary 64 | */ 65 | void tw_storeloc(Textwin *t, int l, Point p){ 66 | int nloc; 67 | if(l>=t->eloc-t->loc){ 68 | nloc=l+SLACK; 69 | t->loc=pl_erealloc(t->loc, nloc*sizeof(Point)); 70 | t->eloc=t->loc+nloc; 71 | } 72 | t->loc[l]=p; 73 | } 74 | /* 75 | * Set the locations at which the given runes should appear. 76 | * Returns the index of the first rune not set, which might not 77 | * be last because we reached the bottom of the window. 78 | * 79 | * N.B. this zaps the loc of r[last], so that value should be saved first, 80 | * if it's important. 81 | */ 82 | int tw_setloc(Textwin *t, int first, int last, Point ul){ 83 | Rune *r, *er; 84 | int x, dt, lp; 85 | char buf[UTFmax+1]; 86 | er=t->text+last; 87 | for(r=t->text+first,lp=first-t->top;r!=er && ul.y+t->hgt<=t->r.max.y;r++,lp++){ 88 | tw_storeloc(t, lp, ul); 89 | switch(*r){ 90 | case '\n': 91 | ul.x=t->r.min.x; 92 | ul.y+=t->hgt; 93 | break; 94 | case '\t': 95 | x=ul.x-t->r.min.x+t->mintab+t->tabstop; 96 | x-=x%t->tabstop; 97 | ul.x=x+t->r.min.x; 98 | if(ul.x>t->r.max.x){ 99 | ul.x=t->r.min.x; 100 | ul.y+=t->hgt; 101 | tw_storeloc(t, lp, ul); 102 | if(ul.y+t->hgt>t->r.max.y) return r-t->text; 103 | ul.x+=+t->tabstop; 104 | } 105 | break; 106 | default: 107 | buf[runetochar(buf, r)]='\0'; 108 | dt=stringwidth(t->font, buf); 109 | ul.x+=dt; 110 | if(ul.x>t->r.max.x){ 111 | ul.x=t->r.min.x; 112 | ul.y+=t->hgt; 113 | tw_storeloc(t, lp, ul); 114 | if(ul.y+t->hgt>t->r.max.y) return r-t->text; 115 | ul.x+=dt; 116 | } 117 | break; 118 | } 119 | } 120 | tw_storeloc(t, lp, ul); 121 | return r-t->text; 122 | } 123 | /* 124 | * Draw the given runes at their locations. 125 | * Bug -- saving up multiple characters would 126 | * reduce the number of calls to string, 127 | * and probably make this a lot faster. 128 | */ 129 | void tw_draw(Textwin *t, int first, int last){ 130 | Rune *r, *er; 131 | Point *lp, ul, ur; 132 | char buf[UTFmax+1]; 133 | if(firsttop) first=t->top; 134 | if(last>t->bot) last=t->bot; 135 | if(last<=first) return; 136 | er=t->text+last; 137 | for(r=t->text+first,lp=t->loc+(first-t->top);r!=er;r++,lp++){ 138 | if(lp->y+t->hgt>t->r.max.y){ 139 | fprint(2, "chr %C, index %ld of %d, loc %d %d, off bottom\n", 140 | *r, lp-t->loc, t->bot-t->top, lp->x, lp->y); 141 | return; 142 | } 143 | switch(*r){ 144 | case '\n': 145 | ur=*lp; 146 | break; 147 | case '\t': 148 | ur=*lp; 149 | if(lp[1].y!=lp[0].y) 150 | ul=Pt(t->r.min.x, lp[1].y); 151 | else 152 | ul=*lp; 153 | pl_clr(t->b, Rpt(ul, Pt(lp[1].x, ul.y+t->hgt))); 154 | break; 155 | default: 156 | buf[runetochar(buf, r)]='\0'; 157 | /***/ pl_clr(t->b, Rpt(*lp, addpt(*lp, stringsize(t->font, buf)))); 158 | ur=string(t->b, *lp, display->black, ZP, t->font, buf); 159 | break; 160 | } 161 | if(lp[1].y!=lp[0].y) 162 | /***/ pl_clr(t->b, Rpt(ur, Pt(t->r.max.x, ur.y+t->hgt))); 163 | } 164 | } 165 | /* 166 | * Hilight the characters with tops between ul and ur 167 | */ 168 | void tw_hilitep(Textwin *t, Point ul, Point ur){ 169 | Point swap; 170 | int y; 171 | if(tw_before(t, ur, ul)){ swap=ul; ul=ur; ur=swap;} 172 | y=ul.y+t->hgt; 173 | if(y>t->r.max.y) y=t->r.max.y; 174 | if(ul.y==ur.y) 175 | pl_highlight(t->b, Rpt(ul, Pt(ur.x, y))); 176 | else{ 177 | pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, y))); 178 | ul=Pt(t->r.min.x, y); 179 | pl_highlight(t->b, Rpt(ul, Pt(t->r.max.x, ur.y))); 180 | ul=Pt(t->r.min.x, ur.y); 181 | y=ur.y+t->hgt; 182 | if(y>t->r.max.y) y=t->r.max.y; 183 | pl_highlight(t->b, Rpt(ul, Pt(ur.x, y))); 184 | } 185 | } 186 | /* 187 | * Hilite/unhilite the given range of characters 188 | */ 189 | void twhilite(Textwin *t, int sel0, int sel1, int on){ 190 | Point ul, ur; 191 | int swap, y; 192 | if(sel1top || t->bottop) sel0=t->top; 195 | if(sel1>t->bot) sel1=t->bot; 196 | if(!on){ 197 | if(sel1==sel0){ 198 | ul=t->loc[sel0-t->top]; 199 | y=ul.y+t->hgt; 200 | if(y>t->r.max.y) y=t->r.max.y; 201 | pl_clr(t->b, Rpt(ul, Pt(ul.x+1, y))); 202 | }else 203 | tw_draw(t, sel0, sel1); 204 | return; 205 | } 206 | ul=t->loc[sel0-t->top]; 207 | if(sel1==sel0) 208 | ur=addpt(ul, Pt(1, 0)); 209 | else 210 | ur=t->loc[sel1-t->top]; 211 | tw_hilitep(t, ul, ur); 212 | } 213 | /* 214 | * Set t->sel[01] from mouse input. 215 | * Also hilites the selection. 216 | * Caller should unhilite the previous 217 | * selection before calling this. 218 | */ 219 | void twselect(Textwin *t, Mouse *m){ 220 | int sel0, sel1, newsel; 221 | Point p0, p1, newp; 222 | sel0=sel1=twpt2rune(t, m->xy); 223 | p0=tw_rune2pt(t, sel0); 224 | p1=addpt(p0, Pt(1, 0)); 225 | twhilite(t, sel0, sel1, 1); 226 | for(;;){ 227 | if(display->bufp > display->buf) 228 | flushimage(display, 1); 229 | *m=emouse(); 230 | if((m->buttons&7)!=1) break; 231 | newsel=twpt2rune(t, m->xy); 232 | newp=tw_rune2pt(t, newsel); 233 | if(eqpt(newp, p0)) newp=addpt(newp, Pt(1, 0)); 234 | if(!eqpt(newp, p1)){ 235 | if((sel0<=sel1 && sel1sel0=sel0; 251 | t->sel1=sel1; 252 | } 253 | else{ 254 | t->sel0=sel1; 255 | t->sel1=sel0; 256 | } 257 | } 258 | /* 259 | * Clear the area following the last displayed character 260 | */ 261 | void tw_clrend(Textwin *t){ 262 | Point ul; 263 | int y; 264 | ul=t->loc[t->bot-t->top]; 265 | y=ul.y+t->hgt; 266 | if(y>t->r.max.y) y=t->r.max.y; 267 | pl_clr(t->b, Rpt(ul, Pt(t->r.max.x, y))); 268 | ul=Pt(t->r.min.x, y); 269 | pl_clr(t->b, Rpt(ul, t->r.max)); 270 | } 271 | /* 272 | * Move part of a line of text, truncating the source or padding 273 | * the destination on the right if necessary. 274 | */ 275 | void tw_moverect(Textwin *t, Point uld, Point urd, Point uls, Point urs){ 276 | int sw, dw, d; 277 | if(urs.y!=uls.y) urs=Pt(t->r.max.x, uls.y); 278 | if(urd.y!=uld.y) urd=Pt(t->r.max.x, uld.y); 279 | sw=uls.x-urs.x; 280 | dw=uld.x-urd.x; 281 | if(dw>sw){ 282 | d=dw-sw; 283 | pl_clr(t->b, Rect(urd.x-d, urd.y, urd.x, urd.y+t->hgt)); 284 | dw=sw; 285 | } 286 | pl_cpy(t->b, uld, Rpt(uls, Pt(uls.x+dw, uls.y+t->hgt))); 287 | } 288 | /* 289 | * Move a block of characters up or to the left: 290 | * Identify contiguous runs of characters whose width doesn't change, and 291 | * move them in one bitblt per run. 292 | * If we get to a point where source and destination are x-aligned, 293 | * they will remain x-aligned for the rest of the block. 294 | * Then, if they are y-aligned, they're already in the right place. 295 | * Otherwise, we can move them in three bitblts; one if all the 296 | * remaining characters are on one line. 297 | */ 298 | void tw_moveup(Textwin *t, Point *dp, Point *sp, Point *esp){ 299 | Point uld, uls; /* upper left of destination/source */ 300 | int y; 301 | while(sp!=esp && sp->x!=dp->x){ 302 | uld=*dp; 303 | uls=*sp; 304 | while(sp!=esp && sp->y==uls.y && dp->y==uld.y && sp->x-uls.x==dp->x-uld.x){ 305 | sp++; 306 | dp++; 307 | } 308 | tw_moverect(t, uld, *dp, uls, *sp); 309 | } 310 | if(sp==esp || esp->y==dp->y) return; 311 | if(esp->y==sp->y){ /* one line only */ 312 | pl_cpy(t->b, *dp, Rpt(*sp, Pt(esp->x, sp->y+t->hgt))); 313 | return; 314 | } 315 | y=sp->y+t->hgt; 316 | pl_cpy(t->b, *dp, Rpt(*sp, Pt(t->r.max.x, y))); 317 | pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt), 318 | Rect(t->r.min.x, y, t->r.max.x, esp->y)); 319 | y=dp->y+esp->y-sp->y; 320 | pl_cpy(t->b, Pt(t->r.min.x, y), 321 | Rect(t->r.min.x, esp->y, esp->x, esp->y+t->hgt)); 322 | } 323 | /* 324 | * Same as above, but moving down and in reverse order, so as not to overwrite stuff 325 | * not moved yet. 326 | */ 327 | void tw_movedn(Textwin *t, Point *dp, Point *bsp, Point *esp){ 328 | Point *sp, urs, urd; 329 | int dy; 330 | dp+=esp-bsp; 331 | sp=esp; 332 | dy=dp->y-sp->y; 333 | while(sp!=bsp && dp[-1].x==sp[-1].x){ 334 | --dp; 335 | --sp; 336 | } 337 | if(dy!=0){ 338 | if(sp->y==esp->y) 339 | pl_cpy(t->b, *dp, Rect(sp->x, sp->y, esp->x, esp->y+t->hgt)); 340 | else{ 341 | pl_cpy(t->b, Pt(t->r.min.x, sp->x+dy), 342 | Rect(t->r.min.x, sp->y, esp->x, esp->y+t->hgt)); 343 | pl_cpy(t->b, Pt(t->r.min.x, dp->y+t->hgt), 344 | Rect(t->r.min.x, sp->y+t->hgt, t->r.max.x, esp->y)); 345 | pl_cpy(t->b, *dp, 346 | Rect(sp->x, sp->y, t->r.max.x, sp->y+t->hgt)); 347 | } 348 | } 349 | while(sp!=bsp){ 350 | urd=*dp; 351 | urs=*sp; 352 | while(sp!=bsp && sp[-1].y==sp[0].y && dp[-1].y==dp[0].y 353 | && sp[-1].x-sp[0].x==dp[-1].x-dp[0].x){ 354 | --sp; 355 | --dp; 356 | } 357 | tw_moverect(t, *dp, urd, *sp, urs); 358 | } 359 | } 360 | /* 361 | * Move the given range of characters, already drawn on 362 | * the given textwin, to the given location. 363 | * Start and end must both index characters that are initially on-screen. 364 | */ 365 | void tw_relocate(Textwin *t, int first, int last, Point dst){ 366 | Point *srcloc; 367 | int nbyte; 368 | if(firsttop || lastbotloc[first-t->top], nbyte); 372 | tw_setloc(t, first, last, dst); 373 | if(tw_before(t, dst, srcloc[0])) 374 | tw_moveup(t, t->loc+first-t->top, srcloc, srcloc+(last-first)); 375 | else 376 | tw_movedn(t, t->loc+first-t->top, srcloc, srcloc+(last-first)); 377 | } 378 | /* 379 | * Replace the runes with indices from r0 to r1-1 with the text 380 | * pointed to by text, and with length ntext. 381 | * Open up a hole in t->text, t->loc. 382 | * Insert new text, calculate their locs (save the extra loc that's overwritten first) 383 | * (swap saved & overwritten locs) 384 | * move tail. 385 | * calc locs and draw new text after tail, if necessary. 386 | * draw new text, if necessary 387 | */ 388 | void twreplace(Textwin *t, int r0, int r1, Rune *ins, int nins){ 389 | int olen, nlen, tlen, dtop; 390 | olen=t->etext-t->text; 391 | nlen=olen+nins-(r1-r0); 392 | tlen=t->eslack-t->text; 393 | if(nlen>tlen){ 394 | tlen=nlen+SLACK; 395 | t->text=pl_erealloc(t->text, tlen*sizeof(Rune)); 396 | t->eslack=t->text+tlen; 397 | } 398 | if(olen!=nlen) 399 | memmove(t->text+r0+nins, t->text+r1, (olen-r1)*sizeof(Rune)); 400 | if(nins!=0) /* ins can be 0 if nins==0 */ 401 | memmove(t->text+r0, ins, nins*sizeof(Rune)); 402 | t->etext=t->text+nlen; 403 | if(r0>t->bot) /* insertion is completely below visible text */ 404 | return; 405 | if(r1top){ /* insertion is completely above visible text */ 406 | dtop=nlen-olen; 407 | t->top+=dtop; 408 | t->bot+=dtop; 409 | return; 410 | } 411 | if(1 || t->bot<=r0+nins){ /* no useful text on screen below r0 */ 412 | if(r0<=t->top) /* no useful text above, either */ 413 | t->top=r0; 414 | t->bot=tw_setloc(t, r0, nlen, t->loc[r0-t->top]); 415 | tw_draw(t, r0, t->bot); 416 | tw_clrend(t); 417 | return; 418 | } 419 | /* 420 | * code for case where there is useful text below is missing (see `1 ||' above) 421 | */ 422 | } 423 | /* 424 | * This works but is stupid. 425 | */ 426 | void twscroll(Textwin *t, int top){ 427 | while(top!=0 && t->text[top-1]!='\n') --top; 428 | t->top=top; 429 | t->bot=tw_setloc(t, top, t->etext-t->text, t->r.min); 430 | tw_draw(t, t->top, t->bot); 431 | tw_clrend(t); 432 | } 433 | void twreshape(Textwin *t, Rectangle r){ 434 | t->r=r; 435 | t->bot=tw_setloc(t, t->top, t->etext-t->text, t->r.min); 436 | tw_draw(t, t->top, t->bot); 437 | tw_clrend(t); 438 | } 439 | Textwin *twnew(Image *b, Font *f, Rune *text, int ntext){ 440 | Textwin *t; 441 | t=pl_emalloc(sizeof(Textwin)); 442 | t->text=pl_emalloc((ntext+SLACK)*sizeof(Rune)); 443 | t->loc=pl_emalloc(SLACK*sizeof(Point)); 444 | t->eloc=t->loc+SLACK; 445 | t->etext=t->text+ntext; 446 | t->eslack=t->etext+SLACK; 447 | if(ntext) memmove(t->text, text, ntext*sizeof(Rune)); 448 | t->top=0; 449 | t->bot=0; 450 | t->sel0=0; 451 | t->sel1=0; 452 | t->b=b; 453 | t->font=f; 454 | t->hgt=f->height; 455 | t->mintab=stringwidth(f, "0"); 456 | t->tabstop=8*t->mintab; 457 | return t; 458 | } 459 | void twfree(Textwin *t){ 460 | free(t->loc); 461 | free(t->text); 462 | free(t); 463 | } 464 | /* 465 | * Correct the character locations in a textwin after the panel is moved. 466 | * This horrid hack would not be necessary if loc values were relative 467 | * to the panel, rather than absolute. 468 | */ 469 | void twmove(Textwin *t, Point d){ 470 | Point *lp; 471 | t->r = rectaddpt(t->r, d); 472 | for(lp=t->loc; lpeloc; lp++) 473 | *lp = addpt(*lp, d); 474 | } 475 | -------------------------------------------------------------------------------- /icons.h: -------------------------------------------------------------------------------- 1 | uchar ibackdata[] = { 2 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 3 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 5 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 7 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 9 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 11 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 12 | 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 15 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 16 | 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x28, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 20 | 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 23 | 0xf5, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 24 | 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 25 | 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 27 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 28 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 29 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 30 | 0x54, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 31 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 32 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 33 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 34 | 0x58, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 35 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 36 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 37 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 38 | 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xbd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 39 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 40 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 41 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 43 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 44 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 45 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x2c, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 48 | 0x52, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 49 | 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 52 | 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 56 | 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 62 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 64 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 65 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 66 | }; 67 | 68 | uchar ifwddata[] = { 69 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 79 | 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 83 | 0xfb, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 87 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 | 0xf1, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 90 | 0xfb, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 91 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 92 | 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 94 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 95 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 96 | 0xff, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 98 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 99 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 100 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 101 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 102 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 103 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 104 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xef, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x00, 0x00, 105 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 106 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 107 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 108 | 0xff, 0x00, 0x00, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 109 | 0xf7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 110 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 111 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 112 | 0x72, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 113 | 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 114 | 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 115 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 119 | 0xfb, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 120 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 122 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 123 | 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 126 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 133 | }; 134 | 135 | uchar ireloaddata[] = { 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 137 | 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 138 | 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 141 | 0x85, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 145 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 146 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 149 | 0xff, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 152 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 153 | 0xff, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 155 | 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 157 | 0xff, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 159 | 0xd9, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 160 | 0xb3, 0x00, 0x00, 0x00, 0xd3, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 161 | 0xff, 0x00, 0x00, 0x00, 0xeb, 0x00, 0x00, 0x00, 0xd5, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 162 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 163 | 0xff, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x42, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 165 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 167 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 169 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 | 0x46, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 171 | 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfb, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 172 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 173 | 0xff, 0x00, 0x00, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 174 | 0xbb, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 175 | 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 176 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xd9, 0x00, 0x00, 0x00, 177 | 0xdb, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 179 | 0xff, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 181 | 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 183 | 0xff, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 186 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 187 | 0xed, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 188 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 190 | 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 191 | 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 192 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 193 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 194 | 0x0e, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 195 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 196 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 197 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 198 | 0xaf, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 199 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 200 | }; 201 | -------------------------------------------------------------------------------- /gopher.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "dat.h" 12 | #include "icons.h" 13 | 14 | void texthit(Panel *p, int b, Rtext *t); 15 | void message(char *s, ...); 16 | 17 | Image *backi; 18 | Image *fwdi; 19 | Image *reloadi; 20 | Panel *root; 21 | Panel *backp; 22 | Panel *fwdp; 23 | Panel *reloadp; 24 | Panel *entryp; 25 | Panel *urlp; 26 | Panel *textp; 27 | Panel *statusp; 28 | Panel *popup; 29 | char *url; 30 | Mouse *mouse; 31 | Hist *hist = nil; 32 | char *bdir; 33 | 34 | enum 35 | { 36 | Msearch, 37 | Maddbookmark, 38 | Mbookmarks, 39 | Mexit, 40 | }; 41 | 42 | char *menu3[] = { 43 | "search", 44 | "add bookmark", 45 | "bookmarks", 46 | "exit", 47 | 0 48 | }; 49 | 50 | Link* 51 | mklink(char *addr, char *sel, int type) 52 | { 53 | Link *l; 54 | 55 | l = malloc(sizeof *l); 56 | if(l==nil) 57 | sysfatal("malloc: %r"); 58 | l->addr = strdup(addr); 59 | l->sel = sel!=nil ? strdup(sel) : nil; 60 | l->type = type; 61 | return l; 62 | } 63 | 64 | Link* 65 | clonelink(Link *l) 66 | { 67 | if(l==nil) 68 | return nil; 69 | return mklink(l->addr, l->sel, l->type); 70 | } 71 | 72 | int 73 | seltype(char c) 74 | { 75 | int t; 76 | 77 | t = -c; 78 | switch(c){ 79 | case '0': t = Ttext; break; 80 | case '1': t = Tmenu; break; 81 | case '2': t = Tns; break; 82 | case '3': t = Terror; break; 83 | case '4': t = Tbinhex; break; 84 | case '5': t = Tdos; break; 85 | case '6': t = Tuuencoded; break; 86 | case '7': t = Tsearch; break; 87 | case '8': t = Ttelnet; break; 88 | case '9': t = Tbinary; break; 89 | case '+': t = Tmirror; break; 90 | case 'g': t = Tgif; break; 91 | case 'I': t = Timage; break; 92 | case 'T': t = Tt3270; break; 93 | case 'd': t = Tdoc; break; 94 | case 'h': t = Thtml; break; 95 | case 'i': t = Tinfo; break; 96 | case 's': t = Tsound; break; 97 | case '.': t = Teof; break; 98 | default: break; 99 | } 100 | return t; 101 | } 102 | 103 | static char Typechar[] = { 104 | '0', '1', '2', '3', '4', '5', 105 | '6', '7', '8', '9', '+', 'g', 106 | 'I', 'T', 'd', 'h', 'i', 's', 107 | '.', 108 | }; 109 | 110 | static char *Typestr[] = { 111 | "FILE", "DIR", "NS", "ERR", "HEX", 112 | "DOS", "UU", "?", "TELNET", "BIN", 113 | "MIRROR", "GIF", "IMG", "T3270", "DOC", 114 | "HTML", "", "SND", "EOF", 115 | }; 116 | 117 | char* 118 | seltypestr(int type) 119 | { 120 | if(type<0) 121 | return smprint("UNKN:%c", (char) -type); 122 | return smprint("%6s", Typestr[type]); 123 | }; 124 | 125 | 126 | Gmenu* 127 | rendermenu(Link *l, Biobuf *bp) 128 | { 129 | char *s, *f[5], *t; 130 | Gmenu *m; 131 | Link *n; 132 | int type, c; 133 | 134 | m = malloc(sizeof *m); 135 | if(m==nil) 136 | sysfatal("malloc: %r"); 137 | m->link = clonelink(l); 138 | m->text = nil; 139 | plrtstr(&m->text, 1000000, 0, 0, font, strdup(" "), 0, 0); 140 | for(;;){ 141 | n = nil; 142 | s = Brdstr(bp, '\n', 0); 143 | if(s==nil || s[0]=='.') 144 | break; 145 | type = seltype(s[0]); 146 | c = getfields(s+1, f, 5, 0, "\t\r\n"); 147 | switch(type){ 148 | case Tinfo: 149 | break; 150 | case Thtml: 151 | n = mklink(strdup(f[1]+4), nil, Thtml); /* +4 skip URL: */ 152 | break; 153 | default: 154 | if(type < 0 && c < 3){ 155 | fprint(2, "skipping invalid menu line '%s'\n", s); 156 | continue; 157 | } 158 | n = mklink(netmkaddr(f[2], "tcp", f[3]), strdup(f[1]), type); 159 | break; 160 | } 161 | t = strdup(f[0]); 162 | plrtstr(&m->text, 1000000, 8, 0, font, seltypestr(type), PL_HEAD, 0); 163 | if(type == Tinfo || type < 0) 164 | plrtstr(&m->text, 8, 0, 0, font, t, 0, 0); 165 | else 166 | plrtstr(&m->text, 8, 0, 0, font, t, PL_HOT, n); 167 | 168 | } 169 | return m; 170 | } 171 | 172 | Gmenu* 173 | rendertext(Link *l, Biobuf *bp) 174 | { 175 | Gmenu *m; 176 | String *buf; 177 | int c, n; 178 | 179 | m = malloc(sizeof *m); 180 | if(m==nil) 181 | sysfatal("malloc: %r"); 182 | m->link = clonelink(l); 183 | m->text = nil; 184 | plrtstr(&m->text, 1000000, 0, 0, font, strdup(" "), 0, 0); 185 | n = 0; 186 | buf = s_new(); 187 | for(;;){ 188 | c = Bgetc(bp); 189 | if(c<0) 190 | break; 191 | else if(c=='\r' || c=='\n'){ 192 | if(c=='\r' && Bgetc(bp)!='\n') 193 | Bungetc(bp); 194 | if(n==1 && s_to_c(buf)[0]=='.') 195 | break; 196 | s_terminate(buf); 197 | plrtstr(&m->text, 1000000, 8, 0, font, strdup(s_to_c(buf)), 0, 0); 198 | s_reset(buf); 199 | n = 0; 200 | }else if(c=='\t'){ 201 | n += 4; 202 | s_append(buf, " "); 203 | }else{ 204 | n++; 205 | s_putc(buf, c); 206 | } 207 | } 208 | s_free(buf); 209 | return m; 210 | } 211 | 212 | Gmenu* 213 | render(Link *l) 214 | { 215 | int fd; 216 | Biobuf *bp; 217 | Gmenu *m; 218 | 219 | fd = dial(l->addr, 0, 0, 0); 220 | if(fd < 0){ 221 | message("unable to connect to %s: %r", l->addr); 222 | return nil; 223 | } 224 | fprint(fd, "%s\r\n", l->sel); 225 | bp = Bfdopen(fd, OREAD); 226 | if(bp==nil){ 227 | close(fd); 228 | sysfatal("bfdopen: %r"); 229 | } 230 | switch(l->type){ 231 | case Tmenu: 232 | m = rendermenu(l, bp); 233 | break; 234 | case Ttext: 235 | m = rendertext(l, bp); 236 | break; 237 | default: 238 | /* TODO error */ 239 | m = nil; 240 | break; 241 | } 242 | Bterm(bp); 243 | close(fd); 244 | return m; 245 | } 246 | 247 | void 248 | message(char *s, ...) 249 | { 250 | static char buf[1024]; 251 | char *out; 252 | va_list args; 253 | 254 | va_start(args, s); 255 | out = buf + vsnprint(buf, sizeof(buf), s, args); 256 | va_end(args); 257 | *out='\0'; 258 | plinitlabel(statusp, PACKN|FILLX, buf); 259 | pldraw(statusp, screen); 260 | flushimage(display, 1); 261 | } 262 | 263 | Link* 264 | urltolink(char *url) 265 | { 266 | char *a, *sel, *hostport, *p; 267 | int type; 268 | Link *l; 269 | 270 | a = strdup(url); 271 | hostport = a; 272 | if(strncmp(a, "gopher://", 9) == 0) 273 | hostport += 9; 274 | p = strchr(hostport, '/'); 275 | if(p){ 276 | *p++ = 0; 277 | type = *p ? seltype(*p++) : Tmenu; 278 | if(type < 0) 279 | return nil; 280 | sel = *p ? p : ""; 281 | }else{ 282 | type = Tmenu; 283 | sel = ""; 284 | } 285 | p = strchr(hostport, ':'); 286 | if(p){ 287 | *p++ = 0; 288 | l = mklink(netmkaddr(hostport, "tcp", p), sel, type); 289 | }else{ 290 | l = mklink(netmkaddr(hostport, "tcp", "70"), sel, type); 291 | } 292 | free(a); 293 | return l; 294 | } 295 | 296 | char* 297 | linktourl(Link *l) 298 | { 299 | char *f[3], *a, *s; 300 | int n; 301 | 302 | a = strdup(l->addr); 303 | n = getfields(a, f, 3, 0, "!"); 304 | if(n != 3) 305 | s = smprint("Url: gopher://%s/%d%s", l->addr, l->type, l->sel); 306 | else if(atoi(f[2])!=70) 307 | s = smprint("Url: gopher://%s:%s/%d%s", f[1], f[2], l->type, l->sel); 308 | else 309 | s = smprint("Url: gopher://%s/%d%s", f[1], l->type, l->sel); 310 | free(a); 311 | return s; 312 | } 313 | 314 | void 315 | seturl(Link *l) 316 | { 317 | free(url); 318 | url = linktourl(l); 319 | } 320 | 321 | void 322 | show(Gmenu *m) 323 | { 324 | plinittextview(textp, PACKE|EXPAND, ZP, m->text, texthit); 325 | pldraw(textp, screen); 326 | plinitlabel(urlp, PACKN|FILLX, url); 327 | pldraw(urlp, screen); 328 | message("gopher!"); 329 | } 330 | 331 | void 332 | freetext(Rtext *t){ 333 | Rtext *tt; 334 | Link *l; 335 | 336 | tt = t; 337 | for(; t!=0; t = t->next){ 338 | t->b=0; 339 | free(t->text); 340 | t->text = 0; 341 | if(l = t->user){ 342 | t->user = 0; 343 | free(l->addr); 344 | if(l->sel!=nil && l->sel[0]!=0) 345 | free(l->sel); 346 | free(l); 347 | } 348 | } 349 | plrtfree(tt); 350 | } 351 | 352 | void 353 | freehist(Hist *h) 354 | { 355 | Hist *n; 356 | Gmenu *m; 357 | 358 | for(n = h->n; h; h = n){ 359 | m = h->m; 360 | freetext(m->text); 361 | if(m->link!=nil){ 362 | free(m->link->addr); 363 | free(m->link->sel); 364 | free(m->link); 365 | } 366 | free(h); 367 | } 368 | } 369 | 370 | void 371 | visit(Link *l, int sethist) 372 | { 373 | Gmenu *m; 374 | Hist *h; 375 | 376 | seturl(l); 377 | message("loading %s...", url); 378 | m = render(l); 379 | if(m==nil) 380 | return; 381 | show(m); 382 | if(!sethist) 383 | return; 384 | h = malloc(sizeof *h); 385 | if(h == nil) 386 | sysfatal("malloc: %r"); 387 | /* FIXME 388 | if(hist != nil && hist->n != nil) 389 | freehist(hist->n); 390 | */ 391 | h->p = hist; 392 | h->n = nil; 393 | h->m = m; 394 | hist = h; 395 | } 396 | 397 | void 398 | plumburl(char *u) 399 | { 400 | int fd; 401 | 402 | fd = plumbopen("send", OWRITE|OCEXEC); 403 | if(fd<0) 404 | return; 405 | plumbsendtext(fd, "gopher", nil, nil, u); 406 | close(fd); 407 | } 408 | 409 | void 410 | dupfds(int fd, ...) 411 | { 412 | int mfd, n, i; 413 | va_list arg; 414 | Dir *dir; 415 | 416 | va_start(arg, fd); 417 | for(mfd = 0; fd >= 0; fd = va_arg(arg, int), mfd++) 418 | if(fd != mfd) 419 | if(dup(fd, mfd) < 0) 420 | sysfatal("dup: %r"); 421 | va_end(arg); 422 | if((fd = open("/fd", OREAD)) < 0) 423 | sysfatal("open: %r"); 424 | n = dirreadall(fd, &dir); 425 | for(i=0; i= mfd) 430 | close(fd); 431 | } 432 | free(dir); 433 | } 434 | 435 | void 436 | page(Link *l) 437 | { 438 | int fd; 439 | 440 | fd = dial(l->addr, 0, 0, 0); 441 | if(fd < 0) 442 | sysfatal("dial: %r"); 443 | fprint(fd, "%s\r\n", l->sel); 444 | switch(rfork(RFFDG|RFPROC|RFMEM|RFREND|RFNOWAIT|RFNOTEG)){ 445 | case -1: 446 | fprint(2, "Can't fork!"); 447 | break; 448 | case 0: 449 | dupfds(fd, 1, 2, -1); 450 | execl("/bin/rc", "rc", "-c", "page -w", nil); 451 | _exits(0); 452 | } 453 | close(fd); 454 | } 455 | 456 | void 457 | save(Link *l, char *name){ 458 | char buf[1024]; 459 | int ifd, ofd; 460 | 461 | ifd = dial(l->addr, 0, 0, 0); 462 | if(ifd < 0){ 463 | message("save: %s: %r", name); 464 | return; 465 | } 466 | fprint(ifd, "%s\r\n", l->sel); 467 | ofd=create(name, OWRITE, 0666); 468 | if(ofd < 0){ 469 | message("save: %s: %r", name); 470 | return; 471 | } 472 | switch(rfork(RFNOTEG|RFNAMEG|RFFDG|RFMEM|RFPROC|RFNOWAIT)){ 473 | case -1: 474 | message("Can't fork: %r"); 475 | break; 476 | case 0: 477 | dup(ifd, 0); 478 | close(ifd); 479 | dup(ofd, 1); 480 | close(ofd); 481 | 482 | snprint(buf, sizeof(buf), 483 | "{tput -p || cat} |[2] {aux/statusmsg -k %q >/dev/null || cat >/dev/null}", name); 484 | execl("/bin/rc", "rc", "-c", buf, nil); 485 | exits("exec"); 486 | } 487 | close(ifd); 488 | close(ofd); 489 | } 490 | 491 | void 492 | search(void) 493 | { 494 | static char last[256]; 495 | char buf[256]; 496 | Reprog *re; 497 | Rtext *tp; 498 | int yoff; 499 | 500 | for(;;){ 501 | if(hist == nil || hist->m == nil || hist->m->text == nil) 502 | return; 503 | strncpy(buf, last, sizeof(buf)-1); 504 | if(eenter("Search for", buf, sizeof(buf), mouse) <= 0) 505 | return; 506 | strncpy(last, buf, sizeof(buf)-1); 507 | re = regcompnl(buf); 508 | if(re == nil){ 509 | message("%r"); 510 | continue; 511 | } 512 | for(tp=hist->m->text;tp;tp=tp->next) 513 | if(tp->flags & PL_SEL) 514 | break; 515 | if(tp == nil) 516 | tp = hist->m->text; 517 | else { 518 | tp->flags &= ~PL_SEL; 519 | tp = tp->next; 520 | } 521 | while(tp != nil){ 522 | tp->flags &= ~PL_SEL; 523 | if(tp->text && *tp->text) 524 | if(regexec(re, tp->text, nil, 0)){ 525 | tp->flags |= PL_SEL; 526 | plsetpostextview(textp, tp->topy); 527 | break; 528 | } 529 | tp = tp->next; 530 | } 531 | free(re); 532 | yoff = plgetpostextview(textp); 533 | plinittextview(textp, PACKE|EXPAND, ZP, hist->m->text, texthit); 534 | plsetpostextview(textp, yoff); 535 | pldraw(textp, screen); 536 | } 537 | 538 | } 539 | 540 | void 541 | addbookmark(void) 542 | { 543 | Link *l; 544 | char buf[255] = {0}, *f, *u[3]; 545 | int n, fd; 546 | 547 | if(hist==nil) 548 | return; 549 | l = hist->m->link; 550 | n = eenter("Name:", buf, sizeof buf, mouse); 551 | if(n<=0) 552 | return; 553 | f = smprint("%s/bookmarks", bdir); 554 | fd = open(f, OWRITE); 555 | if(fd<0){ 556 | fd = create(f, OWRITE, 0644); 557 | if(fd<0){ 558 | message("cannot open %s", f); 559 | free(f); 560 | return; 561 | } 562 | fprint(fd, "iGOPHER Bookmarks\n"); 563 | fprint(fd, "i=================\n"); 564 | fprint(fd, "i \n"); 565 | } 566 | free(f); 567 | f = strdup(l->addr); 568 | getfields(f, u, 3, 0, "!"); 569 | seek(fd, 0, 2); 570 | fprint(fd, "%c%s\t%s\t%s\t%s\n", Typechar[l->type], buf, l->sel, u[1], u[2]); 571 | fprint(fd, "i%s\n", linktourl(l)); 572 | free(f); 573 | close(fd); 574 | message("added bookmark %s", buf); 575 | } 576 | 577 | void 578 | showbookmarks(void) 579 | { 580 | char *f; 581 | Biobuf *bp; 582 | Gmenu *m; 583 | 584 | f = smprint("%s/bookmarks", bdir); 585 | bp = Bopen(f, OREAD); 586 | if(bp==nil){ 587 | message("cannot open %s", f); 588 | free(f); 589 | return; 590 | } 591 | m = rendermenu(nil, bp); 592 | show(m); 593 | free(f); 594 | Bterm(bp); 595 | } 596 | 597 | char* 598 | linktofile(Link *l){ 599 | char *n, *s; 600 | 601 | if(l==nil) 602 | return nil; 603 | n = l->sel; 604 | if(n==nil || n[0]==0) 605 | n = "/"; 606 | if(s = strrchr(n, '/')) 607 | n = s+1; 608 | if(n[0]==0) 609 | n = "file"; 610 | return n; 611 | } 612 | 613 | void 614 | texthit(Panel *p, int b, Rtext *t) 615 | { 616 | Link *l; 617 | char *s, buf[1024] = {0}; 618 | 619 | USED(p); 620 | if(b!=1) 621 | return; 622 | if(t->user==nil) 623 | return; 624 | l = t->user; 625 | switch(l->type){ 626 | case Tmenu: 627 | case Ttext: 628 | visit(l, 1); 629 | break; 630 | case Thtml: 631 | plumburl(l->addr); 632 | break; 633 | case Tdoc: 634 | case Tgif: 635 | case Timage: 636 | page(l); 637 | break; 638 | case Tsearch: 639 | if(eenter("Search:", buf, sizeof buf, mouse)>0){ 640 | s = smprint("%s\t%s", l->sel, buf); 641 | visit(mklink(l->addr, s, Tmenu), 1); 642 | free(s); 643 | } 644 | break; 645 | case Tdos: 646 | case Tbinary: 647 | case Tbinhex: 648 | case Tuuencoded: 649 | snprint(buf, sizeof buf, "%s", linktofile(l)); 650 | if(eenter("Save as:", buf, sizeof buf, mouse)>0){ 651 | save(l, buf); 652 | } 653 | break; 654 | default: 655 | message("unhandled item type '%s'", Typestr[l->type]); 656 | break; 657 | } 658 | } 659 | 660 | void 661 | backhit(Panel *p, int b) 662 | { 663 | USED(p); 664 | if(b!=1) 665 | return; 666 | if(hist==nil || hist->p==nil) 667 | return; 668 | hist->p->n = hist; 669 | hist = hist->p; 670 | seturl(hist->m->link); 671 | show(hist->m); 672 | } 673 | 674 | void 675 | nexthit(Panel *p, int b) 676 | { 677 | USED(p); 678 | if(b!=1) 679 | return; 680 | if(hist==nil || hist->n==nil) 681 | return; 682 | hist = hist->n; 683 | seturl(hist->m->link); 684 | show(hist->m); 685 | } 686 | 687 | void 688 | reloadhit(Panel *p, int b) 689 | { 690 | USED(p); 691 | if(b!=1) 692 | return; 693 | visit(hist->m->link, 0); 694 | } 695 | 696 | void 697 | menuhit(int button, int item) 698 | { 699 | USED(button); 700 | 701 | switch(item){ 702 | case Msearch: 703 | search(); 704 | break; 705 | case Maddbookmark: 706 | addbookmark(); 707 | break; 708 | case Mbookmarks: 709 | showbookmarks(); 710 | break; 711 | case Mexit: 712 | exits(nil); 713 | break; 714 | } 715 | } 716 | 717 | void 718 | entryhit(Panel *p, char *t) 719 | { 720 | Link *l; 721 | 722 | USED(p); 723 | switch(strlen(t)){ 724 | case 0: 725 | return; 726 | case 1: 727 | switch(*t){ 728 | case 'b': 729 | backhit(backp, 1); 730 | break; 731 | case 'n': 732 | nexthit(fwdp, 1); 733 | break; 734 | case 'q': 735 | exits(nil); 736 | break; 737 | default: 738 | message("unknown command %s", t); 739 | break; 740 | } 741 | break; 742 | default: 743 | l = urltolink(t); 744 | if(l==nil) 745 | message("invalid url %s", t); 746 | else 747 | visit(l, 1); 748 | } 749 | plinitentry(entryp, PACKN|FILLX, 0, "", entryhit); 750 | pldraw(root, screen); 751 | } 752 | 753 | void 754 | mkpanels(void) 755 | { 756 | Panel *p, *ybar, *xbar, *m; 757 | 758 | m = plmenu(0, 0, menu3, PACKN|FILLX, menuhit); 759 | root = plpopup(0, EXPAND, 0, 0, m); 760 | p = plgroup(root, PACKN|FILLX); 761 | statusp = pllabel(p, PACKN|FILLX, "gopher!"); 762 | plplacelabel(statusp, PLACEW); 763 | plbutton(p, PACKW|BITMAP|NOBORDER, backi, backhit); 764 | plbutton(p, PACKW|BITMAP|NOBORDER, fwdi, nexthit); 765 | plbutton(p, PACKW|BITMAP|NOBORDER, reloadi, reloadhit); 766 | pllabel(p, PACKW, "Go:"); 767 | entryp = plentry(p, PACKN|FILLX, 0, "", entryhit); 768 | p = plgroup(root, PACKN|FILLX); 769 | urlp = pllabel(p, PACKN|FILLX, ""); 770 | plplacelabel(urlp, PLACEW); 771 | p = plgroup(root, PACKN|EXPAND); 772 | ybar = plscrollbar(p, PACKW|USERFL); 773 | xbar = plscrollbar(p, IGNORE); 774 | textp = pltextview(p, PACKE|EXPAND, ZP, nil, nil); 775 | plscroll(textp, xbar, ybar); 776 | plgrabkb(entryp); 777 | } 778 | 779 | void 780 | eresized(int new) 781 | { 782 | if(new && getwindow(display, Refnone)<0) 783 | sysfatal("cannot reattach: %r"); 784 | plpack(root, screen->r); 785 | pldraw(root, screen); 786 | } 787 | 788 | Image* 789 | loadicon(Rectangle r, uchar *data, int ndata) 790 | { 791 | Image *i; 792 | int n; 793 | 794 | i = allocimage(display, r, RGBA32, 0, DNofill); 795 | if(i==nil) 796 | sysfatal("allocimage: %r"); 797 | n = loadimage(i, r, data, ndata); 798 | if(n<0) 799 | sysfatal("loadimage: %r"); 800 | return i; 801 | } 802 | 803 | void 804 | loadicons(void) 805 | { 806 | Rectangle r = Rect(0,0,16,16); 807 | 808 | backi = loadicon(r, ibackdata, sizeof ibackdata); 809 | fwdi = loadicon(r, ifwddata, sizeof ifwddata); 810 | reloadi = loadicon(r, ireloaddata, sizeof ireloaddata); 811 | } 812 | 813 | void scrolltext(int dy, int whence) 814 | { 815 | Scroll s; 816 | 817 | s = plgetscroll(textp); 818 | switch(whence){ 819 | case 0: 820 | s.pos.y = dy; 821 | break; 822 | case 1: 823 | s.pos.y += dy; 824 | break; 825 | case 2: 826 | s.pos.y = s.size.y+dy; 827 | break; 828 | } 829 | if(s.pos.y > s.size.y) 830 | s.pos.y = s.size.y; 831 | if(s.pos.y < 0) 832 | s.pos.y = 0; 833 | plsetscroll(textp, s); 834 | /* BUG: there is a redraw issue when scrolling 835 | This fixes the issue albeit not properly */ 836 | pldraw(textp, screen); 837 | } 838 | 839 | void 840 | ensurebdir(void) 841 | { 842 | char *home, *tmp; 843 | int fd; 844 | 845 | home = getenv("home"); 846 | if(home){ 847 | tmp = smprint("%s/lib", home); 848 | fd = create(tmp, OREAD, DMDIR|0777); 849 | if(fd>0) 850 | close(fd); 851 | free(tmp); 852 | bdir = smprint("%s/lib/gopher", home); 853 | fd = create(bdir, OREAD, DMDIR|0777); 854 | if(fd>0) 855 | close(fd); 856 | }else 857 | bdir = strdup("/tmp"); 858 | } 859 | 860 | void 861 | main(int argc, char *argv[]) 862 | { 863 | enum { Eplumb = 128 }; 864 | Event e; 865 | Link *l; 866 | char *url; 867 | Plumbmsg *pm; 868 | 869 | if(argc == 2) 870 | url = argv[1]; 871 | else 872 | url = "gopher.floodgap.com"; 873 | quotefmtinstall(); 874 | ensurebdir(); 875 | if(initdraw(nil, nil, "gopher")<0) 876 | sysfatal("initdraw: %r"); 877 | einit(Emouse|Ekeyboard); 878 | plinit(screen->depth); 879 | loadicons(); 880 | mkpanels(); 881 | l = urltolink(url); 882 | if(l==nil) 883 | message("invalid url %s", url); 884 | else 885 | visit(l, 1); 886 | eresized(0); 887 | eplumb(Eplumb, "gopher"); 888 | for(;;){ 889 | switch(event(&e)){ 890 | case Eplumb: 891 | pm = e.v; 892 | if(pm->ndata > 0){ 893 | l = urltolink(pm->data); 894 | if(l!=nil) 895 | visit(l, 1); 896 | } 897 | plumbfree(pm); 898 | break; 899 | case Ekeyboard: 900 | switch(e.kbdc){ 901 | default: 902 | plgrabkb(entryp); 903 | plkeyboard(e.kbdc); 904 | break; 905 | case Khome: 906 | scrolltext(0, 0); 907 | break; 908 | case Kup: 909 | scrolltext(-textp->size.y/4, 1); 910 | break; 911 | case Kpgup: 912 | scrolltext(-textp->size.y/2, 1); 913 | break; 914 | case Kdown: 915 | scrolltext(textp->size.y/4, 1); 916 | break; 917 | case Kpgdown: 918 | scrolltext(textp->size.y/2, 1); 919 | break; 920 | case Kend: 921 | scrolltext(-textp->size.y, 2); 922 | break; 923 | case Kdel: 924 | exits(nil); 925 | break; 926 | } 927 | break; 928 | case Emouse: 929 | mouse = &e.mouse; 930 | if(mouse->buttons & (8|16) && ptinrect(mouse->xy, textp->r)){ 931 | if(mouse->buttons & 8) 932 | scrolltext(textp->r.min.y - mouse->xy.y, 1); 933 | else 934 | scrolltext(mouse->xy.y - textp->r.min.y, 1); 935 | break; 936 | } 937 | plmouse(root, mouse); 938 | /* BUG: there is a redraw issue when scrolling 939 | This fixes the issue albeit not properly */ 940 | if(mouse->buttons != 4) 941 | pldraw(textp, screen); 942 | break; 943 | } 944 | } 945 | } 946 | 947 | --------------------------------------------------------------------------------