Plan 9 from Bell Labs’s /usr/web/sources/contrib/mospak/abaco/abaco-html2-wiring.patch

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


abaco: use more html(2) fields

Use html(2) fields parsehtml already fills but abaco ignores:
refresh, frame borders, client-side image maps, table captions,
hr attributes, image placeholders, select defaults, maxlength,
field name encoding, fragment anchors, hanging indent, row
backgrounds, and cell valign.

--- sys/src/cmd/abaco/html.c
+++ sys/src/cmd/abaco/html.c
@@ -40,15 +40,22 @@
 
 	ci = (Cimage *)i->aux;
 
-	if(ci==nil)
-		return;
+	if(ci==nil || ci->i==nil){
+		/* use html width/height as placeholders */
+		if(i->imwidth > 0)
+			i->width = i->imwidth + 2*(i->border + i->hspace);
+		if(i->imheight > 0)
+			i->height = i->imheight + 2*(i->border + i->vspace);
+		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;
+	i->width = Dx(ci->i->r) + 2*(i->border + i->hspace);
+	i->height = Dy(ci->i->r) + 2*(i->border + i->vspace);
 }
 
 static
@@ -247,12 +254,31 @@
 {
 	Rectangle r;
 	Irule *i;
+	Image *c[3];
+	int w, x;
 
 	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);
+	r.max.y -= Space;
+	if(dimenkind(i->wspec) != Dnone){
+		w = dimwidth(i->wspec, Dx(r));
+		if(w < Dx(r)){
+			x = 0;
+			if(i->align == ALcenter)
+				x = (Dx(r) - w)/2;
+			else if(i->align == ALright)
+				x = Dx(r) - w;
+			r.min.x += x;
+			r.max.x = r.min.x + w;
+		}
+	}
+	if(i->noshade)
+		draw(im, r, getcolor(i->color), nil, ZP);
+	else{
+		colarray(c, getcolor(Dark), getcolor(Light), getcolor(i->color), 1);
+		rect3d(im, r, 1, c, ZP);
+	}
 }
 
 static
@@ -366,6 +392,7 @@
 {
 	Formfield *f;
 	Image *c[3];
+	Option *o;
 
 	f = i->formfield;
 	if(f->options == nil)
@@ -376,8 +403,13 @@
 	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);
+		for(o=f->options; o!=nil; o=o->next)
+			if(o->selected)
+				break;
+		if(o == nil)
+			o = f->options;
+		i->aux = o->display;
+		i->formfield->value = erunestrdup(o->value);
 	}
 	runestring(im, r.min, display->black, ZP,  getfont(WFont), i->aux);
 }
@@ -454,6 +486,189 @@
 	return r ? r: &p->w->page;
 }
 
