/*
* Copyright (c) 2004-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 "mfspinbutton.h"
#include "window.h"
#include
static void SelectedUnit(AG_Event *);
static void InitUnitSystem(AG_MFSpinbutton *, const char *);
AG_MFSpinbutton *
AG_MFSpinbuttonNew(void *parent, Uint flags, const char *unit, const char *sep,
const char *label)
{
AG_MFSpinbutton *fsu;
fsu = Malloc(sizeof(AG_MFSpinbutton));
AG_ObjectInit(fsu, &agMFSpinbuttonClass);
fsu->sep = sep;
if (!(flags & AG_MFSPINBUTTON_NOHFILL)) { AG_ExpandHoriz(fsu); }
if ( flags & AG_MFSPINBUTTON_VFILL) { AG_ExpandVert(fsu); }
if (label != NULL) {
AG_TextboxSetLabel(fsu->input, "%s", label);
}
if (unit != NULL) {
fsu->units = AG_UComboNew(fsu, 0);
AG_SetEvent(fsu->units, "ucombo-selected",
SelectedUnit, "%p", fsu);
InitUnitSystem(fsu, unit);
AG_WidgetSetFocusable(fsu->units, 0);
}
AG_ObjectAttach(parent, fsu);
return (fsu);
}
static void
Bound(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_SELF();
AG_Variable *binding = AG_PTR(1);
if (strcmp(binding->name, "xvalue") == 0 ||
strcmp(binding->name, "yvalue") == 0) {
switch (AG_VARIABLE_TYPE(binding)) {
case AG_VARIABLE_DOUBLE:
fsu->min = -AG_DBL_MAX+1;
fsu->max = AG_DBL_MAX-1;
break;
case AG_VARIABLE_FLOAT:
fsu->min = -AG_FLT_MAX+1;
fsu->max = AG_FLT_MAX-1;
break;
default:
break;
}
}
}
static void
KeyDown(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_SELF();
int keysym = AG_INT(1);
switch (keysym) {
case SDLK_LEFT:
AG_MFSpinbuttonAddValue(fsu, "xvalue", -fsu->inc);
break;
case SDLK_RIGHT:
AG_MFSpinbuttonAddValue(fsu, "xvalue", fsu->inc);
break;
case SDLK_UP:
AG_MFSpinbuttonAddValue(fsu, "yvalue", -fsu->inc);
break;
case SDLK_DOWN:
AG_MFSpinbuttonAddValue(fsu, "yvalue", fsu->inc);
break;
}
}
static void
TextChanged(AG_Event *event)
{
char text[AG_TEXTBOX_STRING_MAX];
AG_MFSpinbutton *fsu = AG_PTR(1);
int unfocus = AG_INT(2);
AG_Variable *stringb;
char *tp = &text[0], *s;
AG_ObjectLock(fsu);
stringb = AG_GetVariable(fsu->input->ed, "string", &s);
Strlcpy(text, s, sizeof(text));
if ((s = AG_Strsep(&tp, fsu->sep)) != NULL) {
AG_MFSpinbuttonSetValue(fsu, "xvalue",
strtod(s, NULL)*fsu->unit->divider);
}
if ((s = AG_Strsep(&tp, fsu->sep)) != NULL) {
AG_MFSpinbuttonSetValue(fsu, "yvalue",
strtod(s, NULL)*fsu->unit->divider);
}
AG_UnlockVariable(stringb);
AG_PostEvent(NULL, fsu, "mfspinbutton-return", NULL);
if (unfocus)
AG_WidgetUnfocus(fsu->input);
AG_ObjectUnlock(fsu);
}
static void
DecrementY(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_PTR(1);
AG_ObjectLock(fsu);
AG_MFSpinbuttonAddValue(fsu, "yvalue", -fsu->inc);
AG_ObjectUnlock(fsu);
}
static void
IncrementY(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_PTR(1);
AG_ObjectLock(fsu);
AG_MFSpinbuttonAddValue(fsu, "yvalue", fsu->inc);
AG_ObjectUnlock(fsu);
}
static void
DecrementX(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_PTR(1);
AG_ObjectLock(fsu);
AG_MFSpinbuttonAddValue(fsu, "xvalue", -fsu->inc);
AG_ObjectUnlock(fsu);
}
static void
IncrementX(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_PTR(1);
AG_ObjectLock(fsu);
AG_MFSpinbuttonAddValue(fsu, "xvalue", fsu->inc);
AG_ObjectUnlock(fsu);
}
/* Widget must be locked. */
static void
UpdateUnitSelector(AG_MFSpinbutton *fsu)
{
AG_ButtonText(fsu->units->button, "%s", AG_UnitAbbr(fsu->unit));
}
static void
SelectedUnit(AG_Event *event)
{
AG_MFSpinbutton *fsu = AG_PTR(1);
AG_TlistItem *ti = AG_PTR(2);
AG_ObjectLock(fsu);
fsu->unit = (const AG_Unit *)ti->p1;
UpdateUnitSelector(fsu);
AG_ObjectUnlock(fsu);
}
static void
InitUnitSystem(AG_MFSpinbutton *fsu, const char *unit_key)
{
const AG_Unit *unit = NULL;
const AG_Unit *ugroup = NULL;
int found = 0;
int i;
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_FatalError("AG_MFSpinbutton: No such unit: %s", unit_key);
}
fsu->unit = unit;
UpdateUnitSelector(fsu);
AG_ObjectLock(fsu->units->list);
AG_TlistDeselectAll(fsu->units->list);
for (unit = &ugroup[0]; unit->key != NULL; unit++) {
AG_TlistItem *it;
it = AG_TlistAddPtr(fsu->units->list, NULL, _(unit->name),
(void *)unit);
if (unit == fsu->unit)
it->selected++;
}
AG_ObjectUnlock(fsu->units->list);
}
static void
Init(void *obj)
{
AG_MFSpinbutton *fsu = obj;
WIDGET(fsu)->flags |= AG_WIDGET_FOCUSABLE;
AG_BindDouble(fsu, "xvalue", &fsu->xvalue);
AG_BindDouble(fsu, "yvalue", &fsu->yvalue);
AG_BindDouble(fsu, "min", &fsu->min);
AG_BindDouble(fsu, "max", &fsu->max);
fsu->xvalue = 0.0;
fsu->yvalue = 0.0;
fsu->inc = 1.0;
fsu->input = AG_TextboxNew(fsu, 0, NULL);
fsu->writeable = 1;
fsu->sep = ",";
Strlcpy(fsu->format, "%.02f", sizeof(fsu->format));
AG_TextboxSizeHint(fsu->input, "888.88");
fsu->unit = AG_FindUnit("identity");
fsu->units = NULL;
fsu->xincbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("+"));
fsu->xdecbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("-"));
fsu->yincbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("+"));
fsu->ydecbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("-"));
AG_ButtonSetPadding(fsu->xincbu, 1,1,1,1);
AG_ButtonSetPadding(fsu->xdecbu, 1,1,1,1);
AG_ButtonSetPadding(fsu->yincbu, 1,1,1,1);
AG_ButtonSetPadding(fsu->ydecbu, 1,1,1,1);
AG_SetEvent(fsu->xincbu, "button-pushed", IncrementX, "%p", fsu);
AG_SetEvent(fsu->xdecbu, "button-pushed", DecrementX, "%p", fsu);
AG_SetEvent(fsu->yincbu, "button-pushed", IncrementY, "%p", fsu);
AG_SetEvent(fsu->ydecbu, "button-pushed", DecrementY, "%p", fsu);
AG_WidgetSetFocusable(fsu->xincbu, 0);
AG_WidgetSetFocusable(fsu->xdecbu, 0);
AG_WidgetSetFocusable(fsu->yincbu, 0);
AG_WidgetSetFocusable(fsu->ydecbu, 0);
AG_SetEvent(fsu, "bound", Bound, NULL);
AG_SetEvent(fsu, "window-keydown", KeyDown, NULL);
AG_SetEvent(fsu->input, "textbox-return", TextChanged, "%p,%i",fsu,1);
AG_SetEvent(fsu->input, "textbox-changed", TextChanged, "%p,%i",fsu,0);
}
static void
SizeRequest(void *obj, AG_SizeReq *r)
{
AG_MFSpinbutton *fsu = obj;
AG_SizeReq rChld, rYinc, rYdec;
AG_WidgetSizeReq(fsu->input, &rChld);
r->w = rChld.w;
r->h = rChld.h;
if (fsu->units != NULL) {
AG_WidgetSizeReq(fsu->units, &rChld);
r->w += rChld.w+4;
}
AG_WidgetSizeReq(fsu->xdecbu, &rChld);
r->w += rChld.w;
AG_WidgetSizeReq(fsu->xincbu, &rChld);
r->w += rChld.w;
AG_WidgetSizeReq(fsu->yincbu, &rYinc);
AG_WidgetSizeReq(fsu->ydecbu, &rYdec);
r->w += MAX(rYinc.w,rYdec.w);
}
static int
SizeAllocate(void *obj, const AG_SizeAlloc *a)
{
AG_MFSpinbutton *fsu = obj;
int szBtn = a->h/2;
int wUnitBox = (fsu->units != NULL) ? 25 : 0;
int x = 0, y = 0;
AG_SizeAlloc aChld;
if (a->w < szBtn*3 + wUnitBox + 4)
return (-1);
/* Input textbox */
aChld.x = x;
aChld.y = y;
aChld.w = a->w - 2 - wUnitBox - 2 - szBtn*3;
aChld.h = a->h;
AG_WidgetSizeAlloc(fsu->input, &aChld);
x += aChld.w + 2;
/* Unit selector */
if (fsu->units != NULL) {
aChld.x = x;
aChld.y = y;
aChld.w = wUnitBox;
aChld.h = a->h;
AG_WidgetSizeAlloc(fsu->units, &aChld);
x += aChld.w + 2;
}
/* Increment buttons */
aChld.w = szBtn;
aChld.h = szBtn;
aChld.x = x;
aChld.y = y + szBtn/2;
AG_WidgetSizeAlloc(fsu->xdecbu, &aChld);
aChld.x = x + szBtn*2;
aChld.y = y + szBtn/2;
AG_WidgetSizeAlloc(fsu->xincbu, &aChld);
aChld.x = x + szBtn;
aChld.y = y;
AG_WidgetSizeAlloc(fsu->ydecbu, &aChld);
aChld.x = x + szBtn;
aChld.y = y + szBtn;
AG_WidgetSizeAlloc(fsu->yincbu, &aChld);
return (0);
}
/* Update the textbox contents from the binding value. */
static void
UpdateTextbox(AG_MFSpinbutton *num)
{
char sx[64], sy[64];
AG_Variable *valueb;
void *value;
/* Get X value */
valueb = AG_GetVariable(num, "xvalue", &value);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
Snprintf(sx, sizeof(sx), num->format,
AG_Base2Unit(*(double *)value, num->unit));
break;
case AG_VARIABLE_FLOAT:
Snprintf(sx, sizeof(sx), num->format,
AG_Base2Unit(*(float *)value, num->unit));
break;
case AG_VARIABLE_INT:
Snprintf(sx, sizeof(sx), "%d", *(int *)value);
break;
case AG_VARIABLE_UINT:
Snprintf(sx, sizeof(sx), "%u", *(Uint *)value);
break;
case AG_VARIABLE_UINT8:
Snprintf(sx, sizeof(sx), "%u", (unsigned)(*(Uint8 *)value));
break;
case AG_VARIABLE_SINT8:
Snprintf(sx, sizeof(sx), "%d", (int)(*(Sint8 *)value));
break;
case AG_VARIABLE_UINT16:
Snprintf(sx, sizeof(sx), "%u", (unsigned)(*(Uint16 *)value));
break;
case AG_VARIABLE_SINT16:
Snprintf(sx, sizeof(sx), "%d", (int)(*(Sint16 *)value));
break;
case AG_VARIABLE_UINT32:
Snprintf(sx, sizeof(sx), "%u", (unsigned)(*(Uint32 *)value));
break;
case AG_VARIABLE_SINT32:
Snprintf(sx, sizeof(sx), "%d", (int)(*(Sint32 *)value));
break;
#if 0
case AG_VARIABLE_UINT64:
Snprintf(sx, sizeof(sx), "%llu",
(unsigned long long)(*(Uint64 *)value));
break;
case AG_VARIABLE_SINT64:
Snprintf(sx, sizeof(sx), "%lld",
(long long)(*(Sint64 *)value));
break;
#endif
default:
break;
}
AG_UnlockVariable(valueb);
/* Get Y value */
valueb = AG_GetVariable(num, "yvalue", &value);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
Snprintf(sy, sizeof(sy), num->format,
AG_Base2Unit(*(double *)value, num->unit));
break;
case AG_VARIABLE_FLOAT:
Snprintf(sy, sizeof(sy), num->format,
AG_Base2Unit(*(float *)value, num->unit));
break;
case AG_VARIABLE_INT:
Snprintf(sy, sizeof(sy), "%d", *(int *)value);
break;
case AG_VARIABLE_UINT:
Snprintf(sy, sizeof(sy), "%u", *(Uint *)value);
break;
case AG_VARIABLE_UINT8:
Snprintf(sy, sizeof(sy), "%u", (unsigned)(*(Uint8 *)value));
break;
case AG_VARIABLE_SINT8:
Snprintf(sy, sizeof(sy), "%d", (int)(*(Sint8 *)value));
break;
case AG_VARIABLE_UINT16:
Snprintf(sy, sizeof(sy), "%u", (unsigned)(*(Uint16 *)value));
break;
case AG_VARIABLE_SINT16:
Snprintf(sy, sizeof(sy), "%d", (int)(*(Sint16 *)value));
break;
case AG_VARIABLE_UINT32:
Snprintf(sy, sizeof(sy), "%u", (unsigned)(*(Uint32 *)value));
break;
case AG_VARIABLE_SINT32:
Snprintf(sy, sizeof(sy), "%d", (int)(*(Sint32 *)value));
break;
#if 0
case AG_VARIABLE_UINT64:
Snprintf(sy, sizeof(sy), "%llu",
(unsigned long long)(*(Uint64 *)value));
break;
case AG_VARIABLE_SINT64:
Snprintf(sy, sizeof(sy), "%lld",
(long long)(*(Uint64 *)value));
break;
#endif
default:
break;
}
AG_TextboxPrintf(num->input, "%s%s%s", sx, num->sep, sy);
AG_UnlockVariable(valueb);
}
static void
Draw(void *obj)
{
AG_MFSpinbutton *fsu = obj;
AG_Variable *xvalueb, *yvalueb;
double *xvalue, *yvalue;
AG_WidgetDraw(fsu->input);
if (fsu->units != NULL) { AG_WidgetDraw(fsu->units); }
AG_WidgetDraw(fsu->xincbu);
AG_WidgetDraw(fsu->yincbu);
AG_WidgetDraw(fsu->xdecbu);
AG_WidgetDraw(fsu->ydecbu);
if (!AG_WidgetFocused(fsu->input))
UpdateTextbox(fsu);
xvalueb = AG_GetVariable(fsu, "xvalue", &xvalue);
yvalueb = AG_GetVariable(fsu, "yvalue", &yvalue);
AG_TextboxPrintf(fsu->input, fsu->format,
*xvalue/fsu->unit->divider,
*yvalue/fsu->unit->divider);
AG_UnlockVariable(xvalueb);
AG_UnlockVariable(yvalueb);
}
void
AG_MFSpinbuttonAddValue(AG_MFSpinbutton *fsu, const char *which, double inc)
{
AG_Variable *valueb, *minb, *maxb;
void *value;
double *min, *max;
AG_ObjectLock(fsu);
inc *= fsu->unit->divider;
valueb = AG_GetVariable(fsu, which, &value);
minb = AG_GetVariable(fsu, "min", &min);
maxb = AG_GetVariable(fsu, "max", &max);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
*(double *)value = *(double *)value+inc < *min ? *min :
*(double *)value+inc > *max ? *max :
*(double *)value+inc;
break;
case AG_VARIABLE_FLOAT:
*(float *)value = *(float *)value+inc < *min ? *min :
*(float *)value+inc > *max ? *max :
*(float *)value+inc;
break;
default:
break;
}
AG_PostEvent(NULL, fsu, "mfspinbutton-changed", "%s", which);
AG_UnlockVariable(valueb);
AG_UnlockVariable(minb);
AG_UnlockVariable(maxb);
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSetValue(AG_MFSpinbutton *fsu, const char *which,
double nvalue)
{
AG_Variable *valueb, *minb, *maxb;
void *value;
double *min, *max;
AG_ObjectLock(fsu);
valueb = AG_GetVariable(fsu, which, &value);
minb = AG_GetVariable(fsu, "min", &min);
maxb = AG_GetVariable(fsu, "max", &max);
switch (AG_VARIABLE_TYPE(valueb)) {
case AG_VARIABLE_DOUBLE:
*(double *)value = nvalue < *min ? *min :
nvalue > *max ? *max :
nvalue;
break;
case AG_VARIABLE_FLOAT:
*(float *)value = nvalue < *min ? *min :
nvalue > *max ? *max :
(float)nvalue;
break;
default:
break;
}
AG_PostEvent(NULL, fsu, "mfspinbutton-changed", "%s", which);
AG_UnlockVariable(valueb);
AG_UnlockVariable(minb);
AG_UnlockVariable(maxb);
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSetMin(AG_MFSpinbutton *fsu, double nmin)
{
AG_Variable *minb;
void *min;
AG_ObjectLock(fsu);
minb = AG_GetVariable(fsu, "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(fsu);
}
void
AG_MFSpinbuttonSetMax(AG_MFSpinbutton *fsu, double nmax)
{
AG_Variable *maxb;
void *max;
AG_ObjectLock(fsu);
maxb = AG_GetVariable(fsu, "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(fsu);
}
void
AG_MFSpinbuttonSetIncrement(AG_MFSpinbutton *fsu, double inc)
{
AG_ObjectLock(fsu);
fsu->inc = inc;
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSetPrecision(AG_MFSpinbutton *fsu, const char *mode,
int precision)
{
AG_ObjectLock(fsu);
Snprintf(fsu->format, sizeof(fsu->format), "%%.%d%s", precision, mode);
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSelectUnit(AG_MFSpinbutton *fsu, const char *uname)
{
AG_TlistItem *it;
AG_ObjectLock(fsu);
AG_ObjectLock(fsu->units->list);
AG_TlistDeselectAll(fsu->units->list);
TAILQ_FOREACH(it, &fsu->units->list->items, items) {
const AG_Unit *u = it->p1;
if (strcmp(u->key, uname) == 0) {
it->selected++;
fsu->unit = u;
UpdateUnitSelector(fsu);
break;
}
}
AG_ObjectUnlock(fsu->units->list);
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSetWriteable(AG_MFSpinbutton *fsu, int writeable)
{
AG_ObjectLock(fsu);
fsu->writeable = writeable;
if (writeable) {
AG_WidgetEnable(fsu->xincbu);
AG_WidgetEnable(fsu->xdecbu);
AG_WidgetEnable(fsu->yincbu);
AG_WidgetEnable(fsu->ydecbu);
AG_WidgetEnable(fsu->input);
} else {
AG_WidgetDisable(fsu->xincbu);
AG_WidgetDisable(fsu->xdecbu);
AG_WidgetDisable(fsu->yincbu);
AG_WidgetDisable(fsu->ydecbu);
AG_WidgetDisable(fsu->input);
}
AG_ObjectUnlock(fsu);
}
void
AG_MFSpinbuttonSetRange(AG_MFSpinbutton *fsu, double min, double max)
{
AG_ObjectLock(fsu);
AG_MFSpinbuttonSetMin(fsu, min);
AG_MFSpinbuttonSetMax(fsu, max);
AG_ObjectUnlock(fsu);
}
AG_WidgetClass agMFSpinbuttonClass = {
{
"Agar(Widget:MFSpinbutton)",
sizeof(AG_MFSpinbutton),
{ 0,0 },
Init,
NULL, /* free */
NULL, /* destroy */
NULL, /* load */
NULL, /* save */
NULL /* edit */
},
Draw,
SizeRequest,
SizeAllocate
};