xref: /plan9/sys/src/cmd/abaco/html.c (revision 7ab27030036b6c877a6f81728daeda263d1ca3cf)
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
sizetext(Lay * lay,Itext * i)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
sizerule(Lay * lay,Irule * i)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
sizeimage(Lay *,Iimage * i)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
sizetextfield(Lay *,Iformfield * i)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
sizecheck(Lay *,Iformfield * i)78 sizecheck(Lay *, Iformfield *i)
79 {
80 	i->width = Boxsize + Space;
81 	i->height = Boxsize;
82 }
83 
84 static
85 void
sizebutton(Lay *,Iformfield * i)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
sizefimage(Lay * lay,Iformfield * i)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
sizeselect(Lay *,Iformfield * i)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
sizeformfield(Lay * lay,Iformfield * i)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
sizetable(Lay * lay,Itable * i)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
sizefloat(Lay * lay,Ifloat * i)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
sizespacer(Lay * lay,Ispacer * i)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
sizeitem(Lay * lay,Item * i)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
drawtext(Box * b,Page * p,Image * im)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
drawrule(Box * b,Page * p,Image * im)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
drawimage(Box * b,Page * p,Image * im)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
drawtextfield(Image * im,Rectangle r,Iformfield * i)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
drawcheck(Image * im,Rectangle r,Formfield * f)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
drawbutton(Image * im,Rectangle r,Formfield * f,int checked)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
drawselect(Image * im,Rectangle r,Iformfield * i)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
drawformfield(Box * b,Page * p,Image * im)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
drawnull(Box *,Page *,Image *)409 drawnull(Box *, Page *, Image *)
410 {
411 }
412 
413 static
414 Page *
whichtarget1(Page * p,Rune * r)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 *
whichtarget(Page * p,int t)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
mouselink(Box * b,Page * p,int but)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
submit(Page * p,Formfield * formfield,int subfl)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
setradios(Formfield * formfield)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
selectmouse(Box * b,Page * p,int but)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
mouseform(Box * b,Page * p,int but)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
keyform(Box * b,Page * p,Rune r)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
boxinit(Box * b)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 *
boxalloc(Line * l,Item * i,Rectangle r)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 
735 	return b;
736 }
737 
738 Box *
pttobox(Line * l,Point xy)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 *
tbtoline(Itable * i,Point xy)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 *
linewhich(Lay * lay,Point xy)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 *
boxwhich(Lay * lay,Point xy)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
justlay(Lay * lay,int x)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
justtable(Itable * i,int x)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
justline1(Line * l,int x)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
justline(Lay * lay,Line * l)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
newline(Lay * lay,int state)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
layitem(Lay * lay,Item * i)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
linefix(Lay * lay)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 *
layitems(Item * items,Rectangle r,int laying)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
laypage(Page * p)962 laypage(Page *p)
963 {
964 	settables(p);
965 	layfree(p->lay);
966 	p->lay = layitems(p->items, Rect(0,0,Dx(p->r),Dy(p->r)), TRUE);
967 	p->lay->r.max.y = max(p->lay->r.max.y, Dy(p->r));
968 }
969 
970 static
971 void
drawline(Page * p,Image * im,Line * l)972 drawline(Page *p, Image *im, Line *l)
973 {
974 	Box *b;
975 
976 	for(b=l->boxes; b!=nil; b=b->next)
977 		b->draw(b, p, im);
978 }
979 
980 void
laydraw(Page * p,Image * im,Lay * lay)981 laydraw(Page *p, Image *im, Lay *lay)
982 {
983 	Rectangle r;
984 	Line *l;
985 
986 	r = rectaddpt(p->lay->r, p->pos);
987 	for(l=lay->lines; l!=nil; l=l->next){
988 		if(rectXrect(r, l->r))
989 			drawline(p, im, l);
990 	}
991 }
992 
993 static
994 void
laytablefree(Table * t)995 laytablefree(Table *t)
996 {
997 	Tablecell *c;
998 
999 	for(c=t->cells; c!=nil; c=c->next){
1000 		layfree(c->lay);
1001 		c->lay = nil;
1002 	}
1003 }
1004 
1005 void
layfree(Lay * lay)1006 layfree(Lay *lay)
1007 {
1008 	Line *l, *nextline;
1009 	Box *b, *nextbox;
1010 	void **aux;
1011 
1012 	if(lay == nil)
1013 		return;
1014 
1015 	for(l=lay->lines; l!=nil; l=nextline){
1016 		for(b=l->boxes; b!=nil; b=nextbox){
1017 			nextbox = b->next;
1018 			if(b->i->tag==Iformfieldtag && istextfield(b->i)){
1019 				aux = &((Iformfield *)b->i)->aux;
1020 				if(*aux){
1021 					textclose(*aux);
1022 					free(*aux);
1023 				}
1024 				*aux = nil;
1025 			}else if(b->i->tag == Itabletag)
1026 				laytablefree(((Itable *)b->i)->table);
1027 
1028 			free(b);
1029 		}
1030 		nextline = l->next;
1031 		free(l);
1032 	}
1033 	free(lay);
1034 }
1035 
1036 void
laysnarf(Page * p,Lay * lay,Runestr * rs)1037 laysnarf(Page *p, Lay *lay, Runestr *rs)
1038 {
1039 	Tablecell *c;
1040 	Itext *i;
1041 	Font *f;
1042 	Line *l;
1043 	Box *b;
1044 	int q0, q1, n;
1045 
1046 	for(l=lay->lines; l!=nil; l=l->next) for(b=l->boxes; b!=nil; b=b->next){
1047 		if(p->selecting && hasbrk(b->i->state)){
1048 			rs->r = runerealloc(rs->r, rs->nr+2);
1049 			rs->r[rs->nr++] = L'\n';
1050 			rs->r[rs->nr] = L'\0';
1051 		}
1052 		if(b->i->tag==Itexttag){
1053 			i = (Itext *)b->i;
1054 			f = getfont(i->fnt);
1055 			if(istextsel(p, b->r, &q0, &q1, i->s, f)){
1056 				if(q1 == 0)
1057 					q1 = runestrlen(i->s);
1058 				n = q1-q0;
1059 				if(n == 0)
1060 					n = runestrlen(i->s);
1061 				rs->r = runerealloc(rs->r, rs->nr+n+2);
1062 				runemove(rs->r+rs->nr, i->s+q0, n);
1063 				rs->nr += n;
1064 				rs->r[rs->nr++] = L' ';
1065 				rs->r[rs->nr] = L'\0';
1066 			}
1067 		}else if(b->i->tag == Itabletag)
1068 			for(c=((Itable *)b->i)->table->cells; c!=nil; c=c->next)
1069 				if(c->lay)
1070 					laysnarf(p, c->lay, rs);
1071 	}
1072 }
1073