+static void mouselink(Box *, Page *, int);
+
+static
+void
+followlink(Page *p, Rune *href, int target, int but)
+{
+	Runestr rs;
+
+	if(href == nil)
+		return;
+	p = whichtarget(p, target);
+	rs.r = urlcombine(getbase(p), 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
+int
+mapcoord(Dimen d, int n)
+{
+	int v;
+
+	v = dimenspec(d);
+	if(dimenkind(d) == Dpercent)
+		v = v*n/100;
+	return v;
+}
+
+static
+int
+inpolygon(Point p, Point *pt, int n)
+{
+	int i, j, in;
+
+	if(n < 3)
+		return 0;
+	in = 0;
+	for(i=0, j=n-1; i<n; j=i++)
+		if(((pt[i].y > p.y) != (pt[j].y > p.y)) &&
+		   (p.x < (pt[j].x-pt[i].x)*(p.y-pt[i].y)/(pt[j].y-pt[i].y) + pt[i].x))
+			in = !in;
+	return in;
+}
+
+static
+int
+maphit(Area *a, Point p, int w, int h)
+{
+	Point *pt;
+	int x0, y0, x1, y1, dx, dy, r, i, n;
+
+	switch(a->shape){
+	case SHrect:
+		if(a->ncoords < 4)
+			return 0;
+		x0 = mapcoord(a->coords[0], w);
+		y0 = mapcoord(a->coords[1], h);
+		x1 = mapcoord(a->coords[2], w);
+		y1 = mapcoord(a->coords[3], h);
+		if(x0 > x1){ i = x0; x0 = x1; x1 = i; }
+		if(y0 > y1){ i = y0; y0 = y1; y1 = i; }
+		return ptinrect(p, Rect(x0, y0, x1, y1));
+	case SHcircle:
+		if(a->ncoords < 3)
+			return 0;
+		dx = p.x - mapcoord(a->coords[0], w);
+		dy = p.y - mapcoord(a->coords[1], h);
+		r = mapcoord(a->coords[2], min(w, h));
+		return dx*dx + dy*dy <= r*r;
+	case SHpoly:
+		n = a->ncoords/2;
+		if(n < 3)
+			return 0;
+		pt = emalloc(n*sizeof(Point));
+		for(i=0; i<n; i++)
+			pt[i] = Pt(mapcoord(a->coords[2*i], w),
+				mapcoord(a->coords[2*i+1], h));
+		i = inpolygon(p, pt, n);
+		free(pt);
+		return i;
+	}
+	return 0;
+}
+
+static
+void
+mousemap(Box *b, Page *p, int but)
+{
+	Area *a;
+	Cimage *ci;
+	Iimage *i;
+	Point pt;
+	Rectangle r;
+
+	while(mousectl->buttons)
+		readmouse(mousectl);
+	i = (Iimage *)b->i;
+	ci = (Cimage *)i->aux;
+	if(i->map==nil || ci==nil || ci->i==nil)
+		return;
+	pt = getpt(p, mouse->xy);
+	r = b->r;
+	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;
+	if(!ptinrect(pt, r)){
+		if(i->anchorid > 0)
+			mouselink(b, p, but);
+		return;
+	}
+	pt = subpt(pt, r.min);
+	for(a=i->map->areas; a!=nil; a=a->next)
+		if(maphit(a, pt, Dx(r), Dy(r))){
+			followlink(p, a->href, a->target, but);
+			return;
+		}
+	if(i->anchorid > 0)
+		mouselink(b, p, but);
+}
+
+static
+int
+finditem(Lay *lay, Item *target)
+{
+	Tablecell *c;
+	Line *l;
+	Box *b;
+	int y;
+
+	if(lay == nil)
+		return -1;
+	for(l=lay->lines; l!=nil; l=l->next)
+		for(b=l->boxes; b!=nil; b=b->next){
+			if(b->i == target)
+				return b->r.min.y;
+			if(b->i->tag == Itabletag)
+				for(c=((Itable *)b->i)->table->cells; c!=nil; c=c->next){
+					y = finditem(c->lay, target);
+					if(y >= 0)
+						return y;
+				}
+		}
+	return -1;
+}
+
+static
+int
+pagescrolltoid(Page *p, Rune *id)
+{
+	DestAnchor *d;
+	int y;
+
+	if(p->doc==nil || p->lay==nil || id==nil)
+		return 0;
+
+	for(d=p->doc->dests; d!=nil; d=d->next)
+		if(d->name && runestrcmp(d->name, id)==0)
+			break;
+	if(d==nil || d->item==nil)
+		return 0;
+
+	y = finditem(p->lay, d->item);
+	if(y < 0)
+		return 0;
+
+	p->pos.y = y;
+	if(p->pos.y > Dy(p->lay->r)-Dy(p->r))
+		p->pos.y = Dy(p->lay->r)-Dy(p->r);
+	if(p->pos.y < 0)
+		p->pos.y = 0;
+	pageredraw(p);
+	return 1;
+}
+
 static
 void
 mouselink(Box *b, Page *p, int but)
@@ -476,6 +691,12 @@
 	if(a==nil || a->href==nil)
 		return;
 
+	/* fragment-only link: scroll without refetch */
+	if(but==1 && a->href[0]==L'#'){
+		pagescrolltoid(p, a->href+1);
+		return;
+	}
+
 	p = whichtarget(p, a->target);
 	rs.r = urlcombine(getbase(p), a->href);
 	if(rs.r == nil)
@@ -484,9 +705,15 @@
 
 	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)
+	else if(but == 2){
+		if(b->i->genattr && b->i->genattr->title){
+			Rune *s;
+			s = runesmprint("%S — %S", rs.r, b->i->genattr->title);
+			textset(&p->w->status, s, runestrlen(s));
+			free(s);
+		}else
+			textset(&p->w->status, rs.r, rs.nr);
+	}else if(but == 3)
 		plumbrunestr(&rs, nil);
 	closerunestr(&rs);
 }
@@ -498,7 +725,7 @@
 	Formfield *f;
 	Form *form;
 	Runestr src, post;
-	Rune *x, *sep, *y, *z;
+	Rune *x, *sep, *y, *z, *w;
 
 	form = formfield->form;
 	x = erunestrdup(L"");
