/*
* Copyright (c) 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 "numerical.h"
#include "primitive.h"
#include
static void UnitSelected(AG_Event *);
AG_Numerical *
AG_NumericalNew(void *parent, Uint flags, const char *unit, const char *label)
{
AG_Numerical *num;
num = Malloc(sizeof(AG_Numerical));
AG_ObjectInit(num, &agNumericalClass);
if ((flags & AG_NUMERICAL_NO_HFILL) == 0) { AG_ExpandHoriz(num); }
if (flags & AG_NUMERICAL_VFILL) { AG_ExpandVert(num); }
if (label != NULL) {
AG_TextboxSetLabel(num->input, "%s", label);
}
if (unit != NULL) {
num->units = AG_UComboNew(num, 0);
AG_SetEvent(num->units, "ucombo-selected",
UnitSelected, "%p", num);
AG_NumericalSetUnitSystem(num, unit);
AG_WidgetSetFocusable(num->units, 0);
}
AG_ObjectAttach(parent, num);
return (num);
}
AG_Numerical *
AG_NumericalNewDbl(void *parent, Uint flags, const char *unit,
const char *label, double *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindDouble(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewDblR(void *parent, Uint flags, const char *unit,
const char *label, double *v, double min, double max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindDouble(num, "value", v);
AG_NumericalSetMinDbl(num, min);
AG_NumericalSetMaxDbl(num, max);
return (num);
}
AG_Numerical *
AG_NumericalNewFlt(void *parent, Uint flags, const char *unit,
const char *label, float *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindFloat(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewFltR(void *parent, Uint flags, const char *unit,
const char *label, float *v, float min, float max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindFloat(num, "value", v);
AG_NumericalSetMinFlt(num, min);
AG_NumericalSetMaxFlt(num, max);
return (num);
}
AG_Numerical *
AG_NumericalNewInt(void *parent, Uint flags, const char *unit,
const char *label, int *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindInt(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewIntR(void *parent, Uint flags, const char *unit,
const char *label, int *v, int min, int max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindInt(num, "value", v);
AG_NumericalSetMin(num, (double)min); /* XXX */
AG_NumericalSetMax(num, (double)max);
return (num);
}
AG_Numerical *
AG_NumericalNewUint(void *parent, Uint flags, const char *unit,
const char *label, Uint *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewUintR(void *parent, Uint flags, const char *unit,
const char *label, Uint *v, Uint min, Uint max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewUint8(void *parent, Uint flags, const char *unit,
const char *label, Uint8 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint8(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewUint8R(void *parent, Uint flags, const char *unit,
const char *label, Uint8 *v, Uint8 min, Uint8 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint8(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewSint8(void *parent, Uint flags, const char *unit,
const char *label, Sint8 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint8(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewSint8R(void *parent, Uint flags, const char *unit,
const char *label, Sint8 *v, Sint8 min, Sint8 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint8(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewUint16(void *parent, Uint flags, const char *unit,
const char *label, Uint16 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint16(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewUint16R(void *parent, Uint flags, const char *unit,
const char *label, Uint16 *v, Uint16 min, Uint16 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint16(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewSint16(void *parent, Uint flags, const char *unit,
const char *label, Sint16 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint16(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewSint16R(void *parent, Uint flags, const char *unit,
const char *label, Sint16 *v, Sint16 min, Sint16 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint16(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewUint32(void *parent, Uint flags, const char *unit,
const char *label, Uint32 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint32(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewUint32R(void *parent, Uint flags, const char *unit,
const char *label, Uint32 *v, Uint32 min, Uint32 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindUint32(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
AG_Numerical *
AG_NumericalNewSint32(void *parent, Uint flags, const char *unit,
const char *label, Sint32 *v)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint32(num, "value", v);
return (num);
}
AG_Numerical *
AG_NumericalNewSint32R(void *parent, Uint flags, const char *unit,
const char *label, Sint32 *v, Sint32 min, Sint32 max)
{
AG_Numerical *num;
num = AG_NumericalNew(parent, flags, unit, label);
AG_BindSint32(num, "value", v);
AG_NumericalSetMinInt(num, (int)min);
AG_NumericalSetMaxInt(num, (int)max);
return (num);
}
static void
Bound(AG_Event *event)
{
AG_Numerical *num = AG_SELF();
AG_Variable *binding = AG_PTR(1);
if (strcmp(binding->name, "value") == 0) {
switch (AG_VARIABLE_TYPE(binding)) {
case AG_VARIABLE_DOUBLE:
num->min = -AG_DBL_MAX+1;
num->max = AG_DBL_MAX-1;
AG_TextboxSetFltOnly(num->input, 1);
break;
case AG_VARIABLE_FLOAT:
num->min = -AG_FLT_MAX+1;
num->max = AG_FLT_MAX-1;
AG_TextboxSetFltOnly(num->input, 1);
break;
case AG_VARIABLE_INT:
num->min = AG_INT_MIN+1;
num->max = AG_INT_MAX-1;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_UINT:
num->min = 0;
num->max = AG_UINT_MAX-1;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_UINT8:
num->min = 0;
num->max = 0xffU;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_SINT8:
num->min = -0x7f+1;
num->max = 0x7f-1;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_UINT16:
num->min = 0;
num->max = 0xffffU;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_SINT16:
num->min = -0x7fff+1;
num->max = 0x7fff-1;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_UINT32:
num->min = 0;
num->max = 0xffffffffU;
AG_TextboxSetIntOnly(num->input, 1);
break;
case AG_VARIABLE_SINT32:
num->min = -0x7fffffff+1;
num->max = 0x7fffffff-1;
AG_TextboxSetIntOnly(num->input, 1);
break;
default:
break;
}
}
}
/* Update the textbox contents from the binding value. */
static void
UpdateTextbox(AG_Numerical *num)
{
AG_Variable *valueb;
void *value;
valueb = AG_GetVariable(num, "value", &value);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
AG_TextboxPrintf(num->input, num->format,
AG_Base2Unit(*(double *)value, num->unit));
break;
case AG_VARIABLE_FLOAT:
AG_TextboxPrintf(num->input, num->format,
AG_Base2Unit(*(float *)value, num->unit));
break;
case AG_VARIABLE_INT:
AG_TextboxPrintf(num->input, "%d", *(int *)value);
break;
case AG_VARIABLE_UINT:
AG_TextboxPrintf(num->input, "%u", *(Uint *)value);
break;
case AG_VARIABLE_UINT8:
AG_TextboxPrintf(num->input, "%u", *(Uint8 *)value);
break;
case AG_VARIABLE_SINT8:
AG_TextboxPrintf(num->input, "%d", *(Sint8 *)value);
break;
case AG_VARIABLE_UINT16:
AG_TextboxPrintf(num->input, "%u", *(Uint16 *)value);
break;
case AG_VARIABLE_SINT16:
AG_TextboxPrintf(num->input, "%d", *(Sint16 *)value);
break;
case AG_VARIABLE_UINT32:
AG_TextboxPrintf(num->input, "%u", *(Uint32 *)value);
break;
case AG_VARIABLE_SINT32:
AG_TextboxPrintf(num->input, "%d", *(Sint32 *)value);
break;
default:
break;
}
AG_UnlockVariable(valueb);
}
static void
KeyDown(AG_Event *event)
{
AG_Numerical *num = AG_SELF();
int keysym = AG_INT(1);
switch (keysym) {
case SDLK_UP:
AG_NumericalAddValue(num, num->inc);
break;
case SDLK_DOWN:
AG_NumericalAddValue(num, -num->inc);
break;
}
}
static void
GainedFocus(AG_Event *event)
{
AG_Numerical *num = AG_SELF();
UpdateTextbox(num);
}
/* Update the numerical value from the textbox. */
static void
UpdateFromText(AG_Event *event)
{
AG_Numerical *num = AG_PTR(1);
int unfocus = AG_INT(2);
AG_Variable *stringb, *valueb;
char *s;
void *value;
valueb = AG_GetVariable(num, "value", &value);
stringb = AG_GetVariable(num->input->ed, "string", &s);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
case AG_VARIABLE_FLOAT:
AG_NumericalSetValue(num,
AG_Unit2Base(strtod(s, NULL), num->unit));
break;
case AG_VARIABLE_INT:
case AG_VARIABLE_UINT:
case AG_VARIABLE_UINT8:
case AG_VARIABLE_SINT8:
case AG_VARIABLE_UINT16:
case AG_VARIABLE_SINT16:
case AG_VARIABLE_UINT32:
case AG_VARIABLE_SINT32:
AG_NumericalSetValue(num, (double)strtol(s, NULL, 10));
break;
default:
break;
}
AG_UnlockVariable(stringb);
AG_UnlockVariable(valueb);
if (unfocus) {
AG_WidgetUnfocus(num->input);
}
AG_PostEvent(NULL, num, "numerical-return", NULL);
}
static void
IncrementValue(AG_Event *event)
{
AG_Numerical *num = AG_PTR(1);
AG_ObjectLock(num);
AG_NumericalAddValue(num, num->inc);
AG_ObjectUnlock(num);
}
static void
DecrementValue(AG_Event *event)
{
AG_Numerical *num = AG_PTR(1);
AG_ObjectLock(num);
AG_NumericalAddValue(num, -num->inc);
AG_ObjectUnlock(num);
}
static void
UpdateUnitSelector(AG_Numerical *num)
{
AG_ButtonText(num->units->button, "%s", AG_UnitAbbr(num->unit));
}
static void
UnitSelected(AG_Event *event)
{
AG_Numerical *num = AG_PTR(1);
AG_TlistItem *ti = AG_PTR(2);
AG_ObjectLock(num);
num->unit = (const AG_Unit *)ti->p1;
UpdateUnitSelector(num);
UpdateTextbox(num);
AG_ObjectUnlock(num);
}
int
AG_NumericalSetUnitSystem(AG_Numerical *num, const char *unit_key)
{
const AG_Unit *unit = NULL;
const AG_Unit *ugroup = NULL;
int found = 0, i;
int w, h, nUnits = 0;
AG_ObjectLock(num);
for (i = 0; i < agnUnitGroups; i++) {
ugroup = agUnitGroups[i];
for (unit = &ugroup[0]; unit->key != NULL; unit++) {
if (strcmp(unit->key, unit_key) == 0) {
found++;
break;
}
}
if (found)
break;
}
if (!found) {
AG_SetError(_("No such unit: %s"), unit_key);
AG_ObjectUnlock(num);
return (-1);
}
num->unit = unit;
UpdateUnitSelector(num);
num->wUnitSel = 0;
num->hUnitSel = 0;
num->wPreUnit = 0;
AG_ObjectLock(num->units->list);
AG_TlistDeselectAll(num->units->list);
AG_TlistBegin(num->units->list);
for (unit = &ugroup[0]; unit->key != NULL; unit++) {
AG_TlistItem *it;
AG_TextSize(AG_UnitAbbr(unit), &w, &h);
if (w > num->wUnitSel) { num->wUnitSel = w; }
if (h > num->hUnitSel) { num->hUnitSel = h; }
AG_TextSize(unit->name, &w, NULL);
if (w > num->wPreUnit) { num->wPreUnit = w; }
it = AG_TlistAddPtr(num->units->list, NULL, _(unit->name),
(void *)unit);
if (unit == num->unit)
it->selected++;
nUnits++;
}
AG_TlistEnd(num->units->list);
AG_ObjectUnlock(num->units->list);
if (num->wPreUnit > 0) { num->wPreUnit += 8; }
AG_UComboSizeHintPixels(num->units, num->wPreUnit,
nUnits<6 ? (nUnits + 1) : 6);
AG_ObjectUnlock(num);
AG_WindowUpdate(AG_ParentWindow(num));
return (0);
}
static void
Init(void *obj)
{
AG_Numerical *num = obj;
WIDGET(num)->flags |= AG_WIDGET_FOCUSABLE;
AG_BindDouble(num, "value", &num->value);
AG_BindDouble(num, "min", &num->min);
AG_BindDouble(num, "max", &num->max);
num->inc = 1.0;
num->value = 0.0;
num->input = AG_TextboxNew(num, 0, NULL);
num->writeable = 1;
num->wUnitSel = 0;
num->hUnitSel = 0;
Strlcpy(num->format, "%.02f", sizeof(num->format));
AG_TextboxSizeHint(num->input, "8888.88");
num->unit = AG_FindUnit("identity");
num->units = NULL;
num->incbu = AG_ButtonNew(num, AG_BUTTON_REPEAT, _("+"));
AG_ButtonSetPadding(num->incbu, 1,1,1,1);
AG_WidgetSetFocusable(num->incbu, 0);
num->decbu = AG_ButtonNew(num, AG_BUTTON_REPEAT, _("-"));
AG_ButtonSetPadding(num->decbu, 1,1,1,1);
AG_WidgetSetFocusable(num->decbu, 0);
AG_SetEvent(num, "window-keydown", KeyDown, NULL);
AG_SetEvent(num, "widget-gainfocus", GainedFocus, NULL);
AG_SetEvent(num, "bound", Bound, NULL);
AG_SetEvent(num->incbu, "button-pushed", IncrementValue, "%p", num);
AG_SetEvent(num->decbu, "button-pushed", DecrementValue, "%p", num);
AG_SetEvent(num->input, "textbox-return",
UpdateFromText, "%p,%i", num, 1);
AG_SetEvent(num->input, "textbox-changed",
UpdateFromText, "%p,%i", num, 0);
AG_WidgetForwardFocus(num, num->input);
}
void
AG_NumericalSizeHint(AG_Numerical *num, const char *text)
{
AG_ObjectLock(num);
AG_TextboxSizeHint(num->input, text);
AG_ObjectUnlock(num);
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
AG_Numerical *num = obj;
AG_SizeReq rChld, rInc, rDec;
AG_WidgetSizeReq(num->input, &rChld);
r->w = rChld.w + num->wUnitSel + 4;
r->h = MAX(rChld.h, num->hUnitSel);
AG_WidgetSizeReq(num->incbu, &rInc);
AG_WidgetSizeReq(num->decbu, &rDec);
r->w += MAX(rInc.w, rDec.w) + 4;
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_Numerical *num = obj;
AG_SizeAlloc aChld;
int szBtn = a->h/2;
int wUnitSel = num->wUnitSel + 4;
int hUnitSel = num->hUnitSel;
if (a->h < 4 || a->w < szBtn+4)
return (-1);
if (num->units != NULL) {
if (wUnitSel > a->w - szBtn-4) {
wUnitSel = a->w - szBtn-4;
}
if (hUnitSel > a->h) {
hUnitSel = a->h;
}
} else {
wUnitSel = 0;
hUnitSel = 0;
}
/* Size input textbox */
aChld.x = 0;
aChld.y = 0;
aChld.w = a->w - wUnitSel - szBtn - 4;
aChld.h = a->h;
AG_WidgetSizeAlloc(num->input, &aChld);
aChld.x += aChld.w + 2;
/* Size unit selector */
if (num->units != NULL) {
aChld.w = wUnitSel;
aChld.h = a->h;
AG_WidgetSizeAlloc(num->units, &aChld);
aChld.x += aChld.w + 2;
}
/* Size increment buttons */
aChld.w = szBtn;
aChld.h = szBtn;
AG_WidgetSizeAlloc(num->incbu, &aChld);
aChld.y += aChld.h;
if (aChld.h*2 < a->h) {
aChld.h++;
}
AG_WidgetSizeAlloc(num->decbu, &aChld);
return (0);
}
static void
Draw(void *obj)
{
AG_Numerical *num = obj;
AG_WidgetDraw(num->input);
if (num->units != NULL) { AG_WidgetDraw(num->units); }
AG_WidgetDraw(num->incbu);
AG_WidgetDraw(num->decbu);
if (!AG_WidgetFocused(num->input))
UpdateTextbox(num);
}
#define ADD_CONVERTED(TYPE) do { \
n = (double)(*(TYPE *)value); \
if ((n+inc) < *min) { n = *min; } \
else if ((n+inc) > *max) { n = *(max); } \
else { n += inc; } \
*(TYPE *)value = (TYPE)n; \
} while (0)
#define ADD_REAL(TYPE) do { \
n = AG_Base2Unit(*(TYPE *)value, num->unit); \
if ((n+inc) < *min) { n = *min; } \
else if ((n+inc) > *max) { n = *(max); } \
else { n += inc; } \
*(TYPE *)value = AG_Unit2Base(n, num->unit); \
} while (0)
void
AG_NumericalAddValue(AG_Numerical *num, double inc)
{
AG_Variable *valueb, *minb, *maxb;
void *value;
double n;
double *min, *max;
AG_ObjectLock(num);
valueb = AG_GetVariable(num, "value", &value);
minb = AG_GetVariable(num, "min", &min);
maxb = AG_GetVariable(num, "max", &max);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE: ADD_REAL(double); break;
case AG_VARIABLE_FLOAT: ADD_REAL(float); break;
case AG_VARIABLE_INT: ADD_CONVERTED(int); break;
case AG_VARIABLE_UINT: ADD_CONVERTED(Uint); break;
case AG_VARIABLE_UINT8: ADD_CONVERTED(Uint8); break;
case AG_VARIABLE_SINT8: ADD_CONVERTED(Sint8); break;
case AG_VARIABLE_UINT16: ADD_CONVERTED(Uint16); break;
case AG_VARIABLE_SINT16: ADD_CONVERTED(Sint16); break;
case AG_VARIABLE_UINT32: ADD_CONVERTED(Uint32); break;
case AG_VARIABLE_SINT32: ADD_CONVERTED(Sint32); break;
default: break;
}
AG_PostEvent(NULL, num, "numerical-changed", NULL);
AG_UnlockVariable(valueb);
AG_UnlockVariable(minb);
AG_UnlockVariable(maxb);
UpdateTextbox(num);
AG_ObjectUnlock(num);
}
#undef ADD_REAL
#undef ADD_CONVERTED
#define ASSIGN_VALUE(TYPE) *(TYPE *)value = nvalue < *min ? *min : \
nvalue > *max ? *max : nvalue
#define CONV_VALUE(TYPE) \
*(TYPE *)value = nvalue < *min ? (TYPE)(*min) : \
nvalue > *max ? (TYPE)(*max) : (TYPE)nvalue
/* TODO int types directly */
void
AG_NumericalSetValue(AG_Numerical *num, double nvalue)
{
AG_Variable *valueb, *minb, *maxb;
void *value;
double *min, *max;
AG_ObjectLock(num);
valueb = AG_GetVariable(num, "value", &value);
minb = AG_GetVariable(num, "min", &min);
maxb = AG_GetVariable(num, "max", &max);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE: ASSIGN_VALUE(double); break;
case AG_VARIABLE_FLOAT: ASSIGN_VALUE(float); break;
case AG_VARIABLE_INT: CONV_VALUE(int); break;
case AG_VARIABLE_UINT: CONV_VALUE(Uint); break;
case AG_VARIABLE_UINT8: CONV_VALUE(Uint8); break;
case AG_VARIABLE_SINT8: CONV_VALUE(Sint8); break;
case AG_VARIABLE_UINT16: CONV_VALUE(Uint16); break;
case AG_VARIABLE_SINT16: CONV_VALUE(Sint16); break;
case AG_VARIABLE_UINT32: CONV_VALUE(Uint32); break;
case AG_VARIABLE_SINT32: CONV_VALUE(Sint32); break;
default: break;
}
AG_PostEvent(NULL, num, "numerical-changed", NULL);
AG_UnlockVariable(valueb);
AG_UnlockVariable(minb);
AG_UnlockVariable(maxb);
AG_ObjectUnlock(num);
}
#undef ASSIGN_VALUE
#undef CONV_VALUE
void
AG_NumericalSetMin(AG_Numerical *num, double nmin)
{
AG_Variable *minb;
void *min;
/* TODO allow integer min/max bindings */
AG_ObjectLock(num);
minb = AG_GetVariable(num, "min", &min);
switch (AG_VARIABLE_TYPE(minb)) {
case AG_VARIABLE_DOUBLE:
*(double *)min = nmin;
break;
case AG_VARIABLE_FLOAT:
*(float *)min = (float)nmin;
break;
default:
break;
}
AG_UnlockVariable(minb);
AG_ObjectUnlock(num);
}
void
AG_NumericalSetMax(AG_Numerical *num, double nmax)
{
AG_Variable *maxb;
void *max;
/* TODO allow integer min/max bindings */
AG_ObjectLock(num);
maxb = AG_GetVariable(num, "max", &max);
switch (AG_VARIABLE_TYPE(maxb)) {
case AG_VARIABLE_DOUBLE:
*(double *)max = nmax;
break;
case AG_VARIABLE_FLOAT:
*(float *)max = (float)nmax;
break;
default:
break;
}
AG_UnlockVariable(maxb);
AG_ObjectUnlock(num);
}
void
AG_NumericalSetIncrement(AG_Numerical *num, double inc)
{
AG_ObjectLock(num);
num->inc = inc;
AG_ObjectUnlock(num);
}
void
AG_NumericalSetPrecision(AG_Numerical *num, const char *mode,
int precision)
{
AG_ObjectLock(num);
num->format[0] = '%';
num->format[1] = '.';
Snprintf(&num->format[2], sizeof(num->format)-2, "%d", precision);
Strlcat(num->format, mode, sizeof(num->format));
AG_ObjectUnlock(num);
}
void
AG_NumericalSelectUnit(AG_Numerical *num, const char *uname)
{
AG_TlistItem *it;
AG_ObjectLock(num);
AG_ObjectLock(num->units->list);
AG_TlistDeselectAll(num->units->list);
TAILQ_FOREACH(it, &num->units->list->items, items) {
const AG_Unit *unit = it->p1;
if (strcmp(unit->key, uname) == 0) {
it->selected++;
num->unit = unit;
UpdateUnitSelector(num);
break;
}
}
AG_ObjectUnlock(num->units->list);
AG_ObjectUnlock(num);
}
void
AG_NumericalSetWriteable(AG_Numerical *num, int writeable)
{
AG_ObjectLock(num);
num->writeable = writeable;
if (writeable) {
AG_WidgetEnable(num->incbu);
AG_WidgetEnable(num->decbu);
AG_WidgetEnable(num->input);
} else {
AG_WidgetDisable(num->incbu);
AG_WidgetDisable(num->decbu);
AG_WidgetDisable(num->input);
}
AG_ObjectUnlock(num);
}
void
AG_NumericalSetRange(AG_Numerical *num, double min, double max)
{
AG_ObjectLock(num);
AG_NumericalSetMin(num, min);
AG_NumericalSetMax(num, max);
AG_ObjectUnlock(num);
}
/* Convert the bound value to a float and return it. */
float
AG_NumericalGetFlt(AG_Numerical *num)
{
AG_Variable *bValue;
void *value;
bValue = AG_GetVariable(num, "value", &value);
switch (AG_VARIABLE_TYPE(bValue)) {
case AG_VARIABLE_FLOAT: return *(float *)value;
case AG_VARIABLE_DOUBLE: return (float)(*(double *)value);
case AG_VARIABLE_INT: return (float)(*(int *)value);
case AG_VARIABLE_UINT: return (float)(*(Uint *)value);
case AG_VARIABLE_UINT8: return (float)(*(Uint8 *)value);
case AG_VARIABLE_UINT16: return (float)(*(Uint16 *)value);
case AG_VARIABLE_UINT32: return (float)(*(Uint32 *)value);
case AG_VARIABLE_SINT8: return (float)(*(Sint8 *)value);
case AG_VARIABLE_SINT16: return (float)(*(Sint16 *)value);
case AG_VARIABLE_SINT32: return (float)(*(Sint32 *)value);
default: return (0.0);
}
}
/* Convert the bound value to a double and return it. */
double
AG_NumericalGetDbl(AG_Numerical *num)
{
AG_Variable *bValue;
void *value;
bValue = AG_GetVariable(num, "value", &value);
switch (AG_VARIABLE_TYPE(bValue)) {
case AG_VARIABLE_DOUBLE: return *(double *)value;
case AG_VARIABLE_FLOAT: return (double)(*(float *)value);
case AG_VARIABLE_INT: return (double)(*(int *)value);
case AG_VARIABLE_UINT: return (double)(*(Uint *)value);
case AG_VARIABLE_UINT8: return (double)(*(Uint8 *)value);
case AG_VARIABLE_UINT16: return (double)(*(Uint16 *)value);
case AG_VARIABLE_UINT32: return (double)(*(Uint32 *)value);
case AG_VARIABLE_SINT8: return (double)(*(Sint8 *)value);
case AG_VARIABLE_SINT16: return (double)(*(Sint16 *)value);
case AG_VARIABLE_SINT32: return (double)(*(Sint32 *)value);
default: return (0.0);
}
}
/* Convert the bound value to a natural integer and return it. */
int
AG_NumericalGetInt(AG_Numerical *num)
{
AG_Variable *bValue;
void *value;
bValue = AG_GetVariable(num, "value", &value);
switch (AG_VARIABLE_TYPE(bValue)) {
case AG_VARIABLE_INT: return *(int *)value;
case AG_VARIABLE_UINT: return (int)(*(Uint *)value);
case AG_VARIABLE_UINT8: return (int)(*(Uint8 *)value);
case AG_VARIABLE_UINT16: return (int)(*(Uint16 *)value);
case AG_VARIABLE_UINT32: return (int)(*(Uint32 *)value);
case AG_VARIABLE_SINT8: return (int)(*(Sint8 *)value);
case AG_VARIABLE_SINT16: return (int)(*(Sint16 *)value);
case AG_VARIABLE_SINT32: return (int)(*(Sint32 *)value);
case AG_VARIABLE_DOUBLE: return (int)(*(double *)value);
case AG_VARIABLE_FLOAT: return (int)(*(float *)value);
default: return (0);
}
}
/* Convert the bound value to a 32-bit integer and return it. */
Uint32
AG_NumericalGetUint32(AG_Numerical *num)
{
AG_Variable *bValue;
void *value;
bValue = AG_GetVariable(num, "value", &value);
switch (AG_VARIABLE_TYPE(bValue)) {
case AG_VARIABLE_INT: return (Uint32)(*(int *)value);
case AG_VARIABLE_UINT: return (Uint32)(*(Uint *)value);
case AG_VARIABLE_UINT8: return (Uint32)(*(Uint8 *)value);
case AG_VARIABLE_UINT16: return (Uint32)(*(Uint16 *)value);
case AG_VARIABLE_UINT32: return *(Uint32 *)value;
case AG_VARIABLE_SINT8: return (Uint32)(*(Sint8 *)value);
case AG_VARIABLE_SINT16: return (Uint32)(*(Sint16 *)value);
case AG_VARIABLE_SINT32: return (Uint32)(*(Sint32 *)value);
case AG_VARIABLE_DOUBLE: return (Uint32)(*(double *)value);
case AG_VARIABLE_FLOAT: return (Uint32)(*(float *)value);
default: return (0);
}
}
AG_WidgetClass agNumericalClass = {
{
"Agar(Widget:Numerical)",
sizeof(AG_Numerical),
{ 0,0 },
Init,
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};