diff options
Diffstat (limited to 'libprefab')
| -rw-r--r-- | libprefab/NOTICE | 25 | ||||
| -rw-r--r-- | libprefab/box.c | 80 | ||||
| -rw-r--r-- | libprefab/compound.c | 193 | ||||
| -rw-r--r-- | libprefab/element.c | 723 | ||||
| -rw-r--r-- | libprefab/elistelement.c | 113 | ||||
| -rw-r--r-- | libprefab/iconbox.c | 83 | ||||
| -rw-r--r-- | libprefab/iconelement.c | 50 | ||||
| -rw-r--r-- | libprefab/mkfile | 21 | ||||
| -rw-r--r-- | libprefab/textbox.c | 138 | ||||
| -rw-r--r-- | libprefab/textelement.c | 488 |
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; +} |
