/* * Copyright (c) 2003-2007 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "box.h" #include "window.h" #include "primitive.h" #include "text.h" AG_Box * AG_BoxNew(void *parent, enum ag_box_type type, Uint flags) { AG_Box *box; box = Malloc(sizeof(AG_Box)); AG_ObjectInit(box, &agBoxClass); box->type = type; box->flags |= flags; if (flags & AG_BOX_HFILL) { AG_ExpandHoriz(box); } if (flags & AG_BOX_VFILL) { AG_ExpandVert(box); } AG_ObjectAttach(parent, box); return (box); } /* Set the label text. */ void AG_BoxSetLabel(AG_Box *box, const char *fmt, ...) { va_list ap; AG_ObjectLock(box); va_start(ap, fmt); Free(box->caption); Vasprintf(&box->caption, fmt, ap); va_end(ap); if (box->sCaption != -1) { AG_WidgetUnmapSurface(box, box->sCaption); box->sCaption = -1; } AG_ObjectUnlock(box); } static void Init(void *obj) { AG_Box *box = obj; box->flags = 0; box->type = AG_BOX_VERT; box->depth = -1; box->padding = 4; box->spacing = 2; box->caption = NULL; box->sCaption = -1; box->rCaption = AG_RECT(5, agTextFontHeight/2, 0, 0); } static void Destroy(void *obj) { AG_Box *box = obj; Free(box->caption); } static void DrawCaption(AG_Box *box) { int y = agTextFontHeight/2; if (box->sCaption == -1) { AG_Surface *sText; sText = AG_TextRender(box->caption); box->sCaption = AG_WidgetMapSurface(box, sText); } STYLE(box)->BoxFrame(box, AG_RECT(0, y, WIDTH(box), HEIGHT(box)-y), box->depth); AG_WidgetBlitSurface(box, box->sCaption, box->rCaption.x, box->rCaption.y); } static void Draw(void *obj) { AG_Box *box = obj; AG_Widget *chld; if (box->caption != NULL) { DrawCaption(box); } else { if (box->flags & AG_BOX_FRAME) { STYLE(box)->BoxFrame(box, AG_RECT(0, 0, WIDTH(box), HEIGHT(box)), box->depth); } } WIDGET_FOREACH_CHILD(chld, box) AG_WidgetDraw(chld); } static int CountChildWidgets(AG_Box *box, int *totFixed) { AG_Widget *chld; AG_SizeReq r; Uint count = 0; int fixed = 0; OBJECT_FOREACH_CHILD(chld, box, ag_widget) { AG_WidgetSizeReq(chld, &r); switch (box->type) { case AG_BOX_HORIZ: if ((chld->flags & AG_WIDGET_HFILL) == 0) fixed += r.w; break; case AG_BOX_VERT: if ((chld->flags & AG_WIDGET_VFILL) == 0) fixed += r.h; break; } count++; fixed += box->spacing; } if (count > 0 && fixed >= box->spacing) { fixed -= box->spacing; } *totFixed = fixed; return (count); } static void SizeRequest(void *obj, AG_SizeReq *r) { AG_Box *box = obj; AG_Widget *chld; AG_SizeReq rChld; int wMax = 0, hMax = 0; int nWidgets, totArea; nWidgets = CountChildWidgets(box, &totArea); r->w = box->padding*2; r->h = box->padding*2; OBJECT_FOREACH_CHILD(chld, box, ag_widget) { AG_WidgetSizeReq(chld, &rChld); if (rChld.w > wMax) { wMax = rChld.w; } if (rChld.h > hMax) { hMax = rChld.h; } switch (box->type) { case AG_BOX_HORIZ: r->h = MAX(r->h, hMax + box->padding*2); r->w += rChld.w + box->spacing; break; case AG_BOX_VERT: r->w = MAX(r->w, wMax + box->padding*2); r->h += rChld.h + box->spacing; break; } } if (nWidgets > 0) { switch (box->type) { case AG_BOX_HORIZ: if (r->w >= box->spacing) r->w -= box->spacing; break; case AG_BOX_VERT: if (r->h >= box->spacing) r->h -= box->spacing; break; } } } static int SizeAllocateHomogenous(AG_Box *box, const AG_SizeAlloc *a, int nWidgets) { int wSize, totUsed = 0, avail; AG_Widget *chld, *chldLast = NULL; AG_SizeAlloc aChld; avail = ((box->type == AG_BOX_HORIZ) ? a->w : a->h); avail -= box->padding*2; wSize = (avail - nWidgets - 1) / nWidgets; aChld.x = box->padding; aChld.y = box->padding; OBJECT_FOREACH_CHILD(chld, box, ag_widget) { aChld.w = (box->type==AG_BOX_HORIZ) ? wSize : (a->w - box->padding*2); aChld.h = (box->type==AG_BOX_VERT) ? wSize : (a->h - box->padding*2); if (AG_WidgetSizeAlloc(chld, &aChld) == -1) continue; if (OBJECT(chld) == TAILQ_LAST(&OBJECT(box)->children,ag_objectq)) { chldLast = chld; } else { if (box->type == AG_BOX_HORIZ) { aChld.x += aChld.w + 1; } else { aChld.y += aChld.h + 1; } } totUsed += ((box->type == AG_BOX_HORIZ) ? aChld.w : aChld.h)+1; } /* Compensate for rounding error due to division. */ if (chldLast != NULL && totUsed < avail) { switch (box->type) { case AG_BOX_VERT: aChld.h += avail - totUsed; AG_WidgetSizeAlloc(chldLast, &aChld); break; case AG_BOX_HORIZ: aChld.w += avail - totUsed; AG_WidgetSizeAlloc(chldLast, &aChld); break; } } return (0); } static int SizeAllocate(void *obj, const AG_SizeAlloc *a) { AG_Box *box = obj; AG_Widget *chld; AG_SizeReq rChld; AG_SizeAlloc aChld; int nWidgets, totFixed; int wAvail, hAvail; int hLabel = (box->caption != NULL) ? agTextFontHeight/2 : 0; box->rCaption.w = a->w; box->rCaption.h = a->h - box->rCaption.y; if ((nWidgets = CountChildWidgets(box, &totFixed)) == 0) { return (0); } if (box->flags & AG_BOX_HOMOGENOUS) { if (SizeAllocateHomogenous(box, a, nWidgets) == -1) { return (-1); } return (0); } wAvail = a->w - box->padding*2; hAvail = a->h - box->padding*2 - hLabel; aChld.x = box->padding; aChld.y = box->padding + hLabel; OBJECT_FOREACH_CHILD(chld, box, ag_widget) { AG_WidgetSizeReq(chld, &rChld); switch (box->type) { case AG_BOX_HORIZ: aChld.w = (chld->flags & AG_WIDGET_HFILL) ? (wAvail - totFixed) : MIN(rChld.w, wAvail); aChld.h = (chld->flags & AG_WIDGET_VFILL) ? hAvail : MIN(hAvail, rChld.h); AG_WidgetSizeAlloc(chld, &aChld); aChld.x += aChld.w + box->spacing; if (aChld.x > a->w+box->padding*2) { chld->flags |= AG_WIDGET_UNDERSIZE; } else { chld->flags &= ~(AG_WIDGET_UNDERSIZE); } break; case AG_BOX_VERT: aChld.w = (chld->flags & AG_WIDGET_HFILL) ? wAvail : MIN(wAvail, rChld.w); aChld.h = (chld->flags & AG_WIDGET_VFILL) ? (hAvail - totFixed) : MIN(rChld.h, hAvail); AG_WidgetSizeAlloc(chld, &aChld); aChld.y += aChld.h + box->spacing; if (aChld.y > a->h+box->padding*2) { chld->flags |= AG_WIDGET_UNDERSIZE; } else { chld->flags &= ~(AG_WIDGET_UNDERSIZE); } break; } } return (0); } void AG_BoxSetHomogenous(AG_Box *box, int enable) { AG_ObjectLock(box); AG_SETFLAGS(box->flags, AG_BOX_HOMOGENOUS, enable); AG_ObjectUnlock(box); } void AG_BoxSetPadding(AG_Box *box, int padding) { AG_ObjectLock(box); box->padding = padding; AG_ObjectUnlock(box); } void AG_BoxSetSpacing(AG_Box *box, int spacing) { AG_ObjectLock(box); box->spacing = spacing; AG_ObjectUnlock(box); } void AG_BoxSetDepth(AG_Box *box, int depth) { AG_ObjectLock(box); box->depth = depth; AG_ObjectUnlock(box); } void AG_BoxSetType(AG_Box *box, enum ag_box_type type) { AG_SizeAlloc a; AG_ObjectLock(box); box->type = type; a.x = WIDGET(box)->x; a.y = WIDGET(box)->y; a.w = WIDGET(box)->w; a.h = WIDGET(box)->h; SizeAllocate(box, &a); AG_ObjectUnlock(box); } AG_WidgetClass agBoxClass = { { "Agar(Widget:Box)", sizeof(AG_Box), { 0,0 }, Init, NULL, /* free */ Destroy, NULL, /* load */ NULL, /* save */ NULL /* edit */ }, Draw, SizeRequest, SizeAllocate };