@@ -514,7 +741,9 @@
 			continue;
 
 		z = ucvt(f->value);
-		y = runesmprint("%S%S%S=%S", x, sep, f->name, z);
+		w = ucvt(f->name);
+		y = runesmprint("%S%S%S=%S", x, sep, w, z);
+		free(w);
 		free(z);
 		sep = L"&";
 		free(x);
@@ -670,6 +899,8 @@
 		return;
 	}
 	t = ((Iformfield *)b->i)->aux;
+	if(f->maxlength > 0 && t->rs.nr >= f->maxlength && r >= 0x20)
+		return;
 	cr = p->b->clipr;
 	replclipr(p->b, 0, p->r);
 	if(t->b != p->b)
@@ -686,6 +917,8 @@
 {
 	if(b->i->anchorid)
 		b->mouse = mouselink;
+	if(b->i->tag == Iimagetag && ((Iimage *)b->i)->map)
+		b->mouse = mousemap;
 	/* override mouselink for forms */
 	if(b->i->tag == Iformfieldtag){
 		b->mouse = mouseform;
@@ -856,7 +1089,7 @@
 newline(Lay *lay, int state)
 {
 	Line *l, *last;
-	int indent, nl;
+	int indent, hang, nl;
 
 	last = lay->lastline;
 	if(lay->laying == TRUE)
@@ -866,13 +1099,16 @@
 	lay->r.max.y = last->r.max.y;
 
 	indent = ((state&IFindentmask)>>IFindentshift) * Tabspace;
+	hang = (state&IFhangmask) * Tabspace / 10;
 	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.x = lay->r.min.x + indent - hang;
+	if(l->r.min.x < lay->r.min.x)
+		l->r.min.x = lay->r.min.x;
 	l->r.min.y = last->r.max.y + font->height*nl;
 	l->r.max = l->r.min;
 	l->prev = last;
@@ -1000,6 +1236,9 @@
 		layfree(c->lay);
 		c->lay = nil;
 	}
+	layfree(t->caption_lay);
+	t->caption_lay = nil;
+	t->caph = 0;
 }
 
 void
--- sys/src/cmd/abaco/tabs.c
+++ sys/src/cmd/abaco/tabs.c
@@ -15,15 +15,37 @@
 void
 drawtable(Box *b, Page *p, Image *im)
 {
-	Rectangle r, cr;
+	Rectangle r, cr, rr;
 	Tablecell *c;
 	Table *t;
+	int i, y, sep;
 
 	t = ((Itable *)b->i)->table;
 	r = rectsubpt(b->r, p->pos);
+	if(t->caption_lay){
+		laydraw(p, im, t->caption_lay);
+		if(t->caption_place == ALbottom)
+			r.max.y -= t->caph;
+		else
+			r.min.y += t->caph;
+	}
 	draw(im, r, getcolor(t->background.color), nil, ZP);
 	if(t->border)
 		border(im, r, t->border, display->black, ZP);
+	/* draw row backgrounds */
+	sep = 2*(t->border+t->cellpadding) + t->cellspacing;
+	y = r.min.y + t->cellspacing + t->border;
+	for(i=0; i<t->nrow; i++){
+		if(t->rows[i].background.color != t->background.color){
+			rr = r;
+			rr.min.x += t->border;
+			rr.max.x -= t->border;
+			rr.min.y = y;
+			rr.max.y = y + t->rows[i].height + 2*(t->border+t->cellpadding);
+			draw(im, rr, getcolor(t->rows[i].background.color), nil, ZP);
+		}
+		y += t->rows[i].height + sep;
+	}
 	for(c=t->cells; c!=nil; c=c->next){
 		cr = rectsubpt(c->lay->r, p->pos);
 		if(c->background.color != t->background.color)
@@ -60,6 +82,22 @@
 	}
 }
 
+static
+void
+setcaption(Table *t, Rectangle r)
+{
+	if(t->caption_lay){
+		layfree(t->caption_lay);
+		t->caption_lay = nil;
+	}
+	t->caph = 0;
+	if(t->caption == nil)
+		return;
+	t->caption_lay = layitems(t->caption, r, TRUE);
+	if(t->caption_lay)
+		t->caph = Dy(t->caption_lay->r);
+}
+
 void
 settables(Page *p)
 {
@@ -297,21 +335,33 @@
 	w = w>0 ? w : 0;
 	t->totw = tablewidth(t, w, sep);
 	t->totw += hsep;
+	setcaption(t, Rect(0, 0, t->totw, 0));
 	t->toth = tableheight(t, sep);
-	t->toth += vsep;
+	t->toth += vsep + t->caph;
 }
 
 void
 laytable(Itable *it, Rectangle r)
 {
-	Rectangle cr;
+	Rectangle cr, ir;
 	Tablecell *c;
 	Table *t;
-	int x, y, h, w;
+	Line *l;
+	Box *b;
+	int x, y, h, w, dy;
 	int sep, i;
 
 	t = it->table;
 
+	if(t->caption){
+		if(t->caption_place == ALbottom){
+			setcaption(t, Rect(r.min.x, r.max.y-t->caph, r.max.x, r.max.y-t->caph));
+			r.max.y -= t->caph;
+		}else{
+			setcaption(t, Rect(r.min.x, r.min.y, r.max.x, r.min.y));
+			r.min.y += t->caph;
+		}
+	}
 	sep = (t->cellpadding+t->border) * 2;
 	r = insetrect(r, t->cellspacing+t->border);
 	for(c=t->cells; c!=nil; c=c->next){
@@ -327,6 +377,24 @@
 				y += t->rows[i].height + sep + t->cellspacing;
 		cr = Rect(x, y, x+w+sep, y+h+sep);
 		c->lay = layitems(c->content, insetrect(cr, sep/2), TRUE);
+		/* vertical alignment */
+		ir = insetrect(cr, sep/2);
+		dy = Dy(ir) - Dy(c->lay->r);
+		if(dy > 0){
+			if(c->align.valign==ALmiddle)
+				dy /= 2;
+			else if(c->align.valign!=ALbottom)
+				dy = 0;
+			if(dy > 0)
+				for(l=c->lay->lines; l!=nil; l=l->next){
+					l->r.min.y += dy;
+					l->r.max.y += dy;
+					for(b=l->boxes; b!=nil; b=b->next){
+						b->r.min.y += dy;
+						b->r.max.y += dy;
+					}
+				}
+		}
 		c->lay->r = cr;
 	}
 }
--- sys/src/cmd/abaco/page.c
+++ sys/src/cmd/abaco/page.c
@@ -326,6 +326,7 @@
 				p->title.r = erunestrdup(p->doc->doctitle);
 				p->title.nr = runestrlen(p->title.r);
 			}
