diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/abaco/page.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/abaco/page.c')
-rwxr-xr-x | sys/src/cmd/abaco/page.c | 839 |
1 files changed, 839 insertions, 0 deletions
diff --git a/sys/src/cmd/abaco/page.c b/sys/src/cmd/abaco/page.c new file mode 100755 index 000000000..6f75f6afb --- /dev/null +++ b/sys/src/cmd/abaco/page.c @@ -0,0 +1,839 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> +#include <memdraw.h> +#include <thread.h> +#include <cursor.h> +#include <mouse.h> +#include <keyboard.h> +#include <frame.h> +#include <plumb.h> +#include <html.h> +#include "dat.h" +#include "fns.h" + +static void pageload1(Page *, Url *, int); + +static +void +addchild(Page *p, Page *c) +{ + Page *t; + + c->parent = p; + c->w = p->w; + c->b = p->b; + c->col = p->col; + c->row = p->row; + if(p->child == nil) + p->child = c; + else{ + for(t=p->child; t->next!=nil; t=t->next) + ; + t->next = c; + } +} + +static +void +loadchilds(Page *p, Kidinfo *k) +{ + Runestr rs; + Kidinfo *t; + Page *c; + + addrefresh(p, "loading frames..."); + p->kidinfo = k; + for(t=k->kidinfos; t!=nil; t=t->next){ + c = emalloc(sizeof(Page)); + addchild(p, c); + if(t->isframeset){ + c->url = urldup(p->url); + loadchilds(c, t); + }else{ + c->kidinfo = t; + /* this check shouldn't be necessary, but... */ + if(t->src){ + rs.r = urlcombine(p->url->act.r, t->src); + rs.nr = runestrlen(rs.r); + pageload1(c, urlalloc(&rs, nil, HGet), FALSE); + closerunestr(&rs); + } + } + } +} + +static struct { + char *mime; + char *filter; +}filtertab[] = { + "image/gif", "gif -t9", + "image/jpeg", "jpg -t9", + "image/jpg", "jpg -t9", + "image/pjpeg", "jpg -t9", + "image/png", "png -t9", + "image/ppm", "ppm -t9", + nil, nil, +}; + +char * +getfilter(Rune *r, int x, int y) +{ + char buf[128]; + int i; + + snprint(buf, sizeof(buf), "%S", r); + for(i=0; filtertab[i].mime!=nil; i++) + if(cistrncmp(buf, filtertab[i].mime, strlen(filtertab[i].mime)) == 0) + break; + + if(filtertab[i].filter == nil) + return nil; + + if(x==0 && y==0) + return smprint("%s", filtertab[i].filter); + if(x!=0 && y!=0) + return smprint("%s | resample -x %d -y %d", filtertab[i].filter, x, y); + if(x != 0) + return smprint("%s | resample -x %d", filtertab[i].filter, x); + /* y != 0 */ + return smprint("%s | resample -y %d", filtertab[i].filter, y); +} + +static Cimage *cimages = nil; +static QLock cimagelock; + +static +void +freecimage(Cimage *ci) +{ + Cimage *ci1; + + qlock(&cimagelock); + if(decref(ci) == 0){ + if(ci->i) + freeimage(ci->i); + else if(ci->mi) + freememimage(ci->mi); + urlfree(ci->url); + ci1 = cimages; + if(ci1 == ci) + cimages = ci->next; + else{ + while(ci1){ + if(ci1->next == ci){ + ci1->next = ci->next; + break; + } + ci1 = ci1->next; + } + } + free(ci); + } + qunlock(&cimagelock); +} + +static +void +closeimages(Page *p) +{ + int i; + + for(i=0; i<p->ncimage; i++) + freecimage(p->cimage[i]); + free(p->cimage); + p->cimage =nil; + p->ncimage = 0; +} + +static +Cimage * +loadimg(Rune *src, int x , int y) +{ + Channel *sync; + Cimage *ci; + Runestr rs; + Exec *e; + char *filter; + int fd, p[2], q[2]; + + ci = emalloc(sizeof(Cimage)); + rs. r = src; + rs.nr = runestrlen(rs.r); + ci->url = urlalloc(&rs, nil, HGet); + fd = urlopen(ci->url); + if(fd < 0){ + Err1: + return ci; + } + filter = getfilter(ci->url->ctype.r, x, y); + if(filter == nil){ + werrstr("%S unsupported: %S", ci->url->ctype.r, ci->url->act.r); + Err2: + close(fd); + goto Err1; + } + + if(pipe(p)<0 || pipe(q)<0) + error("can't create pipe"); + close(p[0]); + p[0] = fd; + sync = chancreate(sizeof(ulong), 0); + if(sync == nil) + error("can't create channel"); + e = emalloc(sizeof(Exec)); + e->p[0] = p[0]; + e->p[1] = p[1]; + e->q[0] = q[0]; + e->q[1] = q[1]; + e->cmd = filter; + e->sync = sync; + proccreate(execproc, e, STACK); + recvul(sync); + chanfree(sync); + close(p[0]); + close(p[1]); + close(q[1]); + + ci->mi = readmemimage(q[0]); + close(q[0]); + if(ci->mi == nil){ + werrstr("can't read image"); + goto Err2; + } + free(filter); + return ci; +} + +static +Cimage * +findimg(Rune *s) +{ + Cimage *ci; + + qlock(&cimagelock); + for(ci=cimages; ci!=nil; ci=ci->next) + if(runestrcmp(ci->url->src.r, s) == 0) + break; + + qunlock(&cimagelock); + return ci; +} + +void +loadimages(Page *p) +{ + Cimage *ci; + Iimage *i; + Rune *src; + + addrefresh(p, "loading images..."); + reverseimages(&p->doc->images); + for(i=p->doc->images; i!=nil; i=i->nextimage){ + if(p->aborting) + break; + src = urlcombine(getbase(p), i->imsrc); + ci = findimg(src); + if(ci == nil){ + ci = loadimg(src, i->imwidth, i->imheight); + qlock(&cimagelock); + ci->next = cimages; + cimages = ci; + qunlock(&cimagelock); + } + free(src); + incref(ci); + i->aux = ci; + p->cimage = erealloc(p->cimage, ++p->ncimage*sizeof(Cimage *)); + p->cimage[p->ncimage-1] = ci; + p->changed = TRUE; + addrefresh(p, ""); + } +} + +static char *mimetab[] = { + "text/html", + "application/xhtml", + nil, +}; + +static +void +pageloadproc(void *v) +{ + Page *p; + char buf[BUFSIZE], *s; + long n, l; + int fd, i, ctype; + + threadsetname("pageloadproc"); + rfork(RFFDG); + + p = v; + addrefresh(p, "opening: %S...", p->url->src.r); + fd = urlopen(p->url); + if(fd < 0){ + addrefresh(p, "%S: %r", p->url->src.r); + Err: + p->loading = FALSE; + return; + } + if(runestrlen(p->url->ctype.r) == 0) /* assume .html when headers don't say anyting */ + goto Html; + + snprint(buf, sizeof(buf), "%S", p->url->ctype.r); + for(i=0; mimetab[i]!=nil; i++) + if(cistrncmp(buf, mimetab[i], strlen(mimetab[i])) == 0) + break; + + if(mimetab[i]){ + Html: + ctype = TextHtml; + }else if(cistrncmp(buf, "text/", 5) == 0) + ctype = TextPlain; + else{ + close(fd); + addrefresh(p, "%S: unsupported mime type: '%S'", p->url->act.r, p->url->ctype.r); + goto Err; + } + addrefresh(p, "loading: %S...", p->url->src.r); + s = nil; + l = 0; + while((n=read(fd, buf, sizeof(buf))) > 0){ + if(p->aborting){ + if(s){ + free(s); + s = nil; + } + break; + } + s = erealloc(s, l+n+1); + memmove(s+l, buf, n); + l += n; + s[l] = '\0'; + } + close(fd); + n = l; + if(s){ + s = convert(p->url->ctype, s, &n); + p->items = parsehtml((uchar *)s, n, p->url->act.r, ctype, UTF_8, &p->doc); + free(s); + fixtext(p); + if(ctype==TextHtml && p->aborting==FALSE){ + p->changed = TRUE; + addrefresh(p, ""); + if(p->doc->doctitle){ + p->title.r = erunestrdup(p->doc->doctitle); + p->title.nr = runestrlen(p->title.r); + } + p->loading = XXX; + if(p->doc->kidinfo) + loadchilds(p, p->doc->kidinfo); + else if(p->doc->images) + loadimages(p); + } + } + p->changed = TRUE; + p->loading = FALSE; + addrefresh(p, ""); +} + +static +void +pageload1(Page *p, Url *u, int dohist) +{ + pageclose(p); + p->loading = TRUE; + p->url = u; + if(dohist) + winaddhist(p->w, p->url); + proccreate(pageloadproc, p, STACK); +} + +void +pageload(Page *p, Url *u, int dohist) +{ + if(p->parent == nil) + textset(&p->w->url, u->src.r, u->src.nr); + draw(p->b, p->all, display->white, nil, ZP); + pageload1(p, u, dohist); +} + +void +pageget(Page *p, Runestr *src, Runestr *post, int m, int dohist) +{ + pageload(p, urlalloc(src, post, m), dohist); +} + +void +pageclose(Page *p) +{ + Page *c, *nc; + + if(p == selpage) + selpage = nil; + pageabort(p); + closeimages(p); + urlfree(p->url); + p->url = nil; + if(p->doc){ + freedocinfo(p->doc); + p->doc = nil; + } + layfree(p->lay); + p->lay = nil; + freeitems(p->items); + p->items = nil; + for(c=p->child; c!=nil; c=nc){ + nc = c->next; + pageclose(c); + free(c); + } + p->child = nil; + closerunestr(&p->title); + closerunestr(&p->refresh.rs); + p->refresh.t = 0; + p->pos = ZP; + p->top = ZP; + p->bot = ZP; + p->loading = p->aborting = FALSE; +} + +int +pageabort(Page *p) +{ + Page *c; + + for(c=p->child; c!=nil; c=c->next) + pageabort(c); + + p->aborting = TRUE; + while(p->loading) + sleep(100); + + p->aborting = FALSE; + return TRUE; +} + + +static Image *tmp; + +void +tmpresize(void) +{ + if(tmp) + freeimage(tmp); + + tmp = eallocimage(display, Rect(0,0,Dx(screen->r),Dy(screen->r)), screen->chan, 0, -1); +} + +static +void +renderchilds(Page *p) +{ + Rectangle r; + Kidinfo *k; + Page *c; + int i, j, x, y, *w, *h; + + draw(p->b, p->all, display->white, nil, ZP); + r = p->all; + y = r.min.y; + c = p->child; + k = p->kidinfo; + frdims(k->rows, k->nrows, Dy(r), &h); + frdims(k->cols, k->ncols, Dx(r), &w); + for(i=0; i<k->nrows; i++){ + x = r.min.x; + for(j=0; j<k->ncols; j++){ + if(c->aborting) + return; + c->b = p->b; + c->all = Rect(x,y,x+w[j],y+h[i]); + c->w = p->w; + pagerender(c); + c = c->next; + x += w[j]; + } + y += h[i]; + } + free(w); + free(h); +} + +static +void +pagerender1(Page *p) +{ + Rectangle r; + + r = p->all; + p->hscrollr = r; + p->hscrollr.min.y = r.max.y-Scrollsize; + p->vscrollr = r; + p->vscrollr.max.x = r.min.x+Scrollsize; + r.max.y -= Scrollsize; + r.min.x += Scrollsize; + p->r = r; + p->vscrollr.max.y = r.max.y; + p->hscrollr.min.x = r.min.x; + laypage(p); + pageredraw(p); +} + +void +pagerender(Page *p) +{ + if(p->child && p->loading==FALSE) + renderchilds(p); + else if(p->doc) + pagerender1(p); +} + +void +pageredraw(Page *p) +{ + Rectangle r; + + r = p->lay->r; + if(Dx(r)==0 || Dy(r)==0){ + draw(p->b, p->r, display->white, nil, ZP); + return; + } + if(tmp == nil) + tmpresize(); + + p->selecting = FALSE; + draw(tmp, tmp->r, getbg(p), nil, ZP); + laydraw(p, tmp, p->lay); + draw(p->b, p->r, tmp, nil, tmp->r.min); + r = p->vscrollr; + r.min.y = r.max.y; + r.max.y += Scrollsize; + draw(p->b, r, tagcols[HIGH], nil, ZP); + draw(p->b, insetrect(r, 1), tagcols[BACK], nil, ZP); + pagescrldraw(p); +} + +static +void +pageselect1(Page *p) /* when called, button 1 is down */ +{ + Point mp, npos, opos; + int b, scrled, x, y; + + b = mouse->buttons; + mp = mousectl->xy; + opos = getpt(p, mp); + do{ + x = y = 0; + if(mp.x < p->r.min.x) + x -= p->r.min.x-mp.x; + else if(mp.x > p->r.max.x) + x += mp.x-p->r.max.x; + if(mp.y < p->r.min.y) + y -= (p->r.min.y-mp.y)*Panspeed; + else if(mp.y > p->r.max.y) + y += (mp.y-p->r.max.y)*Panspeed; + + scrled = pagescrollxy(p, x, y); + npos = getpt(p, mp); + if(opos.y < npos.y){ + p->top = opos; + p->bot = npos; + }else{ + p->top = npos; + p->bot = opos; + } + pageredraw(p); + if(scrled == TRUE) + scrsleep(100); + else + readmouse(mousectl); + + mp = mousectl->xy; + }while(mousectl->buttons == b); +} + +static Rune left1[] = { L'{', L'[', L'(', L'<', L'«', 0 }; +static Rune right1[] = { L'}', L']', L')', L'>', L'»', 0 }; +static Rune left2[] = { L'\'', L'"', L'`', 0 }; + +static +Rune *left[] = { + left1, + left2, + nil +}; +static +Rune *right[] = { + right1, + left2, + nil +}; + +void +pagedoubleclick(Page *p) +{ + Point xy; + Line *l; + Box *b; + + xy = getpt(p, mouse->xy); + l = linewhich(p->lay, xy); + if(l==nil || l->hastext==FALSE) + return; + + if(xy.x<l->boxes->r.min.x && hasbrk(l->state)){ /* beginning of line? */ + p->top = l->boxes->r.min; + if(l->next && !hasbrk(l->next->state)){ + for(l=l->next; l->next!=nil; l=l->next) + if(hasbrk(l->next->state)) + break; + } + p->bot = l->lastbox->r.max;; + }else if(xy.x>l->lastbox->r.max.x && hasbrk(l->next->state)){ /* end of line? */ + p->bot = l->lastbox->r.max; + if(!hasbrk(l->state) && l->prev!=nil){ + for(l=l->prev; l->prev!=nil; l=l->prev) + if(hasbrk(l->state)) + break; + } + p->top = l->boxes->r.min; + }else{ + b = pttobox(l, xy); + if(b!=nil && b->i->tag==Itexttag){ + p->top = b->r.min; + p->bot = b->r.max; + } + } + p->top.y += 2; + p->bot.y -= 2; + pageredraw(p); +} + +static uint clickmsec; + +void +pageselect(Page *p) +{ + int b, x, y; + + + selpage = p; + /* + * To have double-clicking and chording, we double-click + * immediately if it might make sense. + */ + b = mouse->buttons; + if(mouse->msec-clickmsec<500){ + pagedoubleclick(p); + x = mouse->xy.x; + y = mouse->xy.y; + /* stay here until something interesting happens */ + do + readmouse(mousectl); + while(mouse->buttons==b && abs(mouse->xy.x-x)<3 && abs(mouse->xy.y-y)<3); + mouse->xy.x = x; /* in case we're calling pageselect1 */ + mouse->xy.y = y; + } + if(mousectl->buttons == b) + pageselect1(p); + + if(eqpt(p->top, p->bot)){ + if(mouse->msec-clickmsec<500) + pagedoubleclick(p); + else + clickmsec = mouse->msec; + } + while(mouse->buttons){ + mouse->msec = 0; + b = mouse->buttons; + if(b & 2) /* snarf only */ + cut(nil, nil, TRUE, FALSE, nil, 0); + while(mouse->buttons == b) + readmouse(mousectl); + } +} + +Page * +pagewhich(Page *p, Point xy) +{ + Page *c; + + if(p->child == nil) + return p; + + for(c=p->child; c!=nil; c=c->next) + if(ptinrect(xy, c->all)) + return pagewhich(c, xy); + + return nil; +} + +void +pagemouse(Page *p, Point xy, int but) +{ + Box *b; + + p = pagewhich(p, xy); + if(p == nil) + return; + + if(pagerefresh(p)) + return; + + if(p->lay == nil) + return; + + if(ptinrect(xy, p->vscrollr)){ + pagescroll(p, but, FALSE); + return; + } + if(ptinrect(xy, p->hscrollr)){ + pagescroll(p, but, TRUE); + return; + } + xy = getpt(p, xy); + b = boxwhich(p->lay, xy); + if(b && b->mouse) + b->mouse(b, p, but); + else if(but == 1) + pageselect(p); +} + +void +pagetype(Page *p, Rune r, Point xy) +{ + Box *b; + int x, y; + + p = pagewhich(p, xy); + if(p == nil) + return; + + if(pagerefresh(p)) + return; + + if(p->lay == nil) + return; + + /* text field? */ + xy = getpt(p, xy); + b = boxwhich(p->lay, xy); + if(b && b->key){ + b->key(b, p, r); + return; + } + /* ^H: same as 'Back' */ + if(r == 0x08){ + wingohist(p->w, FALSE); + return; + } + + x = 0; + y = 0; + switch(r){ + case Kleft: + x -= Dx(p->r)/2; + break; + case Kright: + x += Dx(p->r)/2; + break; + case Kdown: + case Kscrollonedown: + y += Dy(p->r)/2; + break; + case Kpgdown: + y += Dy(p->r); + break; + case Kup: + case Kscrolloneup: + y -= Dy(p->r)/2; + break; + case Kpgup: + y -= Dy(p->r); + break; + case Khome: + y -= Dy(p->lay->r); /* force p->pos.y = 0 */ + break; + case Kend: + y = Dy(p->lay->r) - Dy(p->r); + break; + default: + return; + } + if(pagescrollxy(p, x, y)) + pageredraw(p); +} + +void +pagesnarf(Page *p) +{ + Runestr rs; + + memset(&rs, 0, sizeof(Runestr)); + laysnarf(p, p->lay, &rs); + putsnarf(&rs); + closerunestr(&rs); +} + +void +pagesetrefresh(Page *p) +{ + Runestr rs; + Rune *s, *q, *t; + char *v; + int n; + + if(!p->doc || !p->doc->refresh) + return; + + s = p->doc->refresh; + q = runestrchr(s, L'='); + if(q == nil) + return; + q++; + if(!q) + return; + n = runestrlen(q); + if(*q == L'''){ + q++; + n -= 2; + } + if(n <= 0) + return; + t = runesmprint("%.*S", n, q); + rs.r = urlcombine(getbase(p), t); + rs.nr = runestrlen(rs.r); + copyrunestr(&p->refresh.rs, &rs); + closerunestr(&rs); + free(t); + + /* now the time */ + q = runestrchr(s, L';'); + if(q){ + v = smprint("%.*S", (int)(q-s), s); + p->refresh.t = atoi(v); + free(v); + }else + p->refresh.t = 1; + + p->refresh.t += time(0); +} + +int +pagerefresh(Page *p) +{ + int t; + + if(!p->refresh.t) + return 0; + + t = p->refresh.t - time(0); + if(t > 0) + return 0; + + pageget(p, &p->refresh.rs, nil, HGet, FALSE); + return 1; +} |