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
|