+			pagesetrefresh(p);
 			p->loading = XXX;
 			if(p->doc->kidinfo)
 				loadchilds(p, p->doc->kidinfo);
@@ -428,10 +429,36 @@
 }
 
 static
+Rectangle
+frameinset(Rectangle r, Kidinfo *k)
+{
+	int dx, dy;
+
+	if(k == nil)
+		return r;
+	dx = dy = 0;
+	if(k->framebd > 0)
+		dx = dy = k->framebd;
+	if(k->marginw > 0)
+		dx += k->marginw;
+	if(k->marginh > 0)
+		dy += k->marginh;
+	if(Dx(r) > 2*dx){
+		r.min.x += dx;
+		r.max.x -= dx;
+	}
+	if(Dy(r) > 2*dy){
+		r.min.y += dy;
+		r.max.y -= dy;
+	}
+	return r;
+}
+
+static
 void
 renderchilds(Page *p)
 {
-	Rectangle r;
+	Rectangle r, fr;
 	Kidinfo *k;
 	Page *c;
 	int i, j, x, y, *w, *h;
@@ -449,9 +476,12 @@
 			if(c->aborting)
 				return;
 			c->b = p->b;
-			c->all = Rect(x,y,x+w[j],y+h[i]);
+			fr = Rect(x, y, x+w[j], y+h[i]);
+			c->all = frameinset(fr, c->kidinfo);
 			c->w = p->w;
 			pagerender(c);
+			if(c->kidinfo && c->kidinfo->framebd > 0)
+				border(p->b, fr, c->kidinfo->framebd, display->black, ZP);
 			c = c->next;
 			x += w[j];
 		}
--- sys/src/cmd/abaco/NOTES
+++ sys/src/cmd/abaco/NOTES
@@ -6,9 +6,9 @@
 	  are caused by sites that return a jpeg and send Content-Type: image/gif.
 
 Not implented:
-	* frame's borders and scroll are ignored
-	* Image: maps
-	* table atributes
+	* frame scroll is ignored
+	* many table attributes still partial
+	* floats (text wrapping around images)
 	* css
 	* Js
 

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].