Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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);
        }
}