ref: f42e53655e9a2a1b516326f6522fba88db59a81c
dir: /sys/src/cmd/abaco/html.c/
#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 sizeitem(Lay *, Item *); static void sizetext(Lay *lay, Itext *i) { lay->font = getfont(i->fnt); i->height = lay->font->height + 2*Space; i->width = runestringwidth(lay->font, i->s); i->width += runestringnwidth(lay->font, L" ", 1); } static void sizerule(Lay *lay, Irule *i) { i->width = lay->width; i->height = Space + i->size + Space; } static void sizeimage(Lay *, Iimage *i) { Cimage *ci; ci = (Cimage *)i->aux; if(ci==nil) return; if(ci->i == nil) getimage(ci, i->altrep); if(ci->i == nil) return; i->width = Dx(ci->i->r) + i->border + i->hspace; i->height = Dy(ci->i->r) + i->border + i->vspace; } static void sizetextfield(Lay *, Iformfield *i) { Formfield *ff; Font *f; int w, h; ff = i->formfield; if(ff->ftype == Ftextarea){ w = ff->cols; h = ff->rows; }else{ w = ff->size; h = 1; } f = getfont(WFont); i->width = runestringnwidth(f, L"0", 1)*w + 2*(Space+Border+Margin); i->width += Scrollsize+Scrollgap; i->height = f->height*h + 2*(Space+Border+Margin); } static void sizecheck(Lay *, Iformfield *i) { i->width = Boxsize + Space; i->height = Boxsize; } static void sizebutton(Lay *, Iformfield *i) { Font *f; int x; x = Margin + Border + Space; f = getfont(WFont); i->width = runestringwidth(f, i->formfield->value) + 2*x + Space; i->height = f->height + 2*x; } static void sizefimage(Lay *lay, Iformfield *i) { Iimage *ii; ii = (Iimage *)i->formfield->image; sizeimage(lay, ii); i->width = ii->width; i->height = ii->height; } static void sizeselect(Lay *, Iformfield *i) { Option *o; Font *f; int x; f = getfont(WFont); i->width = 0; for(o=i->formfield->options; o!=nil; o=o->next) i->width = max(i->width, runestringwidth(f, o->display)); x = Margin + Border + Space; i->width += 2*x; i->height = f->height+2*x; } static void sizeformfield(Lay *lay, Iformfield *i) { int type; type = i->formfield->ftype; if(type==Ftext || type==Ftextarea || type==Fpassword) sizetextfield(lay, i); else if(type==Fcheckbox || type==Fradio) sizecheck(lay, i); else if(type==Fbutton || type==Freset || type==Fsubmit) sizebutton(lay, i); else if(type == Fimage) sizefimage(lay, i); else if(type == Fselect) sizeselect(lay, i); } static void sizetable(Lay *lay, Itable *i) { tablesize(i->table, lay->width); i->width = i->table->totw; i->height = i->table->toth; } static void sizefloat(Lay *lay, Ifloat *i) { sizeitem(lay, i->item); i->width = i->item->width; i->height = i->item->height; } static void sizespacer(Lay *lay, Ispacer *i) { if(i->spkind != ISPnull){ if(i->spkind == ISPhspace) i->width = stringnwidth(lay->font, " ", 1); i->height = lay->font->height + 2*Space; } } static void sizeitem(Lay *lay, Item *i) { switch(i->tag){ case Itexttag: sizetext(lay, (Itext *)i); break; case Iruletag: sizerule(lay, (Irule *)i); break; case Iimagetag: sizeimage(lay, (Iimage *)i); break; case Iformfieldtag: sizeformfield(lay, (Iformfield *)i); break; case Itabletag: sizetable(lay, (Itable *)i); break; case Ifloattag: sizefloat(lay, (Ifloat *)i); break; case Ispacertag: sizespacer(lay, (Ispacer *)i); break; default: error("can't happen"); } } static void drawtext(Box *b, Page *p, Image *im) { Rectangle r, r1; Image *c; Point pt; Font *f; Itext *i; int q0, q1; r = rectsubpt(b->r, p->pos); i = (Itext *)b->i; f = getfont(i->fnt); if(istextsel(p, b->r, &q0, &q1, i->s, f)){ r1 = r; if(q0 > 0) r1.min.x += runestringnwidth(f, i->s, q0); if(q1 > 0) r1.max.x = r1.min.x + runestringnwidth(f, i->s+q0, q1-q0); draw(im, r1, textcols[HIGH], nil, ZP); } c = getcolor(i->fg); runestringbg(im, r.min, c, ZP, f, i->s, im, addpt(r.min, im->r.min)); if(i->ul == ULnone) return; if(i->ul == ULmid) r.min.y += f->height/2; else r.min.y +=f->height-1; pt = r.min; pt.x += runestringwidth(f, i->s); line(im, r.min, pt, Enddisc, Enddisc, 0, c, ZP); } static void drawrule(Box *b, Page *p, Image *im) { Rectangle r; Irule *i; i = ((Irule *)b->i); r = rectsubpt(b->r, p->pos); r.min.y += Space; r.max.y -=Space; draw(im, r, getcolor(i->color), nil, ZP); } static void drawimage(Box *b, Page *p, Image *im) { Rectangle r; Cimage *ci; Iimage *i; Image *c; if(b->i->tag==Iimagetag) i = (Iimage *)b->i; else i = (Iimage *)((Iformfield *)b->i)->formfield->image; ci = (Cimage *)i->aux; if(ci==nil || ci->i==nil) return; r = rectsubpt(b->r, p->pos); r.min.x += i->border + i->hspace; r.min.y += i->border + i->vspace; r.max.x -= i->border + i->hspace; r.max.y -= i->border + i->vspace; draw(im, r, ci->i, nil, ci->i->r.min); if(i->border){ if(i->anchorid >= 0) c = getcolor(p->doc->link); else c = display->black; border(im, r, i->border, c, ZP); } } static void drawtextfield(Image *im, Rectangle r, Iformfield *i) { Formfield *ff; Image *c[3]; Text *t; Font *f; r = insetrect(r, Space); colarray(c, getcolor(Dark), getcolor(Light), display->white, 1); rect3d(im, r, Border, c, ZP); r = insetrect(r, Border+Margin); if(i->aux == nil){ ff = i->formfield; t = emalloc(sizeof(Text)); if(ff->ftype == Ftextarea) t->what = Textarea; else t->what = Entry; if(ff->ftype == Fpassword) f = passfont; else f = getfont(WFont); textinit(t, im, r, f, textcols); if(ff->value!=nil){ textinsert(t, 0, ff->value, runestrlen(ff->value)); textsetselect(t, t->rs.nr, t->rs.nr); } if(t->what == Textarea) textscrdraw(t); i->aux = t; }else textresize(i->aux, im, r); } void drawcheck(Image *im, Rectangle r, Formfield *f) { Image *c[3]; Point pt; int n; if(f->flags & FFchecked) colarray(c, getcolor(Dark), getcolor(Light), getcolor(Red), TRUE); else colarray(c, getcolor(Dark), getcolor(Light), getcolor(Back), FALSE); if(f->ftype == Fradio){ n = Boxsize/2-1; pt = addpt(r.min, Pt(n,n)); ellipse3d(im, pt, n, Border, c, ZP); }else rect3d(im, r, Border, c, ZP); } void drawbutton(Image *im, Rectangle r, Formfield *f, int checked) { Image *c[3]; r = insetrect(r, Space); colarray(c, getcolor(Dark), getcolor(Light), getcolor(Back), checked); rect3d(im, r, Border, c, ZP); r.min.x += Border + Margin; r.min.y += Border + Margin; runestringbg(im, r.min, display->black, ZP, getfont(WFont), f->value, c[2], ZP); } void drawselect(Image *im, Rectangle r, Iformfield *i) { Formfield *f; Image *c[3]; f = i->formfield; if(f->options == nil) return; r = insetrect(r, Space); colarray(c, getcolor(Dark), getcolor(Light), display->white, 1); rect3d(im, r, Border, c, ZP); r = insetrect(r, Border+Margin); draw(im, r, textcols[HIGH], nil, ZP); if(i->aux==nil){ i->aux = f->options->display; i->formfield->value = erunestrdup(f->options->value); } runestring(im, r.min, display->black, ZP, getfont(WFont), i->aux); } /* Formfields are a special case */ static void drawformfield(Box *b, Page *p, Image *im) { Formfield *f; int type; f = ((Iformfield *)b->i)->formfield; type =f->ftype; if(istextfield(b->i)) drawtextfield(im, rectsubpt(b->r, p->pos), (Iformfield *)b->i); else if(type==Fcheckbox || type==Fradio) drawcheck(im, rectsubpt(b->r, p->pos), f); else if(type==Fbutton || type==Freset || type==Fsubmit) drawbutton(im, rectsubpt(b->r, p->pos), f, FALSE); else if(type == Fimage) drawimage(b, p, im); else if(type == Fselect) drawselect(im, rectsubpt(b->r, p->pos), (Iformfield *)b->i); } static void drawnull(Box *, Page *, Image *) { } static Page * whichtarget1(Page *p, Rune *r) { Kidinfo *k; Page *c, *ret; k = p->kidinfo; if(k && k->name && runestrcmp(k->name, r)==0) return p; for(c=p->child; c; c=c->next){ ret = whichtarget1(c, r); if(ret) return ret; } return nil; } static Page * whichtarget(Page *p, int t) { Page *r; switch(t){ case FTblank: case FTtop: r = &p->w->page; break; case FTself: r = p; break; case FTparent: r = p->parent; break; default: if(targetname(t) == L"?") error("targetname"); r = whichtarget1(&p->w->page, targetname(t)); } return r ? r: &p->w->page; } static void mouselink(Box *b, Page *p, int but) { Runestr rs; Anchor *a; /* eat mouse */ while(mousectl->buttons) readmouse(mousectl); if(b->i->anchorid < 0) return; /* binary search would be better */ for(a=p->doc->anchors; a!=nil; a=a->next) if(a->index == b->i->anchorid) break; if(a==nil || a->href==nil) return; p = whichtarget(p, a->target); rs.r = urlcombine(getbase(p), a->href); if(rs.r == nil) return; rs.nr = runestrlen(rs.r); if(but == 1) pageget(p, &rs, nil, HGet, p==&p->w->page); else if(but == 2) textset(&p->w->status, rs.r, rs.nr); else if(but == 3) plumbrunestr(&rs, nil); closerunestr(&rs); } static void submit(Page *p, Formfield *formfield, int subfl) { Formfield *f; Form *form; Runestr src, post; Rune *x, *sep, *y, *z; form = formfield->form; x = erunestrdup(L""); sep = L""; for(f=form->fields; f!=nil; f=f->next){ if(f->ftype == Freset) continue; if((f->ftype==Fradio || f->ftype==Fcheckbox) && !(f->flags&FFchecked)) continue; if(f->ftype==Fsubmit && (f!=formfield || !subfl)) continue; if(f->value==nil || f->name==nil || runestrcmp(f->name, L"_no_name_submit_")==0) continue; z = ucvt(f->value); y = runesmprint("%S%S%S=%S", x, sep, f->name, z); free(z); sep = L"&"; free(x); x = y; } p = whichtarget(p, form->target); y = urlcombine(getbase(p), form->action); memset(&src, 0, sizeof(Runestr)); memset(&post, 0, sizeof(Runestr)); if(form->method == HGet){ if(y[runestrlen(y)-1] == L'?') sep = L""; else sep = L"?"; src.r = runesmprint("%S%S%S",y, sep, x); free(x); free(y); }else{ src.r = y; post.r = x; post.nr = runestrlen(x); if(post.nr == 0){ free(post.r); post.r = nil; } } src.nr = runestrlen(src.r); pageget(p, &src, &post, form->method, p==&p->w->page); closerunestr(&src); closerunestr(&post); } static void setradios(Formfield *formfield) { Formfield *f; for(f=formfield->form->fields; f!=nil; f=f->next) if(f->ftype==Fradio && f!=formfield && runestrcmp(f->name, formfield->name)==0) f->flags &=~FFchecked; } static void selectmouse(Box *b, Page *p, int but) { Formfield *f; Option *o; Menu m; char **item; int i, n; f = ((Iformfield *)b->i)->formfield; n = 0; item = nil; for(o=f->options; o!=nil; o=o->next){ item = erealloc(item, ++n*sizeof(char *)); if(o->display) item[n-1] = smprint("%S", o->display); else item[n-1] = estrdup("--"); } if(item == nil) return; item[n] = 0; m.item = item; i = menuhit(but, mousectl, &m, nil); if(i >= 0){ for(o=f->options; o!=nil; o=o->next, i--){ if(i == 0) break; } ((Iformfield *)b->i)->aux = o->display; drawselect(p->b, rectaddpt(rectsubpt(b->r, p->pos), p->r.min), (Iformfield *)b->i); if(f->value != nil) free(f->value); f->value = erunestrdup(o->value); } for(i=0; i< n; i++) free(item[i]); // free(item); } static void mouseform(Box *b, Page *p, int but) { Rectangle r, cr; Formfield *f; Text *t; f = ((Iformfield *)b->i)->formfield; r = rectaddpt(rectsubpt(b->r, p->pos), p->r.min); if(istextfield(b->i)){ cr = p->b->clipr; replclipr(p->b, 0, p->r); t = ((Iformfield *)b->i)->aux; if(p->b != t->b) drawtextfield(p->b, r, (Iformfield *)b->i); textmouse(t, mouse->xy, but); if(f->value) free(f->value); f->value = runesmprint("%.*S", t->rs.nr, t->rs.r); replclipr(p->b, 0, cr); return; } if(but != 1) return; if(f->ftype==Fselect){ selectmouse(b, p, but); return; } if(f->ftype==Fsubmit || f->ftype==Fimage){ if(f->ftype == Fsubmit) drawbutton(p->b, r, f, TRUE); while(mouse->buttons == but) readmouse(mousectl); if(f->ftype == Fsubmit) drawbutton(p->b, r, f, FALSE); if(mouse->buttons==0 && ptinrect(mouse->xy, r)) submit(p, f, TRUE); return; } if(f->ftype==Fradio || f->ftype==Fcheckbox){ if(f->flags&FFchecked){ if(f->ftype==Fcheckbox) f->flags &=~FFchecked; }else{ f->flags |= FFchecked; } if(f->ftype == Fradio) setradios(f); pageredraw(p); } } static void keyform(Box *b, Page *p, Rune r) { Rectangle cr; Formfield *f; Text *t; f = ((Iformfield *)b->i)->formfield; if(r==L'\n' && f->ftype==Ftext){ submit(p, f, FALSE); return; } t = ((Iformfield *)b->i)->aux; cr = p->b->clipr; replclipr(p->b, 0, p->r); if(t->b != p->b) drawtextfield(p->b, rectaddpt(rectsubpt(b->r, p->pos), p->r.min), (Iformfield *)b->i); texttype(t, r); if(f->value) free(f->value); f->value = runesmprint("%.*S", t->rs.nr, t->rs.r); replclipr(p->b, 0, cr); } void boxinit(Box *b) { if(b->i->anchorid) b->mouse = mouselink; /* override mouselink for forms */ if(b->i->tag == Iformfieldtag){ b->mouse = mouseform; if(istextfield(b->i)) b->key = keyform; } switch(b->i->tag){ case Itexttag: b->draw = drawtext; break; case Iruletag: b->draw = drawrule; break; case Iimagetag: b->draw = drawimage; break; case Iformfieldtag: b->draw = drawformfield; break; case Itabletag: b->draw = drawtable; break; case Ifloattag: b->draw = drawnull; break; case Ispacertag: b->draw = drawnull; } } Box * boxalloc(Line *l, Item *i, Rectangle r) { Box *b; b = emalloc(sizeof(Box)); b->i = i; b->r = r; if(l->boxes == nil) l->boxes = b; else{ b->prev = l->lastbox; l->lastbox->next = b; } l->lastbox = b; setmalloctag(b, getcallerpc(&l)); return b; } Box * pttobox(Line *l, Point xy) { Box *b; for(b=l->boxes; b!=nil; b=b->next) if(ptinrect(xy, b->r)) return b; return nil; } static Line * tbtoline(Itable *i, Point xy) { Tablecell *c; for(c=i->table->cells; c!=nil; c=c->next) if(ptinrect(xy, c->lay->r)) return linewhich(c->lay, xy); return nil; } Line * linewhich(Lay *lay, Point xy) { Line *l, *t; Box *b; t = nil; for(l=lay->lines; l!=nil; l=l->next) if(ptinrect(xy, l->r)) break; if(l!=nil && l->hastable){ b = pttobox(l, xy); if(b!=nil && b->i->tag==Itabletag) t = tbtoline((Itable *)b->i, xy); } return t? t: l; } Box * boxwhich(Lay *lay, Point xy) { Line *l; l = linewhich(lay, xy); if(l) return pttobox(l, xy); return nil; } static void justline1(Line *, int); static void justlay(Lay *lay, int x) { Line *l; lay->r.min.x += x; lay->r.max.x += x; for(l=lay->lines; l!=nil; l=l->next) justline1(l, x); } static void justtable(Itable *i, int x) { Tablecell *c; for(c=i->table->cells; c!=nil; c=c->next) justlay(c->lay, x); } static void justline1(Line *l, int x) { Box *b; l->r.min.x += x; l->r.max.x += x; for(b=l->boxes; b!=nil; b=b->next){ if(b->i->tag == Itabletag) justtable((Itable *)b->i, x); b->r.min.x += x; b->r.max.x += x; } } static void justline(Lay *lay, Line *l) { int w, x; w = Dx(l->r); if(w>0 && w<lay->width){ x = 0; if(l->state & IFrjust) x = lay->width - w; else if(l->state & IFcjust) x = lay->width/2 - w/2; if(x > 0) justline1(l, x); } } static void newline(Lay *lay, int state) { Line *l, *last; int indent, nl; last = lay->lastline; if(lay->laying == TRUE) justline(lay, last); lay->r.max.x = max(lay->r.max.x, last->r.max.x); lay->r.max.y = last->r.max.y; indent = ((state&IFindentmask)>>IFindentshift) * Tabspace; nl = (state & IFbrksp) ? 1 : 0; l = emalloc(sizeof(Line)); l->state = state; l->hastext = FALSE; l->hastable = FALSE; l->r.min.x = lay->r.min.x + indent; l->r.min.y = last->r.max.y + font->height*nl; l->r.max = l->r.min; l->prev = last; last->next = l; lay->lastline = l; } static void layitem(Lay *lay, Item *i) { Rectangle r; Line *l; Box *b; if(i->state&IFbrk || i->state&IFbrksp) newline(lay, i->state); else if(lay->lastline->r.max.x+i->width>lay->xwall && forceitem(i)==FALSE) newline(lay, i->state); l = lay->lastline; r = Rect(l->r.max.x, l->r.min.y, l->r.max.x+i->width, l->r.min.y+i->height); l->r.max.x = r.max.x; if(l->r.max.y < r.max.y) l->r.max.y = r.max.y; if(i->tag == Ifloattag) i = ((Ifloat *)i)->item; if(i->tag == Itexttag) l->hastext = TRUE; else if(i->tag == Itabletag && lay->laying==TRUE){ laytable((Itable *)i, r); l->hastable = TRUE; } b = boxalloc(l, i, r); if(lay->laying) boxinit(b); } static void linefix(Lay *lay) { Line *l; for(l=lay->lines; l!=nil; l=l->next){ l->r.min.x = lay->r.min.x; l->r.max.x = lay->r.max.x; } } Lay * layitems(Item *items, Rectangle r, int laying) { Lay *lay; Line *l; Item *i; lay = emalloc(sizeof(Lay)); lay->r.min = r.min; lay->r.max = r.min; lay->xwall = r.max.x; lay->width = Dx(r); lay->laying = laying; l = emalloc(sizeof(Line)); l->r.min = lay->r.min; l->r.max = lay->r.min; l->state = IFbrk; l->boxes = nil; lay->lines = l; lay->lastline = l; lay->font = font; for(i=items; i; i=i->next){ sizeitem(lay, i); layitem(lay, i); } newline(lay, IFbrk); if(laying) linefix(lay); return lay; } void laypage(Page *p) { layfree(p->lay); p->lay = nil; settables(p); p->lay = layitems(p->items, Rect(0,0,Dx(p->r),Dy(p->r)), TRUE); p->lay->r.max.y = max(p->lay->r.max.y, Dy(p->r)); } static void drawline(Page *p, Image *im, Line *l) { Box *b; for(b=l->boxes; b!=nil; b=b->next) b->draw(b, p, im); } void laydraw(Page *p, Image *im, Lay *lay) { Rectangle r; Line *l; r = rectaddpt(p->lay->r, p->pos); for(l=lay->lines; l!=nil; l=l->next){ if(rectXrect(r, l->r)) drawline(p, im, l); } } static void laytablefree(Table *t) { Tablecell *c; for(c=t->cells; c!=nil; c=c->next){ layfree(c->lay); c->lay = nil; } } void layfree(Lay *lay) { Line *l, *nextline; Box *b, *nextbox; void **aux; if(lay == nil) return; for(l=lay->lines; l!=nil; l=nextline){ for(b=l->boxes; b!=nil; b=nextbox){ nextbox = b->next; if(lay->laying==TRUE){ if(b->i->tag==Iformfieldtag && istextfield(b->i)){ aux = &((Iformfield *)b->i)->aux; if(*aux){ textclose(*aux); free(*aux); } *aux = nil; }else if(b->i->tag == Itabletag) laytablefree(((Itable *)b->i)->table); } free(b); } nextline = l->next; free(l); } free(lay); } void laysnarf(Page *p, Lay *lay, Runestr *rs) { Tablecell *c; Itext *i; Font *f; Line *l; Box *b; int q0, q1, n; for(l=lay->lines; l!=nil; l=l->next) for(b=l->boxes; b!=nil; b=b->next){ if(p->selecting && hasbrk(b->i->state)){ rs->r = runerealloc(rs->r, rs->nr+2); rs->r[rs->nr++] = L'\n'; rs->r[rs->nr] = L'\0'; } if(b->i->tag==Itexttag){ i = (Itext *)b->i; f = getfont(i->fnt); if(istextsel(p, b->r, &q0, &q1, i->s, f)){ if(q1 == 0) q1 = runestrlen(i->s); n = q1-q0; if(n == 0) n = runestrlen(i->s); rs->r = runerealloc(rs->r, rs->nr+n+2); runemove(rs->r+rs->nr, i->s+q0, n); rs->nr += n; rs->r[rs->nr++] = L' '; rs->r[rs->nr] = L'\0'; } }else if(b->i->tag == Itabletag) for(c=((Itable *)b->i)->table->cells; c!=nil; c=c->next) if(c->lay) laysnarf(p, c->lay, rs); } }