]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/abaco/html.c
ndb/dns: remove single-ip-address assuptions
[plan9front.git] / sys / src / cmd / abaco / html.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <thread.h>
6 #include <cursor.h>
7 #include <mouse.h>
8 #include <keyboard.h>
9 #include <frame.h>
10 #include <plumb.h>
11 #include <html.h>
12 #include "dat.h"
13 #include "fns.h"
14
15 static void sizeitem(Lay *, Item *);
16
17 static
18 void
19 sizetext(Lay *lay, Itext *i)
20 {
21         lay->font = getfont(i->fnt);
22         i->height = lay->font->height + 2*Space;
23         i->width = runestringwidth(lay->font, i->s);
24         i->width += runestringnwidth(lay->font, L" ", 1);
25 }
26
27 static
28 void
29 sizerule(Lay *lay, Irule *i)
30 {
31         i->width = lay->width;
32         i->height = Space + i->size + Space;
33 }
34
35 static
36 void
37 sizeimage(Lay *, Iimage *i)
38 {
39         Cimage *ci;
40
41         ci = (Cimage *)i->aux;
42
43         if(ci==nil)
44                 return;
45
46         if(ci->i == nil)
47                 getimage(ci, i->altrep);
48         if(ci->i == nil)
49                 return;
50         i->width = Dx(ci->i->r) + i->border + i->hspace;
51         i->height = Dy(ci->i->r) + i->border + i->vspace;
52 }
53
54 static
55 void
56 sizetextfield(Lay *, Iformfield *i)
57 {
58         Formfield *ff;
59         Font *f;
60         int w, h;
61
62         ff = i->formfield;
63         if(ff->ftype == Ftextarea){
64                 w = ff->cols;
65                 h = ff->rows;
66         }else{
67                 w = ff->size;
68                 h = 1;
69         }
70         f = getfont(WFont);
71         i->width = runestringnwidth(f, L"0", 1)*w + 2*(Space+Border+Margin);
72         i->width += Scrollsize+Scrollgap;
73         i->height = f->height*h + 2*(Space+Border+Margin);
74 }
75
76 static
77 void
78 sizecheck(Lay *, Iformfield *i)
79 {
80         i->width = Boxsize + Space;
81         i->height = Boxsize;
82 }
83
84 static
85 void
86 sizebutton(Lay *, Iformfield *i)
87 {
88         Font *f;
89         int x;
90
91         x = Margin + Border + Space;
92         f = getfont(WFont);
93         i->width = runestringwidth(f, i->formfield->value) + 2*x + Space;
94         i->height = f->height + 2*x;
95 }
96
97 static
98 void
99 sizefimage(Lay *lay, Iformfield *i)
100 {
101         Iimage *ii;
102
103         ii = (Iimage *)i->formfield->image;
104         sizeimage(lay, ii);
105         i->width = ii->width;
106         i->height = ii->height;
107 }
108
109 static
110 void
111 sizeselect(Lay *, Iformfield *i)
112 {
113         Option *o;
114         Font *f;
115         int x;
116
117         f = getfont(WFont);
118         i->width = 0;
119         for(o=i->formfield->options; o!=nil; o=o->next)
120                 i->width = max(i->width, runestringwidth(f, o->display));
121         x = Margin + Border + Space;
122         i->width += 2*x;
123         i->height = f->height+2*x;
124 }
125
126 static
127 void
128 sizeformfield(Lay *lay, Iformfield *i)
129 {
130         int type;
131
132         type = i->formfield->ftype;
133
134         if(type==Ftext || type==Ftextarea || type==Fpassword)
135                 sizetextfield(lay, i);
136         else if(type==Fcheckbox || type==Fradio)
137                 sizecheck(lay, i);
138         else if(type==Fbutton || type==Freset || type==Fsubmit)
139                 sizebutton(lay, i);
140         else if(type == Fimage)
141                 sizefimage(lay, i);
142         else if(type == Fselect)
143                 sizeselect(lay, i);
144 }
145
146 static
147 void
148 sizetable(Lay *lay, Itable *i)
149 {
150         tablesize(i->table, lay->width);
151         i->width = i->table->totw;
152         i->height = i->table->toth;
153 }
154
155 static
156 void
157 sizefloat(Lay *lay, Ifloat *i)
158 {
159         sizeitem(lay, i->item);
160         i->width = i->item->width;
161         i->height = i->item->height;
162 }
163
164 static
165 void
166 sizespacer(Lay *lay, Ispacer *i)
167 {
168         if(i->spkind != ISPnull){
169                 if(i->spkind == ISPhspace)
170                         i->width = stringnwidth(lay->font, " ", 1);
171                 i->height = lay->font->height + 2*Space;
172         }
173 }
174
175 static
176 void
177 sizeitem(Lay *lay, Item *i)
178 {
179
180         switch(i->tag){
181         case Itexttag:
182                 sizetext(lay, (Itext *)i);
183                 break;
184         case Iruletag:
185                 sizerule(lay, (Irule *)i);
186                 break;
187         case Iimagetag:
188                 sizeimage(lay, (Iimage *)i);
189                 break;
190         case Iformfieldtag:
191                 sizeformfield(lay, (Iformfield *)i);
192                 break;
193         case Itabletag:
194                 sizetable(lay, (Itable *)i);
195                 break;
196         case Ifloattag:
197                 sizefloat(lay, (Ifloat *)i);
198                 break;
199         case Ispacertag:
200                 sizespacer(lay, (Ispacer *)i);
201                 break;
202         default:
203                 error("can't happen");
204         }
205 }
206
207 static
208 void
209 drawtext(Box *b, Page *p, Image *im)
210 {
211         Rectangle r, r1;
212         Image *c;
213         Point pt;
214         Font *f;
215         Itext *i;
216         int q0, q1;
217
218         r = rectsubpt(b->r, p->pos);
219         i = (Itext *)b->i;
220         f = getfont(i->fnt);
221         if(istextsel(p, b->r, &q0, &q1, i->s, f)){
222                 r1 = r;
223                 if(q0 > 0)
224                         r1.min.x += runestringnwidth(f, i->s, q0);
225                 if(q1 > 0)
226                         r1.max.x = r1.min.x + runestringnwidth(f, i->s+q0, q1-q0);
227                 draw(im, r1, textcols[HIGH], nil, ZP);
228         }
229         c = getcolor(i->fg);
230         runestringbg(im, r.min, c, ZP, f, i->s, im, addpt(r.min, im->r.min));
231
232         if(i->ul == ULnone)
233                 return;
234
235         if(i->ul == ULmid)
236                 r.min.y += f->height/2;
237         else
238                 r.min.y +=f->height-1;
239         pt = r.min;
240         pt.x +=  runestringwidth(f, i->s);
241         line(im, r.min, pt, Enddisc, Enddisc, 0, c, ZP);
242 }
243
244 static
245 void
246 drawrule(Box *b, Page *p, Image *im)
247 {
248         Rectangle r;
249         Irule *i;
250
251         i = ((Irule *)b->i);
252         r = rectsubpt(b->r, p->pos);
253         r.min.y += Space;
254         r.max.y -=Space;
255         draw(im, r, getcolor(i->color), nil, ZP);
256 }
257
258 static
259 void
260 drawimage(Box *b, Page *p, Image *im)
261 {
262         Rectangle r;
263         Cimage *ci;
264         Iimage *i;
265         Image *c;
266
267         if(b->i->tag==Iimagetag)
268                 i = (Iimage *)b->i;
269         else
270                 i = (Iimage *)((Iformfield *)b->i)->formfield->image;
271
272         ci = (Cimage *)i->aux;
273         if(ci==nil || ci->i==nil)
274                 return;
275
276         r = rectsubpt(b->r, p->pos);
277         r.min.x += i->border + i->hspace;
278         r.min.y += i->border + i->vspace;
279         r.max.x -= i->border + i->hspace;
280         r.max.y -= i->border + i->vspace;
281
282         draw(im, r,  ci->i, nil, ci->i->r.min);
283
284         if(i->border){
285                 if(i->anchorid >= 0)
286                         c = getcolor(p->doc->link);
287                 else
288                         c = display->black;
289
290                 border(im, r, i->border, c, ZP);
291         }
292 }
293
294 static
295 void
296 drawtextfield(Image *im, Rectangle r, Iformfield *i)
297 {
298         Formfield *ff;
299         Image *c[3];
300         Text *t;
301         Font *f;
302
303         r = insetrect(r, Space);
304         colarray(c, getcolor(Dark), getcolor(Light), display->white, 1);
305         rect3d(im, r, Border, c, ZP);
306         r = insetrect(r, Border+Margin);
307
308         if(i->aux == nil){
309                 ff = i->formfield;
310                 t = emalloc(sizeof(Text));
311                 if(ff->ftype == Ftextarea)
312                         t->what = Textarea;
313                 else
314                         t->what = Entry;
315                 if(ff->ftype == Fpassword)
316                         f = passfont;
317                 else
318                         f = getfont(WFont);
319                 textinit(t, im, r, f, textcols);
320                 if(ff->value!=nil){
321                         textinsert(t, 0, ff->value, runestrlen(ff->value));
322                         textsetselect(t, t->rs.nr, t->rs.nr);
323                 }
324                 if(t->what == Textarea)
325                         textscrdraw(t);
326                 i->aux = t;
327         }else
328                 textresize(i->aux, im, r);
329 }
330
331 void
332 drawcheck(Image *im, Rectangle r, Formfield *f)
333 {
334         Image *c[3];
335         Point pt;
336         int n;
337
338         if(f->flags & FFchecked)
339                 colarray(c, getcolor(Dark), getcolor(Light), getcolor(Red), TRUE);
340         else
341                 colarray(c, getcolor(Dark), getcolor(Light), getcolor(Back), FALSE);
342
343         if(f->ftype == Fradio){
344                 n = Boxsize/2-1;
345                 pt = addpt(r.min, Pt(n,n));
346                 ellipse3d(im, pt, n, Border, c, ZP);
347         }else
348                 rect3d(im, r, Border, c, ZP);
349 }
350
351 void
352 drawbutton(Image *im, Rectangle r, Formfield *f, int checked)
353 {
354         Image *c[3];
355
356         r = insetrect(r, Space);
357         colarray(c, getcolor(Dark), getcolor(Light), getcolor(Back), checked);
358         rect3d(im, r, Border, c, ZP);
359         r.min.x += Border + Margin;
360         r.min.y += Border + Margin;
361         runestringbg(im, r.min, display->black, ZP,  getfont(WFont), f->value, c[2], ZP);
362 }
363
364 void
365 drawselect(Image *im, Rectangle r,      Iformfield *i)
366 {
367         Formfield *f;
368         Image *c[3];
369
370         f = i->formfield;
371         if(f->options == nil)
372                 return;
373         r = insetrect(r, Space);
374         colarray(c, getcolor(Dark), getcolor(Light), display->white, 1);
375         rect3d(im, r, Border, c, ZP);
376         r = insetrect(r, Border+Margin);
377         draw(im, r, textcols[HIGH], nil, ZP);
378         if(i->aux==nil){
379                 i->aux = f->options->display;
380                 i->formfield->value = erunestrdup(f->options->value);
381         }
382         runestring(im, r.min, display->black, ZP,  getfont(WFont), i->aux);
383 }
384
385 /* Formfields are a special case */
386 static
387 void
388 drawformfield(Box *b, Page *p, Image *im)
389 {
390         Formfield *f;
391         int type;
392
393         f = ((Iformfield *)b->i)->formfield;
394         type =f->ftype;
395         if(istextfield(b->i))
396                 drawtextfield(im, rectsubpt(b->r, p->pos), (Iformfield *)b->i);
397         else if(type==Fcheckbox || type==Fradio)
398                 drawcheck(im, rectsubpt(b->r, p->pos), f);
399         else if(type==Fbutton || type==Freset || type==Fsubmit)
400                 drawbutton(im, rectsubpt(b->r, p->pos), f, FALSE);
401         else if(type == Fimage)
402                 drawimage(b, p, im);
403         else if(type == Fselect)
404                 drawselect(im, rectsubpt(b->r, p->pos), (Iformfield *)b->i);
405 }
406
407 static
408 void
409 drawnull(Box *, Page *, Image *)
410 {
411 }
412
413 static
414 Page *
415 whichtarget1(Page *p, Rune *r)
416 {
417         Kidinfo *k;
418         Page *c, *ret;
419
420         k = p->kidinfo;
421         if(k && k->name && runestrcmp(k->name, r)==0)
422                 return p;
423         for(c=p->child; c; c=c->next){
424                 ret = whichtarget1(c, r);
425                 if(ret)
426                         return ret;
427         }
428         return nil;
429 }
430
431 static
432 Page *
433 whichtarget(Page *p, int t)
434 {
435         Page *r;
436
437         switch(t){
438         case FTblank:
439         case FTtop:
440                 r = &p->w->page;
441                 break;
442         case FTself:
443                 r = p;
444                 break;
445         case FTparent:
446                 r = p->parent;
447                 break;
448         default:
449                 if(targetname(t) == L"?")
450                         error("targetname");
451                 r = whichtarget1(&p->w->page, targetname(t));
452         }
453
454         return r ? r: &p->w->page;
455 }
456
457 static
458 void
459 mouselink(Box *b, Page *p, int but)
460 {
461         Runestr rs;
462         Anchor *a;
463
464         /* eat mouse */
465         while(mousectl->buttons)
466                 readmouse(mousectl);
467
468         if(b->i->anchorid < 0)
469                 return;
470
471         /* binary search would be better */
472         for(a=p->doc->anchors; a!=nil; a=a->next)
473                 if(a->index == b->i->anchorid)
474                         break;
475
476         if(a==nil || a->href==nil)
477                 return;
478
479         p = whichtarget(p, a->target);
480         rs.r = urlcombine(getbase(p), a->href);
481         if(rs.r == nil)
482                 return;
483         rs.nr = runestrlen(rs.r);
484
485         if(but == 1)
486                 pageget(p, &rs, nil, HGet, p==&p->w->page);
487         else if(but == 2)
488                 textset(&p->w->status, rs.r, rs.nr);
489         else if(but == 3)
490                 plumbrunestr(&rs, nil);
491         closerunestr(&rs);
492 }
493
494 static
495 void
496 submit(Page *p, Formfield *formfield, int subfl)
497 {
498         Formfield *f;
499         Form *form;
500         Runestr src, post;
501         Rune *x, *sep, *y, *z;
502
503         form = formfield->form;
504         x = erunestrdup(L"");
505         sep = L"";
506         for(f=form->fields; f!=nil; f=f->next){
507                 if(f->ftype == Freset)
508                         continue;
509                 if((f->ftype==Fradio || f->ftype==Fcheckbox) && !(f->flags&FFchecked))
510                         continue;
511                 if(f->ftype==Fsubmit && (f!=formfield || !subfl))
512                         continue;
513                 if(f->value==nil || f->name==nil || runestrcmp(f->name, L"_no_name_submit_")==0)
514                         continue;
515
516                 z = ucvt(f->value);
517                 y = runesmprint("%S%S%S=%S", x, sep, f->name, z);
518                 free(z);
519                 sep = L"&";
520                 free(x);
521                 x = y;
522         }
523         p = whichtarget(p, form->target);
524         y = urlcombine(getbase(p), form->action);
525
526         memset(&src, 0, sizeof(Runestr));
527         memset(&post, 0, sizeof(Runestr));
528         if(form->method == HGet){
529                 if(y[runestrlen(y)-1] == L'?')
530                         sep = L"";
531                 else
532                         sep = L"?";
533                 src.r = runesmprint("%S%S%S",y, sep,  x);
534                 free(x);
535                 free(y);
536         }else{
537                 src.r = y;
538                 post.r = x;
539                 post.nr = runestrlen(x);
540                 if(post.nr == 0){
541                         free(post.r);
542                         post.r = nil;
543                 }
544         }
545         src.nr = runestrlen(src.r);
546         pageget(p, &src, &post, form->method, p==&p->w->page);
547         closerunestr(&src);
548         closerunestr(&post);
549 }
550
551 static
552 void
553 setradios(Formfield *formfield)
554 {
555         Formfield *f;
556
557         for(f=formfield->form->fields; f!=nil; f=f->next)
558                 if(f->ftype==Fradio && f!=formfield && runestrcmp(f->name, formfield->name)==0)
559                         f->flags &=~FFchecked;
560 }
561
562 static
563 void
564 selectmouse(Box *b, Page *p, int but)
565 {
566         Formfield *f;
567         Option *o;
568         Menu m;
569         char **item;
570         int i, n;
571
572         f = ((Iformfield *)b->i)->formfield;
573         n = 0;
574         item = nil;
575         for(o=f->options; o!=nil; o=o->next){
576                 item = erealloc(item, ++n*sizeof(char *));
577                 if(o->display)
578                         item[n-1] = smprint("%S", o->display);
579                 else
580                         item[n-1] = estrdup("--");
581         }
582         if(item == nil)
583                 return;
584
585         item[n] = 0;
586         m.item = item;
587         i = menuhit(but, mousectl, &m, nil);
588         if(i >= 0){
589                 for(o=f->options; o!=nil; o=o->next, i--){
590                         if(i == 0)
591                                 break;
592                 }
593                 ((Iformfield *)b->i)->aux = o->display;
594                 drawselect(p->b, rectaddpt(rectsubpt(b->r, p->pos), p->r.min), (Iformfield *)b->i);
595                 if(f->value != nil)
596                         free(f->value);
597                 f->value = erunestrdup(o->value);
598         }
599         for(i=0; i< n; i++)
600                 free(item[i]);
601 //      free(item);
602 }
603
604 static
605 void
606 mouseform(Box *b, Page *p, int but)
607 {
608         Rectangle r, cr;
609         Formfield *f;
610         Text *t;
611
612         f = ((Iformfield *)b->i)->formfield;
613         r = rectaddpt(rectsubpt(b->r, p->pos), p->r.min);
614         if(istextfield(b->i)){
615                 cr = p->b->clipr;
616                 replclipr(p->b, 0, p->r);
617                 t = ((Iformfield *)b->i)->aux;
618                 if(p->b != t->b)
619                         drawtextfield(p->b, r, (Iformfield *)b->i);
620                 textmouse(t, mouse->xy, but);
621                 if(f->value)
622                         free(f->value);
623                 f->value = runesmprint("%.*S", t->rs.nr, t->rs.r);
624                 replclipr(p->b, 0, cr);
625                 return;
626         }
627
628         if(but != 1)
629                 return;
630
631         if(f->ftype==Fselect){
632                 selectmouse(b, p, but);
633                 return;
634         }
635         if(f->ftype==Fsubmit || f->ftype==Fimage){
636                 if(f->ftype == Fsubmit)
637                         drawbutton(p->b, r, f, TRUE);
638                 while(mouse->buttons == but)
639                         readmouse(mousectl);
640                 if(f->ftype == Fsubmit)
641                         drawbutton(p->b, r, f, FALSE);
642                 if(mouse->buttons==0 && ptinrect(mouse->xy, r))
643                         submit(p, f, TRUE);
644                 return;
645         }
646         if(f->ftype==Fradio || f->ftype==Fcheckbox){
647                 if(f->flags&FFchecked){
648                         if(f->ftype==Fcheckbox)
649                                 f->flags &=~FFchecked;
650                 }else{
651                         f->flags |= FFchecked;
652                 }
653                 if(f->ftype == Fradio)
654                         setradios(f);
655                 pageredraw(p);
656         }
657 }
658
659 static
660 void
661 keyform(Box *b, Page *p, Rune r)
662 {
663         Rectangle cr;
664         Formfield *f;
665         Text *t;
666
667         f = ((Iformfield *)b->i)->formfield;
668         if(r==L'\n' && f->ftype==Ftext){
669                 submit(p, f, FALSE);
670                 return;
671         }
672         t = ((Iformfield *)b->i)->aux;
673         cr = p->b->clipr;
674         replclipr(p->b, 0, p->r);
675         if(t->b != p->b)
676                 drawtextfield(p->b, rectaddpt(rectsubpt(b->r, p->pos), p->r.min), (Iformfield *)b->i);
677         texttype(t, r);
678         if(f->value)
679                 free(f->value);
680         f->value = runesmprint("%.*S", t->rs.nr, t->rs.r);
681         replclipr(p->b, 0, cr);
682 }
683
684 void
685 boxinit(Box *b)
686 {
687         if(b->i->anchorid)
688                 b->mouse = mouselink;
689         /* override mouselink for forms */
690         if(b->i->tag == Iformfieldtag){
691                 b->mouse = mouseform;
692                 if(istextfield(b->i))
693                         b->key = keyform;
694         }
695         switch(b->i->tag){
696         case Itexttag:
697                 b->draw = drawtext;
698                 break;
699         case Iruletag:
700                 b->draw = drawrule;
701                 break;
702         case Iimagetag:
703                 b->draw = drawimage;
704                 break;
705         case Iformfieldtag:
706                 b->draw = drawformfield;
707                 break;
708         case Itabletag:
709                 b->draw = drawtable;
710                 break;
711         case Ifloattag:
712                 b->draw = drawnull;
713                 break;
714         case Ispacertag:
715                 b->draw = drawnull;
716         }
717 }
718
719 Box *
720 boxalloc(Line *l, Item *i, Rectangle r)
721 {
722         Box *b;
723
724         b = emalloc(sizeof(Box));
725         b->i = i;
726         b->r = r;
727         if(l->boxes == nil)
728                 l->boxes = b;
729         else{
730                 b->prev = l->lastbox;
731                 l->lastbox->next = b;
732         }
733         l->lastbox = b;
734         setmalloctag(b, getcallerpc(&l));
735         return b;
736 }
737
738 Box *
739 pttobox(Line *l, Point xy)
740 {
741         Box *b;
742
743         for(b=l->boxes; b!=nil; b=b->next)
744                 if(ptinrect(xy, b->r))
745                         return b;
746
747         return nil;
748 }
749
750 static
751 Line *
752 tbtoline(Itable *i, Point xy)
753 {
754         Tablecell *c;
755
756         for(c=i->table->cells; c!=nil; c=c->next)
757                 if(ptinrect(xy, c->lay->r))
758                         return linewhich(c->lay, xy);
759
760         return nil;
761 }
762
763 Line *
764 linewhich(Lay *lay, Point xy)
765 {
766         Line *l, *t;
767         Box *b;
768
769         t = nil;
770         for(l=lay->lines; l!=nil; l=l->next)
771                 if(ptinrect(xy, l->r))
772                         break;
773
774         if(l!=nil && l->hastable){
775                 b = pttobox(l, xy);
776                 if(b!=nil && b->i->tag==Itabletag)
777                         t = tbtoline((Itable *)b->i, xy);
778         }
779         return t? t: l;
780 }
781
782 Box *
783 boxwhich(Lay *lay, Point xy)
784 {
785         Line *l;
786
787         l = linewhich(lay, xy);
788         if(l)
789                 return pttobox(l, xy);
790
791         return nil;
792 }
793
794 static void justline1(Line *, int);
795
796 static
797 void
798 justlay(Lay *lay, int x)
799 {
800         Line *l;
801
802         lay->r.min.x += x;
803         lay->r.max.x += x;
804
805         for(l=lay->lines; l!=nil; l=l->next)
806                 justline1(l, x);
807 }
808
809 static
810 void
811 justtable(Itable *i, int x)
812 {
813         Tablecell *c;
814
815         for(c=i->table->cells; c!=nil; c=c->next)
816                 justlay(c->lay, x);
817 }
818
819 static
820 void
821 justline1(Line *l, int x)
822 {
823         Box *b;
824
825         l->r.min.x += x;
826         l->r.max.x += x;
827         for(b=l->boxes; b!=nil; b=b->next){
828                 if(b->i->tag == Itabletag)
829                         justtable((Itable *)b->i, x);
830                 b->r.min.x += x;
831                 b->r.max.x += x;
832         }
833 }
834
835 static
836 void
837 justline(Lay *lay, Line *l)
838 {
839
840         int w, x;
841
842         w = Dx(l->r);
843         if(w>0 && w<lay->width){
844                 x = 0;
845                 if(l->state & IFrjust)
846                         x = lay->width - w;
847                 else if(l->state & IFcjust)
848                         x = lay->width/2 - w/2;
849                 if(x > 0)
850                         justline1(l, x);
851         }
852 }
853
854 static
855 void
856 newline(Lay *lay, int state)
857 {
858         Line *l, *last;
859         int indent, nl;
860
861         last = lay->lastline;
862         if(lay->laying == TRUE)
863                 justline(lay, last);
864
865         lay->r.max.x = max(lay->r.max.x, last->r.max.x);
866         lay->r.max.y = last->r.max.y;
867
868         indent = ((state&IFindentmask)>>IFindentshift) * Tabspace;
869         nl = (state & IFbrksp) ? 1 : 0;
870
871         l = emalloc(sizeof(Line));
872         l->state = state;
873         l->hastext = FALSE;
874         l->hastable = FALSE;
875         l->r.min.x = lay->r.min.x + indent;
876         l->r.min.y = last->r.max.y + font->height*nl;
877         l->r.max = l->r.min;
878         l->prev = last;
879         last->next = l;
880         lay->lastline = l;
881 }
882
883
884 static
885 void
886 layitem(Lay *lay, Item *i)
887 {
888         Rectangle r;
889         Line *l;
890         Box *b;
891
892         if(i->state&IFbrk || i->state&IFbrksp)
893                 newline(lay, i->state);
894         else    if(lay->lastline->r.max.x+i->width>lay->xwall && forceitem(i)==FALSE)
895                 newline(lay, i->state);
896
897         l = lay->lastline;
898         r = Rect(l->r.max.x, l->r.min.y, l->r.max.x+i->width, l->r.min.y+i->height);
899         l->r.max.x = r.max.x;
900         if(l->r.max.y < r.max.y)
901                 l->r.max.y = r.max.y;
902
903         if(i->tag == Ifloattag)
904                 i = ((Ifloat *)i)->item;
905         if(i->tag == Itexttag)
906                 l->hastext = TRUE;
907         else if(i->tag == Itabletag && lay->laying==TRUE){
908                 laytable((Itable *)i, r);
909                 l->hastable = TRUE;
910         }
911         b = boxalloc(l, i, r);
912         if(lay->laying)
913                 boxinit(b);
914 }
915
916 static
917 void
918 linefix(Lay *lay)
919 {
920         Line *l;
921
922         for(l=lay->lines; l!=nil; l=l->next){
923                 l->r.min.x = lay->r.min.x;
924                 l->r.max.x = lay->r.max.x;
925         }
926 }
927
928 Lay *
929 layitems(Item *items, Rectangle r, int laying)
930 {
931         Lay *lay;
932         Line *l;
933         Item *i;
934
935         lay = emalloc(sizeof(Lay));
936         lay->r.min = r.min;
937         lay->r.max = r.min;
938         lay->xwall = r.max.x;
939         lay->width = Dx(r);
940         lay->laying = laying;
941         l = emalloc(sizeof(Line));
942         l->r.min = lay->r.min;
943         l->r.max = lay->r.min;
944         l->state = IFbrk;
945         l->boxes = nil;
946         lay->lines = l;
947         lay->lastline = l;
948         lay->font = font;
949
950         for(i=items; i; i=i->next){
951                 sizeitem(lay, i);
952                 layitem(lay, i);
953         }
954         newline(lay, IFbrk);
955         if(laying)
956                 linefix(lay);
957
958         return lay;
959 }
960
961 void
962 laypage(Page *p)
963 {
964         layfree(p->lay);
965         p->lay = nil;
966         settables(p);
967         p->lay = layitems(p->items, Rect(0,0,Dx(p->r),Dy(p->r)), TRUE);
968         p->lay->r.max.y = max(p->lay->r.max.y, Dy(p->r));
969 }
970
971 static
972 void
973 drawline(Page *p, Image *im, Line *l)
974 {
975         Box *b;
976
977         for(b=l->boxes; b!=nil; b=b->next)
978                 b->draw(b, p, im);
979 }
980
981 void
982 laydraw(Page *p, Image *im, Lay *lay)
983 {
984         Rectangle r;
985         Line *l;
986
987         r = rectaddpt(p->lay->r, p->pos);
988         for(l=lay->lines; l!=nil; l=l->next){
989                 if(rectXrect(r, l->r))
990                         drawline(p, im, l);
991         }
992 }
993
994 static
995 void
996 laytablefree(Table *t)
997 {
998         Tablecell *c;
999
1000         for(c=t->cells; c!=nil; c=c->next){
1001                 layfree(c->lay);
1002                 c->lay = nil;
1003         }
1004 }
1005
1006 void
1007 layfree(Lay *lay)
1008 {
1009         Line *l, *nextline;
1010         Box *b, *nextbox;
1011         void **aux;
1012
1013         if(lay == nil)
1014                 return;
1015
1016         for(l=lay->lines; l!=nil; l=nextline){
1017                 for(b=l->boxes; b!=nil; b=nextbox){
1018                         nextbox = b->next;
1019                         if(lay->laying==TRUE){
1020                                 if(b->i->tag==Iformfieldtag && istextfield(b->i)){
1021                                         aux = &((Iformfield *)b->i)->aux;
1022                                         if(*aux){
1023                                                 textclose(*aux);
1024                                                 free(*aux);
1025                                         }
1026                                         *aux = nil;
1027                                 }else if(b->i->tag == Itabletag)
1028                                         laytablefree(((Itable *)b->i)->table);
1029                         }
1030                         free(b);
1031                 }
1032                 nextline = l->next;
1033                 free(l);
1034         }
1035         free(lay);
1036 }
1037
1038 void
1039 laysnarf(Page *p, Lay *lay, Runestr *rs)
1040 {
1041         Tablecell *c;
1042         Itext *i;
1043         Font *f;
1044         Line *l;
1045         Box *b;
1046         int q0, q1, n;
1047
1048         for(l=lay->lines; l!=nil; l=l->next) for(b=l->boxes; b!=nil; b=b->next){
1049                 if(p->selecting && hasbrk(b->i->state)){
1050                         rs->r = runerealloc(rs->r, rs->nr+2);
1051                         rs->r[rs->nr++] = L'\n';
1052                         rs->r[rs->nr] = L'\0';
1053                 }
1054                 if(b->i->tag==Itexttag){
1055                         i = (Itext *)b->i;
1056                         f = getfont(i->fnt);
1057                         if(istextsel(p, b->r, &q0, &q1, i->s, f)){
1058                                 if(q1 == 0)
1059                                         q1 = runestrlen(i->s);
1060                                 n = q1-q0;
1061                                 if(n == 0)
1062                                         n = runestrlen(i->s);
1063                                 rs->r = runerealloc(rs->r, rs->nr+n+2);
1064                                 runemove(rs->r+rs->nr, i->s+q0, n);
1065                                 rs->nr += n;
1066                                 rs->r[rs->nr++] = L' ';
1067                                 rs->r[rs->nr] = L'\0';
1068                         }
1069                 }else if(b->i->tag == Itabletag)
1070                         for(c=((Itable *)b->i)->table->cells; c!=nil; c=c->next)
1071                                 if(c->lay)
1072                                         laysnarf(p, c->lay, rs);
1073         }
1074 }