/* * 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. */ /* * LEGACY: Use AG_Numerical(3) instead of this widget. */ #include #include #include "fspinbutton.h" #include "primitive.h" #include static void SelectedUnit(AG_Event *); static void InitUnitSystem(AG_FSpinbutton *, const char *); AG_FSpinbutton * AG_FSpinbuttonNew(void *parent, Uint flags, const char *unit, const char *label) { AG_FSpinbutton *fsu; fsu = Malloc(sizeof(AG_FSpinbutton)); AG_ObjectInit(fsu, &agFSpinbuttonClass); if (!(flags & AG_FSPINBUTTON_NOHFILL)) { AG_ExpandHoriz(fsu); } if ( flags & AG_FSPINBUTTON_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_FSpinbutton *fsu = AG_SELF(); AG_Variable *binding = AG_PTR(1); if (strcmp(binding->name, "value") == 0) { switch (AG_VARIABLE_TYPE(binding)) { case AG_VARIABLE_DOUBLE: fsu->min = -AG_DBL_MAX+1; fsu->max = AG_DBL_MAX-1; AG_TextboxSetFltOnly(fsu->input, 1); break; case AG_VARIABLE_FLOAT: fsu->min = -AG_FLT_MAX+1; fsu->max = AG_FLT_MAX-1; AG_TextboxSetFltOnly(fsu->input, 1); break; case AG_VARIABLE_INT: fsu->min = AG_INT_MIN+1; fsu->max = AG_INT_MAX-1; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_UINT: fsu->min = 0; fsu->max = AG_UINT_MAX-1; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_UINT8: fsu->min = 0; fsu->max = 0xffU; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_SINT8: fsu->min = -0x7f+1; fsu->max = 0x7f-1; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_UINT16: fsu->min = 0; fsu->max = 0xffffU; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_SINT16: fsu->min = -0x7fff+1; fsu->max = 0x7fff-1; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_UINT32: fsu->min = 0; fsu->max = 0xffffffffU; AG_TextboxSetIntOnly(fsu->input, 1); break; case AG_VARIABLE_SINT32: fsu->min = -0x7fffffff+1; fsu->max = 0x7fffffff-1; AG_TextboxSetIntOnly(fsu->input, 1); break; default: break; } } } static void KeyDown(AG_Event *event) { AG_FSpinbutton *fsu = AG_SELF(); int keysym = AG_INT(1); switch (keysym) { case SDLK_UP: AG_FSpinbuttonAddValue(fsu, fsu->inc); break; case SDLK_DOWN: AG_FSpinbuttonAddValue(fsu, -fsu->inc); break; } } static void TextChanged(AG_Event *event) { AG_FSpinbutton *fsu = AG_PTR(1); int unfocus = AG_INT(2); AG_Variable *stringb, *valueb; char *s; void *value; AG_ObjectLock(fsu); valueb = AG_GetVariable(fsu, "value", &value); stringb = AG_GetVariable(fsu->input->ed, "string", &s); switch (AG_VARIABLE_TYPE(valueb)) { case AG_VARIABLE_DOUBLE: case AG_VARIABLE_FLOAT: AG_FSpinbuttonSetValue(fsu, AG_Unit2Base(strtod(s, NULL), fsu->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_FSpinbuttonSetValue(fsu, (double)strtol(s, NULL, 10)); break; default: break; } AG_UnlockVariable(stringb); AG_UnlockVariable(valueb); if (unfocus) { AG_WidgetUnfocus(fsu->input); } AG_PostEvent(NULL, fsu, "fspinbutton-return", NULL); AG_ObjectUnlock(fsu); } static void Increment(AG_Event *event) { AG_FSpinbutton *fsu = AG_PTR(1); AG_ObjectLock(fsu); AG_FSpinbuttonAddValue(fsu, fsu->inc); AG_ObjectUnlock(fsu); } static void Decrement(AG_Event *event) { AG_FSpinbutton *fsu = AG_PTR(1); AG_ObjectLock(fsu); AG_FSpinbuttonAddValue(fsu, -fsu->inc); AG_ObjectUnlock(fsu); } /* Widget must be locked. */ static __inline__ void UpdateUnitButton(AG_FSpinbutton *fsu) { AG_ButtonText(fsu->units->button, "%s", AG_UnitAbbr(fsu->unit)); } static void SelectedUnit(AG_Event *event) { AG_FSpinbutton *fsu = AG_PTR(1); AG_TlistItem *ti = AG_PTR(2); AG_ObjectLock(fsu); fsu->unit = (const AG_Unit *)ti->p1; UpdateUnitButton(fsu); AG_ObjectUnlock(fsu); } static void InitUnitSystem(AG_FSpinbutton *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_FSpinbutton: No such unit: %s", unit_key); } fsu->unit = unit; UpdateUnitButton(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_FSpinbutton *fsu = obj; AG_BindDouble(fsu, "value", &fsu->value); AG_BindDouble(fsu, "min", &fsu->min); AG_BindDouble(fsu, "max", &fsu->max); fsu->inc = 1.0; fsu->value = 0.0; fsu->input = AG_TextboxNew(fsu, AG_TEXTBOX_FLT_ONLY, NULL); fsu->writeable = 1; Strlcpy(fsu->format, "%.02f", sizeof(fsu->format)); AG_TextboxSizeHint(fsu->input, "88.88"); fsu->unit = AG_FindUnit("identity"); fsu->units = NULL; fsu->incbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("+")); AG_ButtonSetPadding(fsu->incbu, 1,1,1,1); AG_WidgetSetFocusable(fsu->incbu, 0); fsu->decbu = AG_ButtonNew(fsu, AG_BUTTON_REPEAT, _("-")); AG_ButtonSetPadding(fsu->decbu, 1,1,1,1); AG_WidgetSetFocusable(fsu->incbu, 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); AG_SetEvent(fsu->incbu, "button-pushed", Increment, "%p",fsu); AG_SetEvent(fsu->decbu, "button-pushed", Decrement, "%p",fsu); } void AG_FSpinbuttonSizeHint(AG_FSpinbutton *fsu, const char *text) { AG_ObjectLock(fsu); AG_TextboxSizeHint(fsu->input, text); AG_ObjectUnlock(fsu); } static void SizeRequest(void *obj, AG_SizeReq *r) { AG_FSpinbutton *num = obj; AG_SizeReq rChld, rInc, rDec; AG_WidgetSizeReq(num->input, &rChld); r->w = rChld.w; r->h = rChld.h; if (num->units != NULL) { AG_WidgetSizeReq(num->units, &rChld); r->w += rChld.w; } AG_WidgetSizeReq(num->incbu, &rInc); AG_WidgetSizeReq(num->decbu, &rDec); r->w += MAX(rInc.w, rDec.w); } static int SizeAllocate(void *obj, const AG_SizeAlloc *a) { AG_FSpinbutton *num = obj; AG_SizeAlloc aChld; AG_SizeReq rUnits; int szBtn = a->h/2; if (a->h < 4 || a->w < szBtn+4) return (-1); if (num->units != NULL) { AG_WidgetSizeReq(num->units, &rUnits); if (rUnits.w > a->w - szBtn-4) { rUnits.w = a->w - szBtn-4; } if (rUnits.h > a->h) { rUnits.h = a->h; } } else { rUnits.w = 0; rUnits.h = 0; } /* Size input textbox */ aChld.x = 0; aChld.y = 0; aChld.w = a->w - rUnits.w - 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 = rUnits.w; 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_FSpinbutton *fsu = obj; AG_Variable *valueb; void *value; AG_WidgetDraw(fsu->input); AG_WidgetDraw(fsu->incbu); AG_WidgetDraw(fsu->decbu); if (fsu->units != NULL) { AG_WidgetDraw(fsu->units); } if (AG_WidgetFocused(fsu->input)) return; valueb = AG_GetVariable(fsu, "value", &value); switch (AG_VARIABLE_TYPE(valueb)) { case AG_VARIABLE_DOUBLE: AG_TextboxPrintf(fsu->input, fsu->format, AG_Base2Unit(*(double *)value, fsu->unit)); break; case AG_VARIABLE_FLOAT: AG_TextboxPrintf(fsu->input, fsu->format, AG_Base2Unit(*(float *)value, fsu->unit)); break; case AG_VARIABLE_INT: AG_TextboxPrintf(fsu->input, "%d", *(int *)value); break; case AG_VARIABLE_UINT: AG_TextboxPrintf(fsu->input, "%u", *(Uint *)value); break; case AG_VARIABLE_UINT8: AG_TextboxPrintf(fsu->input, "%u", *(Uint8 *)value); break; case AG_VARIABLE_SINT8: AG_TextboxPrintf(fsu->input, "%d", *(Sint8 *)value); break; case AG_VARIABLE_UINT16: AG_TextboxPrintf(fsu->input, "%u", *(Uint16 *)value); break; case AG_VARIABLE_SINT16: AG_TextboxPrintf(fsu->input, "%d", *(Sint16 *)value); break; case AG_VARIABLE_UINT32: AG_TextboxPrintf(fsu->input, "%u", *(Uint32 *)value); break; case AG_VARIABLE_SINT32: AG_TextboxPrintf(fsu->input, "%d", *(Sint32 *)value); break; default: break; } AG_UnlockVariable(valueb); } #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, fsu->unit); \ if ((n+inc) < *min) { n = *min; } \ else if ((n+inc) > *max) { n = *(max); } \ else { n += inc; } \ *(TYPE *)value = AG_Unit2Base(n, fsu->unit); \ } while (0) void AG_FSpinbuttonAddValue(AG_FSpinbutton *fsu, double inc) { AG_Variable *valueb, *minb, *maxb; void *value; double n; double *min, *max; valueb = AG_GetVariable(fsu, "value", &value); minb = AG_GetVariable(fsu, "min", &min); maxb = AG_GetVariable(fsu, "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, fsu, "fspinbutton-changed", NULL); AG_UnlockVariable(valueb); AG_UnlockVariable(minb); AG_UnlockVariable(maxb); } #undef ADD_INCREMENT #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_FSpinbuttonSetValue(AG_FSpinbutton *fsu, double nvalue) { AG_Variable *valueb, *minb, *maxb; void *value; double *min, *max; valueb = AG_GetVariable(fsu, "value", &value); minb = AG_GetVariable(fsu, "min", &min); maxb = AG_GetVariable(fsu, "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, fsu, "fspinbutton-changed", NULL); AG_UnlockVariable(valueb); AG_UnlockVariable(minb); AG_UnlockVariable(maxb); } #undef ASSIGN_VALUE #undef CONV_VALUE void AG_FSpinbuttonSetMin(AG_FSpinbutton *fsu, double nmin) { AG_Variable *minb; void *min; /* TODO allow integer min/max bindings */ 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); } void AG_FSpinbuttonSetMax(AG_FSpinbutton *fsu, double nmax) { AG_Variable *maxb; void *max; /* TODO allow integer min/max bindings */ 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); } void AG_FSpinbuttonSetIncrement(AG_FSpinbutton *fsu, double inc) { AG_ObjectLock(fsu); fsu->inc = inc; AG_ObjectUnlock(fsu); } void AG_FSpinbuttonSetPrecision(AG_FSpinbutton *fsu, const char *mode, int precision) { AG_ObjectLock(fsu); fsu->format[0] = '%'; fsu->format[1] = '.'; Snprintf(&fsu->format[2], sizeof(fsu->format)-2, "%d", precision); Strlcat(fsu->format, mode, sizeof(fsu->format)); AG_ObjectUnlock(fsu); } void AG_FSpinbuttonSelectUnit(AG_FSpinbutton *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 *unit = it->p1; if (strcmp(unit->key, uname) == 0) { it->selected++; fsu->unit = unit; UpdateUnitButton(fsu); break; } } AG_ObjectUnlock(fsu->units->list); AG_ObjectUnlock(fsu); } void AG_FSpinbuttonSetWriteable(AG_FSpinbutton *fsu, int writeable) { AG_ObjectLock(fsu); fsu->writeable = writeable; if (writeable) { AG_WidgetEnable(fsu->incbu); AG_WidgetEnable(fsu->decbu); AG_WidgetEnable(fsu->input); } else { AG_WidgetDisable(fsu->incbu); AG_WidgetDisable(fsu->decbu); AG_WidgetDisable(fsu->input); } AG_ObjectUnlock(fsu); } void AG_FSpinbuttonSetRange(AG_FSpinbutton *fsu, double min, double max) { AG_ObjectLock(fsu); AG_FSpinbuttonSetMin(fsu, min); AG_FSpinbuttonSetMax(fsu, max); AG_ObjectUnlock(fsu); } AG_WidgetClass agFSpinbuttonClass = { { "Agar(Widget:FSpinbutton)", sizeof(AG_FSpinbutton), { 0,0 }, Init, NULL, /* free */ NULL, /* destroy */ NULL, /* load */ NULL, /* save */ NULL /* edit */ }, Draw, SizeRequest, SizeAllocate };