summaryrefslogtreecommitdiff
path: root/sys/src/cmd/abaco/page.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/abaco/page.c839
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;
+}