summaryrefslogtreecommitdiff
path: root/libprefab
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /libprefab
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'libprefab')
-rw-r--r--libprefab/NOTICE25
-rw-r--r--libprefab/box.c80
-rw-r--r--libprefab/compound.c193
-rw-r--r--libprefab/element.c723
-rw-r--r--libprefab/elistelement.c113
-rw-r--r--libprefab/iconbox.c83
-rw-r--r--libprefab/iconelement.c50
-rw-r--r--libprefab/mkfile21
-rw-r--r--libprefab/textbox.c138
-rw-r--r--libprefab/textelement.c488
10 files changed, 1914 insertions, 0 deletions
diff --git a/libprefab/NOTICE b/libprefab/NOTICE
new file mode 100644
index 00000000..e8c19e7f
--- /dev/null
+++ b/libprefab/NOTICE
@@ -0,0 +1,25 @@
+This copyright NOTICE applies to all files in this directory and
+subdirectories, unless another copyright notice appears in a given
+file or subdirectory. If you take substantial code from this software to use in
+other programs, you must somehow include with it an appropriate
+copyright notice that includes the copyright notice and the other
+notices below. It is fine (and often tidier) to do that in a separate
+file such as NOTICE, LICENCE or COPYING.
+
+Copyright © 1995-1999 Lucent Technologies Inc.
+Portions Copyright © 1997-2000 Vita Nuova Limited
+Portions Copyright © 2000-2006 Vita Nuova Holdings Limited
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License (`LGPL') as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
diff --git a/libprefab/box.c b/libprefab/box.c
new file mode 100644
index 00000000..a64d791f
--- /dev/null
+++ b/libprefab/box.c
@@ -0,0 +1,80 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+PCompound*
+box(Prefab_Environ *e, Draw_Point p, Prefab_Element *title, Prefab_Element *list)
+{
+ Draw_Rect er, r, lr;
+ PCompound *pc;
+ Prefab_Compound *c;
+ Image *disp;
+ Draw_Image *ddisp;
+ Screen *screen;
+ Heap *h;
+ Point pt;
+ int w;
+
+ if(list == H)
+ return H;
+ screen = lookupscreen(e->screen);
+ if(screen == nil)
+ return H;
+ h = heapz(TCompound);
+ if(h == H)
+ return H;
+ pc = H2D(PCompound*, h);
+ c = &pc->c;
+
+ gchalt++;
+ r = list->r;
+ if(title != H){
+ w = 2+1+3+Dx(title->r)+1;
+ if(w > Dx(r))
+ r.max.x = r.min.x + w;
+ r.max.y += 2+1+Dy(title->r)+1;
+ }
+
+ er = edgerect(e, p, &r);
+
+ disp = allocwindow(screen, IRECT(er), Refbackup /*refreshcompound*/, DWhite);
+ if(disp == nil){
+ Err:
+ destroy(c);
+ gchalt--;
+ return H;
+ }
+ if((ddisp=mkdrawimage(disp, e->screen, e->screen->display, nil)) == H){
+ freeimage(disp);
+ goto Err;
+ }
+
+ lr = r;
+ if(title != H){
+ pt.x = r.min.x+3;
+ pt.y = r.min.y+3;
+ translateelement(title, pt);
+ lr.min.y = title->r.max.y+1;
+ }
+ translateelement(list, subpt(IPOINT(lr.min), IPOINT(list->r.min)));
+
+ c->r = r;
+ c->image = ddisp;
+ c->environ = e;
+ D2H(e)->ref++;
+ if(title != H){
+ c->title = title;
+ D2H(title)->ref++;
+ }
+ if(list != H){
+ c->contents = (Prefab_Element*)list;
+ D2H(list)->ref++;
+ }
+ pc->display = screen->display;
+ gchalt--;
+ return pc;
+}
diff --git a/libprefab/compound.c b/libprefab/compound.c
new file mode 100644
index 00000000..b9cf820e
--- /dev/null
+++ b/libprefab/compound.c
@@ -0,0 +1,193 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+extern void queuerefresh(Image *i, Rectangle r, Reffn reffn, void *refptr);
+
+Draw_Rect
+edgerect(Prefab_Environ *e, Draw_Point p, Draw_Rect *rin)
+{
+ Draw_Rect r;
+ Screen *s;
+
+ r.min.x = p.x;
+ r.min.y = p.y;
+ r.max.x = p.x + 1 + Dx(*rin) + 1;
+ r.max.y = p.y + 1 + Dy(*rin) + 1;
+ /* outer box computed; now make sure it's all visible */
+ s = lookupscreen(e->screen);
+ if(s != nil)
+ fitrect((Rectangle*)&r, s->display->image->r);
+
+ rin->min.x = r.min.x+1;
+ rin->min.y = r.min.y+1;
+ rin->max.x = r.max.x-1;
+ rin->max.y = r.max.y-1;
+
+ return r;
+}
+
+/*
+ * Draw edge around r.
+ * Assume geometry has already been clipped and adjusted.
+ */
+void
+edge(Prefab_Environ *e, Image *box, Draw_Rect dr, Draw_Rect dclipr)
+{
+ Rectangle r, r1, clipr;
+ Image *ec;
+ Screen *s;
+
+ R2R(r, dr);
+ R2R(clipr, dclipr);
+ r.min.x -= 1;
+ r.min.y -= 1;
+ r.max.x += 1;
+ r.max.y += 1;
+ s = lookupscreen(e->screen);
+ if(s == nil)
+ return;
+ ec = lookupimage(e->style->edgecolor);
+ if(ec == nil)
+ return;
+
+ r1 = r;
+ r1.min.y++;
+ r1.max.y = r1.min.y+2;
+ r1.max.x = r1.min.x+2*(r1.max.x-r1.min.x)/3;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+ r1 = r;
+ r1.min.x++;
+ r1.max.x = r1.min.x+2;
+ r1.max.y = r1.min.y+2*(r1.max.y-r1.min.y)/3;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+ r1=r;
+ r1.min.x = r1.max.x-1;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+ r1=r;
+ r1.min.y = r1.max.y-1;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+ r1 = r;
+ r1.max.y = r1.min.y+1;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+ r1=r;
+ r1.max.x = r1.min.x+1;
+ if(rectclip(&r1, clipr))
+ draw(box, r1, ec, nil, r1.min);
+}
+
+void
+redrawcompound(Image *i, Rectangle clipr, Prefab_Compound *c)
+{
+ Rectangle r1, rt, r;
+ int l, len;
+ Prefab_Style *s;
+ Image *elemcolor, *edgecolor;
+ List *list;
+ Font *font;
+ Prefab_Element *e;
+
+ if(c==H || badenviron(c->environ, 0))
+ return;
+
+ r = clipr;
+ s = c->environ->style;
+ elemcolor = lookupimage(s->elemcolor);
+ edgecolor = lookupimage(s->edgecolor);
+ if(elemcolor==nil || edgecolor==nil)
+ return;
+ draw(i, r, elemcolor, nil, r.min);
+ if(lookupelement(c->title) != H){
+ R2R(rt, c->title->r);
+ if(c->title->environ!=H && c->title->environ->style!=H && rectXrect(r, rt)){
+ drawelement(c->title, i, r, c->environ==c->title->environ, 0);
+ r1.min.x = c->r.min.x;
+ r1.min.y = c->title->r.max.y;
+ s = c->title->environ->style;
+ len = 0;
+ switch(c->title->kind){
+ case ETitle:
+ font = lookupfont(s->titlefont);
+ if(font != nil)
+ len = 2+1+stringwidth(font, string2c(c->title->str));
+ break;
+ case EVertical:
+ font = lookupfont(s->titlefont);
+ if(font != nil)
+ for(list=c->title->kids; list!=H; list=list->tail){
+ e = *(Prefab_Element**)list->data;
+ l = stringwidth(font, string2c(e->str));
+ if(l > len)
+ len = l;
+ }
+ len += 2+1;
+ break;
+ default:
+ len = r1.min.x+2*Dx(c->r)/3;
+ }
+ r1.max.x = r1.min.x + len;
+ r1.max.y = r1.min.y+1;
+ draw(i, r1, edgecolor, nil, r.min);
+ r.min.y = r1.max.y;
+ }
+ }
+ if(c->contents!=H)
+ drawelement(c->contents, i, r, c->environ==c->contents->environ, 0);
+ edge(c->environ, i, c->r, DRECT(clipr));
+}
+
+void
+refreshcompound(Image *i, Rectangle r, void *ptr)
+{
+ Prefab_Compound *c;
+
+ c = ptr;
+ if(c == nil)
+ return;
+ if(i == nil){ /* called from flushimage */
+ i = lookupimage(c->image);
+ if(i == nil)
+ return;
+ }
+ redrawcompound(i, r, c);
+}
+
+void
+localrefreshcompound(Memimage *mi, Rectangle r, void *ptr)
+{
+ Prefab_Compound *c;
+ Image *i;
+
+ USED(mi); /* can't do anything with this, but it's part of the memlayer interface */
+ c = ptr;
+ if(c == nil)
+ return;
+ i = lookupimage(c->image);
+ if(i == nil)
+ return;
+ queuerefresh(i, r, refreshcompound, ptr);
+}
+
+void
+drawcompound(Prefab_Compound *c)
+{
+ Image *i;
+
+ if(c==H || c->image==H)
+ return;
+ i = lookupimage(c->image);
+ redrawcompound(i, insetrect(IRECT(c->r), -1), c);
+ if(i->display->local && i->refptr==nil)
+ if(drawlsetrefresh(i->display->dataqid, i->id, localrefreshcompound, c) <= 0)
+ fprint(2, "drawcompound: can't set refresh\n");
+ i->refptr = c; /* can now be refreshed */
+}
diff --git a/libprefab/element.c b/libprefab/element.c
new file mode 100644
index 00000000..721a955c
--- /dev/null
+++ b/libprefab/element.c
@@ -0,0 +1,723 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+
+void icondraw(Prefab_Element*, Image*, Rectangle, int, int);
+void textdraw(Prefab_Element*, Image*, Rectangle, int, int);
+void listdraw(Prefab_Element*, Image*, Rectangle, int, int);
+void outlinehighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
+void texthighlight(Prefab_Element*, Image*, Prefab_Compound*, int);
+void simpleclip(Prefab_Element*, Rectangle);
+void horizontalclip(Prefab_Element*, Rectangle);
+void verticalclip(Prefab_Element*, Rectangle);
+void textscroll(Prefab_Element*, Point, int*);
+void horizontalscroll(Prefab_Element*, Point, int*);
+void verticalscroll(Prefab_Element*, Point, int*);
+void iconscroll(Prefab_Element*, Point, int*);
+
+struct
+{
+ void (*draw)(Prefab_Element*, Image*, Rectangle, int, int);
+ void (*highlight)(Prefab_Element*, Image*, Prefab_Compound*, int);
+ void (*clip)(Prefab_Element*, Rectangle);
+ void (*scroll)(Prefab_Element*, Point, int*);
+}elemfn[] = {
+ /* EIcon */ { icondraw, outlinehighlight, simpleclip, iconscroll, },
+ /* EText */ { textdraw, texthighlight, simpleclip, textscroll, },
+ /* ETitle */ { textdraw, outlinehighlight, simpleclip, textscroll, },
+ /* EHorizontal */ { listdraw, outlinehighlight, horizontalclip, horizontalscroll, },
+ /* EVertical */ { listdraw, outlinehighlight, verticalclip, verticalscroll, },
+ /* ESeparator */ { icondraw, outlinehighlight, simpleclip, iconscroll, },
+};
+
+Point
+iconsize(Image *image)
+{
+ Point dd;
+
+ if(image->repl){
+ dd.x = Dx(image->clipr);
+ dd.y = Dy(image->clipr);
+ }else{
+ dd.x = Dx(image->r);
+ dd.y = Dy(image->r);
+ }
+ return dd;
+}
+
+void
+icondraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Rectangle r;
+ Point p;
+ PElement *pelem;
+ Image *image, *c;
+ Point size;
+
+ USED(highlight);
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->image==H || elem->mask==H || badenviron(elem->environ, 0))
+ return;
+ style = elem->environ->style;
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ r.min = pelem->drawpt;
+ image = lookupimage(elem->image);
+ if(image == nil)
+ return;
+ size = iconsize(image);
+ r.max.x = r.min.x+size.x;
+ r.max.y = r.min.y+size.y;
+ if(rectclip(&r, clipr)){
+ p = image->r.min;
+ p.x += r.min.x-pelem->drawpt.x;
+ p.y += r.min.y-pelem->drawpt.y;
+ c = lookupimage(elem->mask);
+ if(c != nil)
+ draw(i, r, image, c, p);
+ }
+}
+
+void
+textdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Rectangle r;
+ PElement *pelem;
+ Image *color, *c;
+ Font *font;
+
+ USED(highlight);
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->str==H || badenviron(elem->environ, 0))
+ return;
+ style = elem->environ->style;
+ font = lookupfont(elem->font);
+ if(font == nil)
+ return;
+ if(highlight)
+ color = lookupimage(style->highlightcolor);
+ else
+ color = lookupimage(elem->image);
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ if(color != nil)
+ _string(i, pelem->drawpt, color, pelem->drawpt, font, string2c(elem->str), nil, 1<<24, clipr, nil, pelem->drawpt, SoverD);
+}
+
+void
+listdraw(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ Prefab_Style *style;
+ Prefab_Element *e;
+ List *l;
+ Rectangle r;
+ PElement *pelem;
+ Image *c;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(!rectclip(&clipr, i->clipr))
+ return;
+ R2R(r, elem->r);
+ if(!rectclip(&clipr, r))
+ return;
+ if(elem->kids==H || badenviron(elem->environ, 0))
+ return;
+ if(pelem->first != elem->kids) /* error? */
+ return;
+ style = elem->environ->style;
+ if(!clean){
+ c = lookupimage(style->elemcolor);
+ if(c != nil)
+ draw(i, clipr, c, nil, clipr.min);
+ }
+ for(l=pelem->vfirst; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ R2R(r, e->r);
+ if(rectXrect(r, clipr))
+ drawelement(e, i, clipr, elem->environ==e->environ, highlight);
+ if(l == pelem->vlast)
+ break;
+ }
+}
+
+void
+drawelement(Prefab_Element *elem, Image *i, Rectangle clipr, int clean, int highlight)
+{
+ PElement *pelem;
+
+ if(elem != H){
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ (*elemfn[elem->kind].draw)(elem, i, clipr, clean, highlight);
+ if(!highlight && pelem->highlight!=H)
+ (*elemfn[elem->kind].highlight)(elem, i, pelem->highlight, 1);
+ }
+}
+
+void
+translateelement(Prefab_Element *elem, Point delta)
+{
+ PElement *pelem;
+ List *l;
+
+ if(elem == H)
+ return;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ elem->r.min.x += delta.x;
+ elem->r.min.y += delta.y;
+ elem->r.max.x += delta.x;
+ elem->r.max.y += delta.y;
+ pelem->drawpt.x += delta.x;
+ pelem->drawpt.y += delta.y;
+ switch(elem->kind){
+ case EHorizontal:
+ case EVertical:
+ if(pelem->first != elem->kids)
+ return;
+ for(l=elem->kids; l!=H; l=l->tail)
+ translateelement(*(Prefab_Element**)l->data, delta);
+ break;
+ }
+}
+
+int
+fitrect(Rectangle *r, Rectangle sr)
+{
+ if(r->max.x > sr.max.x){
+ r->min.x -= r->max.x-sr.max.x;
+ r->max.x = sr.max.x;
+ }
+ if(r->max.y > sr.max.y){
+ r->min.y -= r->max.y-sr.max.y;
+ r->max.y = sr.max.y;
+ }
+ if(r->min.x < sr.min.x){
+ r->max.x += sr.min.x-r->min.x;
+ r->min.x = sr.min.x;
+ }
+ if(r->min.y < sr.min.y){
+ r->max.y += sr.min.y-r->min.y;
+ r->min.y = sr.min.y;
+ }
+ return rectinrect(*r, sr);
+}
+
+void
+adjusthorizontal(Prefab_Element *elem, int spacing, int position)
+{
+ int edx, dx, i, x;
+ int nlist; /* BUG: should precompute */
+ List *l;
+ PElement *pelem;
+ Prefab_Element *e;
+ Point p;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(pelem->first != elem->kids)
+ return;
+ p.y = 0;
+ switch(spacing){
+ default: /* shouldn't happen; protected by adjustelement */
+ case Adjpack:
+ x = elem->r.min.x;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ p.x = x - e->r.min.x;
+ translateelement(e, p);
+ x += Dx(e->r);
+ }
+ elem->r.max.x = x;
+ return;
+
+ case Adjequal:
+ dx = 0;
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(dx < Dx(e->r))
+ dx = Dx(e->r);
+ nlist++;
+ }
+ elem->r.max.x = elem->r.min.x+nlist*dx;
+ break;
+
+ case Adjfill:
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail)
+ nlist++;
+ dx = Dx(elem->r)/nlist;
+ break;
+ }
+ i = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ edx = Dx(e->r);
+ if(position == Adjleft)
+ edx = 0;
+ else if(position == Adjcenter)
+ edx = (dx-edx)/2;
+ else /* right */
+ edx = dx-edx;
+ p.x = (elem->r.min.x+i*dx + edx) - e->r.min.x;
+ translateelement(e, p);
+ i++;
+ }
+}
+
+void
+adjustvertical(Prefab_Element *elem, int spacing, int position)
+{
+ int edy, dy, i, y;
+ int nlist; /* BUG: should precompute */
+ List *l;
+ PElement *pelem;
+ Prefab_Element *e;
+ Point p;
+
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ if(pelem->first != elem->kids)
+ return;
+ p.x = 0;
+ switch(spacing){
+ default: /* shouldn't happen; protected by adjustelement */
+ case Adjpack:
+ y = elem->r.min.y;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ p.y = y - e->r.min.y;
+ translateelement(e, p);
+ y += Dy(e->r);
+ }
+ elem->r.max.y = y;
+ return;
+
+ case Adjequal:
+ dy = 0;
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(dy < Dy(e->r))
+ dy = Dy(e->r);
+ nlist++;
+ }
+ elem->r.max.y = elem->r.min.y+nlist*dy;
+ break;
+
+ case Adjfill:
+ nlist = 0;
+ for(l=elem->kids; l!=H; l=l->tail)
+ nlist++;
+ dy = Dy(elem->r)/nlist;
+ break;
+ }
+ i = 0;
+ for(l=elem->kids; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ edy = Dy(e->r);
+ if(position == Adjup)
+ edy = 0;
+ else if(position == Adjcenter)
+ edy = (dy-edy)/2;
+ else /* down */
+ edy = dy-edy;
+ p.y = (elem->r.min.y+i*dy + edy) - e->r.min.y;
+ translateelement(e, p);
+ i++;
+ }
+}
+
+void
+adjustelement(Prefab_Element *elem, int spacing, int position)
+{
+ if(lookupelement(elem) == H)
+ return;
+ if(spacing<Adjpack || spacing>Adjfill || position<Adjleft || position>Adjdown)
+ return;
+ switch(elem->kind){
+ case EVertical:
+ adjustvertical(elem, spacing, position);
+ break;
+ case EHorizontal:
+ adjusthorizontal(elem, spacing, position);
+ break;
+ }
+}
+
+void
+highlightelement(Prefab_Element *elem, Image *i, Prefab_Compound *comp, int on)
+{
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem!=H && lookupcompound(comp)!=H){
+ if(on)
+ pelem->highlight = comp;
+ else
+ pelem->highlight = H;
+ (*elemfn[elem->kind].highlight)(elem, i, comp, on);
+ }
+}
+
+static
+int
+anytextelements(Prefab_Element *e)
+{
+ Prefab_Element *t;
+ List *l;
+
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind == EText)
+ return 1;
+ }
+ return 0;
+}
+
+void
+textlisthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ Prefab_Element *t;
+ List *l;
+
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind == EText)
+ texthighlight(t, i, c, on);
+ }
+}
+
+void
+outlinehighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ List *l;
+ Prefab_Element *t;
+ Image *color;
+ Rectangle r, r1, r2;
+ Point dp;
+ int done;
+
+ /* see if we can do it by highlighting just a text element */
+ if((e->kind==EVertical || e->kind==EHorizontal) && e->kids!=H){
+ /* is any child a text element? */
+ if(anytextelements(e)){
+ textlisthighlight(e, i, c, on);
+ return;
+ }
+ /* grandchild? */
+ done = 0;
+ for(l=e->kids; l!=H; l=l->tail){
+ t = *(Prefab_Element**)l->data;
+ if(t->kind==EVertical || t->kind==EHorizontal)
+ if(anytextelements(t)){
+ textlisthighlight(t, i, c, on);
+ done = 1;
+ }
+ }
+ if(done)
+ return;
+ }
+ if(on){
+ color = lookupimage(e->environ->style->highlightcolor);
+ if(color == nil)
+ return;
+ R2R(r, e->r);
+ /* avoid outlining empty space around images */
+ dp = ((PElement*)e)->drawpt;
+ if(e->kind==EIcon && e->image->repl==0 && ptinrect(dp, r)){
+ R2R(r1, e->image->r);
+ R2R(r2, e->image->clipr);
+ if(rectclip(&r1, r2)){
+ dp.x += Dx(r1);
+ dp.y += Dy(r1);
+ if(ptinrect(dp, r))
+ r = Rpt(((PElement*)e)->drawpt, dp);
+ }
+ }
+ draw(i, r, color, nil, r.min);
+ drawelement(e, i, insetrect(r, 2), Dirty, 1);
+ }else{
+ drawelement(e, i, IRECT(e->r), Dirty, 0);
+ edge(c->environ, i, c->r, e->r);
+ }
+}
+
+void
+texthighlight(Prefab_Element *e, Image *i, Prefab_Compound *c, int on)
+{
+ drawelement(e, i, IRECT(e->r), Clean, on);
+ edge(c->environ, i, c->r, e->r);
+}
+
+void
+clipelement(Prefab_Element *elem, Rectangle r)
+{
+ if(lookupelement(elem) != H)
+ (*elemfn[elem->kind].clip)(elem, r);
+}
+
+void
+simpleclip(Prefab_Element *elem, Rectangle r)
+{
+ R2R(elem->r, r);
+}
+
+void
+horizontalclip(Prefab_Element *elem, Rectangle r)
+{
+ int x;
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ x = r.min.x;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ for(l=pelem->vfirst; l!=H && x<r.max.x; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ x += Dx(e->r);
+ }
+ pelem->vlast = l;
+ R2R(elem->r, r);
+}
+
+void
+verticalclip(Prefab_Element *elem, Rectangle r)
+{
+ int y;
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ y = r.min.y;
+ pelem = lookupelement(elem);
+ if(pelem == H)
+ return;
+ for(l=pelem->vfirst; l!=H && y<r.max.y; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ y += Dy(e->r);
+ }
+ pelem->vlast = l;
+ R2R(elem->r, r);
+}
+
+void
+scrollelement(Prefab_Element *elem, Point d, int *moved)
+{
+ if(lookupelement(elem) != H)
+ (*elemfn[elem->kind].scroll)(elem, d, moved);
+}
+
+void
+textscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || (d.x==0 && d.y==0))
+ return;
+ pelem->drawpt = subpt(pelem->drawpt, d);
+ *moved = 1;
+}
+
+void
+iconscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ Point p;
+ Image *i;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->image==H || (d.x==0 && d.y==0))
+ return;
+ i = lookupimage(elem->image);
+ if(i == nil)
+ return;
+ p = subpt(pelem->drawpt, d);
+ if(i->repl == 0){
+ if(p.x+Dx(i->clipr) < elem->r.max.x)
+ p.x = elem->r.max.x - Dx(i->clipr);
+ if(p.y+Dy(i->clipr) < elem->r.max.y)
+ p.y = elem->r.max.y - Dy(i->clipr);
+ if(p.x > elem->r.min.x)
+ p.x = elem->r.min.x;
+ if(p.y > elem->r.min.y)
+ p.y = elem->r.min.y;
+ }
+ *moved = !eqpt(pelem->drawpt, p);
+ pelem->drawpt = p;
+}
+
+void
+horizontalscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
+ return;
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ translateelement(e, d);
+ }
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(e->r.max.x > elem->r.min.x)
+ break;
+ }
+ pelem->vfirst = l;
+ pelem->vlast = l;
+ for(; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ pelem->vlast = l;
+ if(e->r.min.x >= elem->r.max.x)
+ break;
+ }
+ *moved = 1;
+}
+
+void
+verticalscroll(Prefab_Element *elem, Point d, int *moved)
+{
+ List *l;
+ Prefab_Element *e;
+ PElement *pelem;
+
+ pelem = lookupelement(elem);
+ if(pelem==H || elem->kids==H || (d.x==0 && d.y==0))
+ return;
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ translateelement(e, d);
+ }
+ for(l=pelem->first; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ if(e->r.max.y > elem->r.min.y)
+ break;
+ }
+ pelem->vfirst = l;
+ pelem->vlast = l;
+ for(; l!=H; l=l->tail){
+ e = *(Prefab_Element**)l->data;
+ pelem->vlast = l;
+ if(e->r.min.y >= elem->r.max.y)
+ break;
+ }
+ *moved = 1;
+}
+
+/*
+ * Make e visible within list. Return value is whether any change was made;
+ * if so, must redraw (BUG: should probably do this here)
+ */
+int
+showelement(Prefab_Element *list, Prefab_Element *e)
+{
+ Point p;
+ Prefab_Element *h, *t;
+ PElement *plist;
+ int moved;
+
+ p.x = p.y = 0;
+ if(list->kids == H)
+ return 0;
+ plist = lookupelement(list);
+ if(plist == H)
+ return 0;
+ h = *(Prefab_Element**)plist->first->data;
+ t = *(Prefab_Element**)plist->last->data;
+ if(list->kind == EHorizontal){
+ p.x = (list->r.min.x+Dx(list->r)/2) - e->r.min.x;
+ if(e->r.min.x < list->r.min.x){ /* scroll to right */
+ if(e->r.max.x+p.x > list->r.max.x)
+ p.x = list->r.min.x-e->r.min.x;
+ if(h->r.min.x + p.x > list->r.min.x)
+ p.x = list->r.min.x-h->r.min.x;
+ }else if(e->r.max.x > list->r.max.x){ /* scroll to left */
+ if(e->r.min.x+p.x < list->r.min.x)
+ p.x = list->r.min.x-e->r.min.x;
+ if(t->r.max.x + p.x < list->r.max.x)
+ p.x = list->r.max.x-t->r.max.x;
+ }else
+ return 0;
+ }else if(list->kind == EVertical){
+ p.y = (list->r.min.y+Dy(list->r)/2) - e->r.min.y;
+ if(e->r.min.y < list->r.min.y){ /* scroll towards bottom */
+ if(e->r.max.y+p.y > list->r.max.y)
+ p.y = list->r.min.y-e->r.min.y;
+ if(h->r.min.y + p.y > list->r.min.y)
+ p.y = list->r.min.y-h->r.min.y;
+ }else if(e->r.max.y > list->r.max.y){ /* scroll towards top */
+ if(e->r.min.y+p.y < list->r.min.y)
+ p.y = list->r.min.y-e->r.min.y;
+ if(t->r.max.y + p.y < list->r.max.y)
+ p.y = list->r.max.y-t->r.max.y;
+ }else
+ return 0;
+ }else
+ return 0;
+ if(p.x!=0 || p.y!=0){
+ scrollelement(list, p, &moved);
+ return 1;
+ }
+ return 0;
+}
+
+PElement*
+mkelement(Prefab_Environ *env, enum Elementtype t)
+{
+ Heap *h;
+ PElement *p;
+
+ h = heapz(TElement);
+ p = H2D(PElement*, h);
+ p->highlight = H;
+ p->first = H;
+ p->last = H;
+ p->vfirst = H;
+ p->vlast = H;
+ p->nkids = 1;
+ p->pkind = t;
+ p->e.kind = t;
+ p->e.environ = env;
+ D2H(env)->ref++;
+ return p;
+}
diff --git a/libprefab/elistelement.c b/libprefab/elistelement.c
new file mode 100644
index 00000000..69ca2026
--- /dev/null
+++ b/libprefab/elistelement.c
@@ -0,0 +1,113 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+#include <kernel.h>
+
+List*
+prefabwrap(void *elem)
+{
+ List *l;
+ Heap *h, *e;
+
+ e = D2H(elem);
+ h = nheap(sizeof(List) + sizeof(WORD*));
+ h->t = &Tlist;
+ Tlist.ref++;
+ l = H2D(List*, h);
+ l->tail = H;
+ l->t = &Tptr;
+ Tptr.ref++;
+ e->ref++;
+ *(WORD**)l->data = elem;
+ return l;
+}
+
+static
+PElement*
+elistelement1(Prefab_Environ *e, Prefab_Element *elem, Prefab_Element *new, enum Elementtype kind)
+{
+ int first;
+ PElement *pelem;
+ List *atom;
+
+ if(badenviron(e, 0))
+ return H;
+
+ gchalt++;
+ first = 0;
+ if(new == H)
+ atom = H;
+ else
+ atom = prefabwrap(new);
+ if(elem == H){
+ pelem = mkelement(e, kind);
+ elem = &pelem->e;
+ pelem->first = H;
+ pelem->nkids = 0;
+ }else
+ pelem = (PElement*)elem;
+ if(atom == H)
+ goto Return;
+
+ if(elem->kids != pelem->first)
+ error("list Element has been modified externally");
+ if(elem->kids == H){
+ elem->kids = atom;
+ pelem->first = atom;
+ pelem->last = atom;
+ pelem->vfirst = atom;
+ pelem->vlast = atom;
+ first = 1;
+ }
+ if(new->kind!=ESeparator && Dx(elem->r)==0){
+ elem->r = new->r;
+ pelem->drawpt.x = elem->r.min.x;
+ pelem->drawpt.y = elem->r.min.y;
+ }
+ pelem->nkids++;
+ if(first)
+ goto Return;
+ pelem->last->tail = atom;
+ pelem->last = atom;
+ pelem->vlast = atom;
+ if(new->kind != ESeparator){
+ if(kind == EVertical){
+ elem->r.max.y += Dy(new->r);
+ if(elem->r.min.x > new->r.min.x)
+ elem->r.min.x = new->r.min.x;
+ if(elem->r.max.x < new->r.max.x)
+ elem->r.max.x = new->r.max.x;
+ }else{
+ elem->r.max.x += Dx(new->r);
+ if(elem->r.min.y > new->r.min.y)
+ elem->r.min.y = new->r.min.y;
+ if(elem->r.max.y < new->r.max.y)
+ elem->r.max.y = new->r.max.y;
+ }
+ }
+ pelem->pkind = kind;
+
+ Return:
+ gchalt--;
+ return pelem;
+}
+
+PElement*
+elistelement(Prefab_Environ *e, Prefab_Element *new, enum Elementtype kind)
+{
+ return elistelement1(e, H, new, kind);
+}
+
+PElement*
+appendelist(Prefab_Element *elem, Prefab_Element *new)
+{
+ if(elem->kind!=EVertical && elem->kind!=EHorizontal){
+ kwerrstr("appendelist to non-list");
+ return H;
+ }
+ return elistelement1(elem->environ, elem, new, elem->kind);
+}
diff --git a/libprefab/iconbox.c b/libprefab/iconbox.c
new file mode 100644
index 00000000..0586d6a0
--- /dev/null
+++ b/libprefab/iconbox.c
@@ -0,0 +1,83 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+PCompound*
+iconbox(Prefab_Environ *e, Draw_Point p, String *titletext, Draw_Image *icon, Draw_Image *mask)
+{
+ Draw_Rect er, r, ir;
+ PCompound *pc;
+ Prefab_Compound *c;
+ PElement *elem, *title;
+ Image *disp;
+ Draw_Image *ddisp;
+ Screen *screen;
+ Heap *h;
+ Rectangle t;
+ Point pt;
+
+ screen = lookupscreen(e->screen);
+ if(screen == nil)
+ return H;
+ h = heapz(TCompound);
+ if(h == H)
+ return H;
+ pc = H2D(PCompound*, h);
+ c = &pc->c;
+
+ gchalt++;
+ title = H;
+ if(titletext != H){
+ er.min.x = 0;
+ er.min.y = 0;
+ er.max.x = Dx(icon->r)-5;
+ er.max.y = 0;
+ title = textelement(e, titletext, er, ETitle);
+ if(title == H){
+ Err:
+ destroy(c);
+ gchalt--;
+ return H;
+ }
+ c->title = (Prefab_Element*)title;
+ }
+
+ r = icon->r;
+ if(title != H)
+ r.max.y += 2+1+title->nkids*e->style->titlefont->height+1;
+
+ er = edgerect(e, p, &r);
+
+ R2R(t, er);
+ disp = allocwindow(screen, t, Refbackup /*refreshcompound*/, DWhite);
+ if(disp == nil)
+ goto Err;
+
+ if((ddisp=mkdrawimage(disp, e->screen, e->screen->display, nil)) == H){
+ freeimage(disp);
+ goto Err;
+ }
+
+ ir = r;
+ if(title != H){
+ ir = r;
+ pt.x = r.min.x+3;
+ pt.y = r.min.y+3;
+ translateelement(&title->e, pt);
+ ir.min.y = title->e.r.max.y+1;
+ }
+
+ elem = iconelement(e, ir, icon, mask);
+ c->r = r;
+ c->image = ddisp;
+ c->environ = e;
+ D2H(e)->ref++;
+ c->contents = (Prefab_Element*)elem;
+ pc->display = screen->display;
+ gchalt--;
+ return pc;
+}
diff --git a/libprefab/iconelement.c b/libprefab/iconelement.c
new file mode 100644
index 00000000..94d089bb
--- /dev/null
+++ b/libprefab/iconelement.c
@@ -0,0 +1,50 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+static
+PElement*
+iconelement1(Prefab_Environ *e, Draw_Rect r, Draw_Image *icon, Draw_Image *mask, enum Elementtype kind)
+{
+ PElement *pelem;
+ Prefab_Element *elem;
+
+ if(badenviron(e, 0) || icon==H || mask==H)
+ return H;
+ pelem = mkelement(e, kind);
+ if(pelem == H)
+ return H;
+ elem = &pelem->e;
+
+ if(Dx(r))
+ elem->r = r;
+ else{
+ elem->r.min = r.min;
+ elem->r.max.x = r.min.x + Dx(icon->r);
+ elem->r.max.y = r.min.y + Dy(icon->r);
+ }
+ elem->mask = mask;
+ D2H(mask)->ref++;
+ elem->image = icon;
+ D2H(icon)->ref++;
+ pelem->drawpt = IPOINT(r.min);
+ pelem->nkids = 1;
+ pelem->pkind = kind;
+ return pelem;
+}
+
+PElement*
+iconelement(Prefab_Environ *e, Draw_Rect r, Draw_Image *icon, Draw_Image *mask)
+{
+ return iconelement1(e, r, icon, mask, EIcon);
+}
+
+PElement*
+separatorelement(Prefab_Environ *e, Draw_Rect r, Draw_Image *icon, Draw_Image *mask)
+{
+ return iconelement1(e, r, icon, mask, ESeparator);
+}
diff --git a/libprefab/mkfile b/libprefab/mkfile
new file mode 100644
index 00000000..f874c98d
--- /dev/null
+++ b/libprefab/mkfile
@@ -0,0 +1,21 @@
+<../mkconfig
+
+LIB=libprefab.a
+
+OFILES=\
+ box.$O\
+ compound.$O\
+ element.$O\
+ elistelement.$O\
+ iconbox.$O\
+ iconelement.$O\
+ textbox.$O\
+ textelement.$O\
+
+HFILES=\
+ $ROOT/include/draw.h\
+ $ROOT/include/prefab.h\
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+
+$OFILES: $ROOT/module/prefab.m
diff --git a/libprefab/textbox.c b/libprefab/textbox.c
new file mode 100644
index 00000000..6211bfdb
--- /dev/null
+++ b/libprefab/textbox.c
@@ -0,0 +1,138 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+
+PCompound*
+layoutbox(Prefab_Environ *e, Draw_Rect rr, String *titletext, List *texttext)
+{
+ Draw_Rect er, r, lr;
+ PCompound *pc;
+ Prefab_Compound *c;
+ PElement *title, *text;
+ Image *disp;
+ Draw_Image *ddisp;
+ Screen *screen;
+ Heap *h;
+ Rectangle t;
+ int wid, w;
+ Point p, pt;
+
+ screen = lookupscreen(e->screen);
+ if(screen == nil)
+ return H;
+
+ gchalt++;
+ wid = Dx(rr);
+ P2P(p, rr.min);
+ title = H;
+ text = H;
+ if(texttext != H){
+ er.min.x = 0;
+ er.min.y = 0;
+ er.max.x = wid-5;
+ er.max.y = Dy(rr);
+ text = layoutelement(e, texttext, er, EText);
+ if(text == H){
+ gchalt--;
+ return H;
+ }
+ if(wid <= 0)
+ wid = Dx(text->e.r)+5;
+ }
+ if(titletext != H){
+ /* see how wide title wants to be */
+ memset(&er, 0, sizeof er);
+ title = textelement(e, titletext, er, ETitle);
+ if(title == H){
+ Errtitle:
+ destroy(text);
+ gchalt--;
+ return H;
+ }
+ w = 2+1+3+Dx(title->e.r)+1;
+ /* if title is wider than text, adjust wid accordingly */
+ if(text!=0 && Dx(text->e.r)<w){
+ if(Dx(text->e.r) < 100){ /* narrow text; don't let title get too wide */
+ if(w > 250+5)
+ w = 250+5;
+ wid = w;
+ }
+ destroy(title);
+ er.min.x = 0;
+ er.min.y = 0;
+ er.max.x = wid-5;
+ er.max.y = 0;
+ title = textelement(e, titletext, er, ETitle);
+ if(title == H)
+ goto Errtitle;
+ }
+ if(wid <= 0)
+ wid = Dx(title->e.r)+5;
+ }
+
+ h = heapz(TCompound);
+ pc = H2D(PCompound*, h);
+ c = &pc->c;
+ c->title = (Prefab_Element*)title;
+ c->contents = (Prefab_Element*)text;
+ /* now can just destroy c to clean up */
+
+ r.min = DPOINT(p);
+ r.max.x = r.min.x+wid;
+ r.max.y = p.y+2+1 + 1+1;
+ if(title != H)
+ r.max.y += title->nkids*e->style->titlefont->height+1;
+ if(text != H)
+ r.max.y += Dy(text->e.r);
+
+ er = edgerect(e, DPOINT(p), &r);
+
+ R2R(t, er);
+ disp = allocwindow(screen, t, Refbackup /*refreshcompound*/, DWhite);
+ if(disp == nil){
+ Err:
+ destroy(c);
+ gchalt--;
+ return H;
+ }
+ if((ddisp=mkdrawimage(disp, e->screen, e->screen->display, nil)) == H){
+ freeimage(disp);
+ goto Err;
+ }
+
+ lr = r;
+ if(title != H){
+ pt.x = r.min.x+3;
+ pt.y = r.min.y+3;
+ translateelement(&title->e, pt);
+ lr.min.y = title->e.r.max.y+1;
+ }
+
+ if(text != H)
+ translateelement((Prefab_Element*)text, subpt(IPOINT(lr.min), IPOINT(text->e.r.min)));
+
+ c->r = r;
+ c->environ = e;
+ c->image = ddisp;
+ D2H(e)->ref++;
+ pc->display = screen->display;
+ gchalt--;
+ return pc;
+
+}
+
+PCompound*
+textbox(Prefab_Environ *e, Draw_Rect rr, String *titletext, String *texttext)
+{
+ PCompound *pc;
+ List *l;
+
+ l = listoflayout(e->style, texttext, EText);
+ pc = layoutbox(e, rr, titletext, l);
+ free(l);
+ return pc;
+}
diff --git a/libprefab/textelement.c b/libprefab/textelement.c
new file mode 100644
index 00000000..598b688c
--- /dev/null
+++ b/libprefab/textelement.c
@@ -0,0 +1,488 @@
+#include <lib9.h>
+#include <draw.h>
+#include <interp.h>
+#include <isa.h>
+#include "../libinterp/runt.h"
+#include <drawif.h>
+#include <prefab.h>
+#include <kernel.h>
+
+typedef struct State State;
+
+struct State
+{
+ Prefab_Environ *env;
+ List *list;
+ char word[Maxchars+UTFmax];
+ char *s;
+ char *pending;
+ Draw_Font *font;
+ Draw_Image *color;
+ Draw_Image *icon;
+ Draw_Image *mask;
+ String *tag;
+ Point p;
+ int mainkind;
+ int kind;
+ int wid;
+ int newelem;
+ int ascent;
+ int descent;
+};
+
+static
+char*
+advword(char *s, char *word)
+{
+ char *e;
+ int w;
+ Rune r;
+
+ e = s+Maxchars-1;
+ switch(*word++ = *s){
+ case '\t': /* BUG: what to do about tabs? */
+ strcpy(word-1, " ");
+ return s+1;
+ case '\n':
+ case ' ':
+ *word = 0;
+ return s+1;
+ case '\0':
+ return s;
+ }
+ s++;
+ while(s<e && *s && *s!=' ' && *s!='\t' && *s!='\n'){
+ if(*(uchar*)s < Runeself)
+ *word++ = *s++;
+ else{
+ w = chartorune(&r, s);
+ memmove(word, s, w);
+ word += w;
+ s += w;
+ }
+ }
+ *word = 0;
+ return s;
+}
+
+static
+int
+ismore(State *state)
+{
+ Prefab_Style *style;
+ Prefab_Layout *lay;
+ int text, icon;
+
+ state->newelem = 0;
+ if(state->kind==EIcon || (state->s && state->s[0]) || state->pending)
+ return 1;
+ if(state->list == H)
+ return 0;
+ lay = (Prefab_Layout*)state->list->data;
+ text = (lay->text!=H && lay->text->len != 0);
+ icon = (lay->icon!=H && lay->mask!=H);
+ if(!text && !icon)
+ return 0;
+ state->newelem = 1;
+ state->s = string2c(lay->text);
+ state->font = lay->font;
+ state->color = lay->color;
+ state->icon = lay->icon;
+ state->mask = lay->mask;
+ state->tag = lay->tag;
+ style = state->env->style;
+ if(icon) /* has precedence; if lay->icon is set, we ignore the text */
+ state->kind = EIcon;
+ else{
+ if(state->mainkind == ETitle){
+ if(state->font == H)
+ state->font = style->titlefont;
+ if(state->color == H)
+ state->color = style->titlecolor;
+ }else{
+ if(state->font == H)
+ state->font = style->textfont;
+ if(state->color == H)
+ state->color = style->textcolor;
+ }
+ state->kind = state->mainkind;
+ }
+ state->list = state->list->tail;
+ return 1;
+}
+
+PElement*
+growtext(PElement *pline, State *state, char *w, int minx, int maxx)
+{
+ String *s;
+ PElement *pe, *plist;
+ Prefab_Element *e;
+ List *atom;
+ Point size;
+ Image *image;
+
+ if(state->newelem || pline==H) {
+ pe = mkelement(state->env, state->kind);
+ e = &pe->e;
+ e->r.min.x = minx;
+ if(state->kind == EIcon){
+ e->image = state->icon;
+ D2H(e->image)->ref++;
+ e->mask = state->mask;
+ D2H(e->mask)->ref++;
+ }else{
+ e->image = state->color;
+ D2H(e->image)->ref++;
+ e->font = state->font;
+ D2H(e->font)->ref++;
+ }
+ e->tag = state->tag;
+ if(e->tag != H)
+ D2H(e->tag)->ref++;
+ if(pline == H)
+ pline = pe;
+ else{
+ if(pline->pkind != EHorizontal){
+ /* promote pline to list encapsulating current contents */
+ atom = prefabwrap(pline);
+ plist = mkelement(state->env, EHorizontal);
+ destroy(pline);
+ /* rest of plist->e.r will be set later */
+ plist->e.r.min.x = state->p.x;
+ plist->drawpt = state->p;
+ plist->e.kids = atom;
+ plist->first = atom;
+ plist->last = atom;
+ plist->vfirst = atom;
+ plist->vlast = atom;
+ pline = plist;
+ }
+ /* add e to line */
+ atom = prefabwrap(e);
+ destroy(e); /* relevant data now in wrapper */
+ e = *(Prefab_Element**)atom->data;
+ pline->last->tail = atom;
+ pline->last = atom;
+ pline->vlast = atom;
+ pline->nkids++;
+ }
+ state->newelem = 0;
+ }else{
+ pe = pline;
+ if(pe->pkind == EHorizontal)
+ pe = *(PElement**)pe->last->data;
+ e = &pe->e;
+ }
+
+ if(state->kind == EIcon){
+ /* guaranteed OK by buildine */
+ image = lookupimage(state->icon);
+ size = iconsize(image);
+ /* put one pixel on each side */
+ e->r.max.x = e->r.min.x+1+size.x+1;
+ pline->e.r.max.x = e->r.max.x;
+ if(state->ascent < size.y)
+ state->ascent = size.y;
+ state->kind = -1; /* consume EIcon from state */
+ return pline;
+ }
+
+ e->r.max.x = maxx;
+ pline->e.r.max.x = maxx;
+ if(*w == '\n') {
+ pline->newline = 1;
+ return pline;
+ }
+
+ s = addstring(e->str, c2string(w, strlen(w)), 0);
+ destroy(e->str);
+ e->str = s;
+
+ if(state->ascent < e->font->ascent)
+ state->ascent = e->font->ascent;
+ if(state->descent < e->font->height-e->font->ascent)
+ state->descent = e->font->height-e->font->ascent;
+ return pline;
+}
+
+PElement*
+buildline(State *state, int *ok)
+{
+ int wordwid, linewid, nb, rwid, x;
+ char tmp[UTFmax+1], *w, *t;
+ PElement *pl, *pe;
+ Rune r;
+ Font *f;
+ List *l;
+ Image *icon;
+ Point size;
+
+ *ok = 1;
+ linewid = 0;
+ pl = H;
+ state->ascent = 0;
+ state->descent = 0;
+ x = state->p.x;
+ while(ismore(state)){
+ f = nil;
+ if(state->kind == EIcon){
+ icon = lookupimage(state->icon);
+ if(icon == nil){
+ Error:
+ destroy(pl);
+ *ok = 0;
+ return H;
+ }
+ size = iconsize(icon);
+ wordwid = 1+size.x+1;
+ }else{
+ if(state->pending == 0){
+ state->s = advword(state->s, state->word);
+ state->pending = state->word;
+ }
+ if(*(state->pending) == '\n'){
+ pl = growtext(pl, state, state->pending, x, x);
+ if(pl == H){
+ *ok = 0;
+ return H;
+ }
+ state->pending = 0;
+ break;
+ }
+ f = lookupfont(state->font);
+ if(f == nil)
+ goto Error;
+ wordwid = stringwidth(f, state->pending);
+ }
+ if(linewid+wordwid<=state->wid){
+ Easy:
+ pl = growtext(pl, state, state->pending, x, x+wordwid);
+ if(pl == H){
+ *ok = 0;
+ return H;
+ }
+ linewid += wordwid;
+ state->pending = 0;
+ x += wordwid;
+ continue;
+ }
+ /* this word doesn't fit on this line */
+ /* if it's white space or an icon, just generate a line break */
+ if(state->word[0]==' ' || state->kind==EIcon){
+ if(linewid == 0) /* it's just too wide; emit it and it'll get clipped */
+ goto Easy;
+ state->pending = 0;
+ break;
+ }
+ /* if word would fit were we to break the line now, do so */
+ if(wordwid <= state->wid)
+ break;
+ /* worst case: bite off the biggest piece that fits */
+ w = state->pending;
+ while(*w){
+ nb = chartorune(&r, w);
+ memmove(tmp, w, nb);
+ tmp[nb] = 0;
+ rwid = stringwidth(f, tmp);
+ if(linewid+rwid > state->wid)
+ break;
+ linewid += rwid;
+ w += nb;
+ }
+ if(w == state->pending){
+ /* first char too wide for remaining space */
+ if(linewid > 0)
+ break;
+ /* remaining space is all we'll get */
+ kwerrstr("can't handle wide word in textelement\n");
+ goto Error;
+ }
+ nb = w-state->pending;
+ t = malloc(nb+1);
+ if(t == nil)
+ goto Error;
+ memmove(t, state->pending, nb);
+ t[nb] = 0;
+ pl = growtext(pl, state, t, x, state->p.x+linewid);
+ free(t);
+ if(pl == H){
+ *ok = 0;
+ return H;
+ }
+ state->pending = w;
+ break;
+ }
+ pl->e.r.min.y = state->p.y;
+ pl->e.r.max.y = state->p.y+state->ascent+state->descent;
+ P2P(pl->drawpt, pl->e.r.min);
+ if(pl->pkind==EHorizontal){
+ for(l=pl->first; l!=H; l=l->tail){
+ pe = *(PElement**)l->data;
+ pe->e.r.min.y = state->p.y;
+ pe->e.r.max.y = state->p.y+state->ascent+state->descent;
+ pe->drawpt.x = pe->e.r.min.x;
+ if(pe->e.kind == EIcon){
+ /* add a pixel on the left; room was left in growtext */
+ pe->drawpt.x += 1;
+ pe->drawpt.y = pe->e.r.min.y+(state->ascent-Dy(pe->e.image->r));
+ }else
+ pe->drawpt.y = pe->e.r.min.y+(state->ascent-pe->e.font->ascent);
+ }
+ }
+ return pl;
+}
+
+PElement*
+layoutelement(Prefab_Environ *env, List *laylist, Draw_Rect rr, enum Elementtype kind)
+{
+ PElement *pline, *plist, *firstpline;
+ List *lines, *atom, *tail;
+ State state;
+ int nlines, linewid, maxwid, wid, trim, maxy, ok;
+ Point p;
+ Rectangle r;
+ Screen *screen;
+
+ nlines = 0;
+ trim = 0;
+ wid = Dx(rr);
+ if(wid < 25){
+ if(wid <= 0)
+ trim = 1;
+ screen = lookupscreen(env->screen);
+ if(screen == nil)
+ return H;
+ wid = Dx(screen->display->image->r)-32;
+ if(wid < 100)
+ wid = 100;
+ }
+ wid -= 3+3; /* three pixels left and right */
+
+ gchalt++;
+ state.env = env;
+ state.list = laylist;
+ state.s = 0;
+ state.pending = 0;
+ state.font = H;
+ state.color = H;
+ state.tag = H;
+ p = IPOINT(rr.min);
+ p.x += 3;
+ state.p = p;
+ state.kind = EText; /* anything but EIcon */
+ state.mainkind = kind;
+ state.wid = wid;
+ lines = H;
+ tail = H;
+ firstpline = H;
+ maxwid = 0;
+ maxy = 0;
+ while(ismore(&state)){
+ pline = buildline(&state, &ok);
+ if(ok == 0){
+ plist = H;
+ goto Return;
+ }
+ if(pline == H)
+ break;
+ linewid = Dx(pline->e.r);
+ if(linewid > maxwid)
+ maxwid = linewid;
+ if(firstpline == H)
+ firstpline = pline;
+ else{
+ atom = prefabwrap(pline);
+ destroy(pline); /* relevant data now in wrapper */
+ pline = *(PElement**)atom->data;
+ if(lines == H){
+ lines = prefabwrap(firstpline);
+ destroy(firstpline);
+ firstpline = 0; /* never used again; this proves it! */
+ tail = lines;
+ }
+ tail->tail = atom;
+ tail = atom;
+ }
+ nlines++;
+ state.p.y = pline->e.r.max.y;
+ if(maxy==0 || state.p.y<=rr.max.y)
+ maxy = state.p.y;
+ }
+ if(trim == 0)
+ maxwid = wid;
+ if(nlines == 0){
+ plist = H;
+ goto Return;
+ }
+ if(nlines == 1){
+ if(trim == 0){ /* restore clipping around element */
+ firstpline->e.r.min.x = rr.min.x;
+ firstpline->e.r.max.x = rr.min.x+3+maxwid+3;
+ }
+ plist = firstpline;
+ goto Return;
+ }
+ plist = mkelement(env, EVertical);
+ plist->e.r.min.x = rr.min.x;
+ plist->e.r.min.y = p.y;
+ plist->e.r.max.x = rr.min.x+3+maxwid+3;
+ plist->e.r.max.y = (*(Prefab_Element**)tail->data)->r.max.y;
+ plist->drawpt = p;
+ plist->e.kids = lines;
+ plist->first = lines;
+ plist->last = tail;
+ plist->vfirst = lines;
+ plist->vlast = tail;
+ plist->nkids = nlines;
+ /* if asked for a fixed size and list is too long, clip */
+ if(Dy(rr)>0 && rr.max.y<plist->e.r.max.y){
+ R2R(r, plist->e.r);
+ r.max.y = maxy;
+ clipelement(&plist->e, r);
+ }
+
+Return:
+ gchalt--;
+ return plist;
+}
+
+/*
+ * Create List with one Layout in it, using malloc instead of heap to
+ * keep it out of the eyes of the garbage collector
+ */
+List*
+listoflayout(Prefab_Style *style, String *text, int kind)
+{
+ List *listp;
+ Prefab_Layout *layp;
+
+ listp = malloc(sizeof(List) + TLayout->size);
+ if(listp == nil)
+ return H;
+ listp->tail = H;
+ layp = (Prefab_Layout*)listp->data;
+ if(kind == EText){
+ layp->font = style->textfont;
+ layp->color = style->textcolor;
+ }else{
+ layp->font = style->titlefont;
+ layp->color = style->titlecolor;
+ }
+ layp->text = text;
+ layp->icon = H;
+ layp->mask = H;
+ layp->tag = H;
+ return listp;
+}
+
+PElement*
+textelement(Prefab_Environ *env, String *str, Draw_Rect rr, enum Elementtype kind)
+{
+ PElement *pe;
+ List *l;
+
+ l = listoflayout(env->style, str, kind);
+ pe = layoutelement(env, l, rr, kind);
+ free(l);
+ return pe;
+}