summaryrefslogtreecommitdiff
path: root/sys/src/cmd/abaco/html.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/html.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/abaco/html.c')
-rwxr-xr-xsys/src/cmd/abaco/html.c1072
1 files changed, 1072 insertions, 0 deletions
diff --git a/sys/src/cmd/abaco/html.c b/sys/src/cmd/abaco/html.c
new file mode 100755
index 000000000..9a53eec53
--- /dev/null
+++ b/sys/src/cmd/abaco/html.c
@@ -0,0 +1,1072 @@
+#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;
+
+ 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)
+{
+ settables(p);
+ layfree(p->lay);
+ 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(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);
+ }
+}