1 /*
2  * Copyright (c) 2007-2012 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #ifdef __NetBSD__
27 #define _NETBSD_SOURCE
28 #endif
29 
30 #include <agar/core/core.h>
31 #include <agar/gui/numerical.h>
32 #include <agar/gui/primitive.h>
33 
34 #include <string.h>
35 
36 #include <agar/config/_mk_have_strtold.h>
37 #include <agar/config/_mk_have_strtoll.h>
38 #if defined(_MK_HAVE_STRTOLD_H) || defined(_MK_HAVE_STRTOLL_H)
39 # define _XOPEN_SOURCE 600
40 # include <stdlib.h>
41 #endif
42 
43 static void UnitSelected(AG_Event *);
44 
45 AG_Numerical *
AG_NumericalNew(void * parent,Uint flags,const char * unit,const char * fmt,...)46 AG_NumericalNew(void *parent, Uint flags, const char *unit, const char *fmt,
47     ...)
48 {
49 	char s[AG_LABEL_MAX];
50 	va_list ap;
51 
52 	if (fmt != NULL) {
53 		va_start(ap, fmt);
54 		Vsnprintf(s, sizeof(s), fmt, ap);
55 		va_end(ap);
56 		return AG_NumericalNewS(parent, flags, unit, s);
57 	} else {
58 		return AG_NumericalNewS(parent, flags, unit, NULL);
59 	}
60 }
61 
62 AG_Numerical *
AG_NumericalNewS(void * parent,Uint flags,const char * unit,const char * label)63 AG_NumericalNewS(void *parent, Uint flags, const char *unit, const char *label)
64 {
65 	AG_Numerical *num;
66 
67 	num = Malloc(sizeof(AG_Numerical));
68 	AG_ObjectInit(num, &agNumericalClass);
69 
70 	if (flags & AG_NUMERICAL_HFILL) { AG_ExpandHoriz(num); }
71 	if (flags & AG_NUMERICAL_VFILL) { AG_ExpandVert(num); }
72 	if (label != NULL) {
73 		AG_TextboxSetLabelS(num->input, label);
74 	}
75 	if (unit != NULL) {
76 		num->units = AG_UComboNew(num, 0);
77 		AG_SetEvent(num->units, "ucombo-selected",
78 		    UnitSelected, "%p", num);
79 		AG_NumericalSetUnitSystem(num, unit);
80 		AG_WidgetSetFocusable(num->units, 0);
81 	}
82 
83 	AG_ObjectAttach(parent, num);
84 	return (num);
85 }
86 
87 AG_Numerical *
AG_NumericalNewDbl(void * parent,Uint flags,const char * unit,const char * label,double * v)88 AG_NumericalNewDbl(void *parent, Uint flags, const char *unit, const char *label, double *v)
89 {
90 	AG_Numerical *num;
91 	num = AG_NumericalNewS(parent, flags, unit, label);
92 	AG_BindDouble(num, "value", v);
93 	return (num);
94 }
95 AG_Numerical *
AG_NumericalNewDblR(void * parent,Uint flags,const char * unit,const char * label,double * v,double min,double max)96 AG_NumericalNewDblR(void *parent, Uint flags, const char *unit, const char *label, double *v, double min, double max)
97 {
98 	AG_Numerical *num;
99 	num = AG_NumericalNewS(parent, flags, unit, label);
100 	AG_BindDouble(num, "value", v);
101 	AG_SetDouble(num, "min", min);
102 	AG_SetDouble(num, "max", max);
103 	return (num);
104 }
105 
106 #ifdef HAVE_LONG_DOUBLE
107 AG_Numerical *
AG_NumericalNewLdbl(void * parent,Uint flags,const char * unit,const char * label,long double * v)108 AG_NumericalNewLdbl(void *parent, Uint flags, const char *unit, const char *label, long double *v)
109 {
110 	AG_Numerical *num;
111 	num = AG_NumericalNewS(parent, flags, unit, label);
112 	AG_BindLongDouble(num, "value", v);
113 	return (num);
114 }
115 AG_Numerical *
AG_NumericalNewLdblR(void * parent,Uint flags,const char * unit,const char * label,long double * v,long double min,long double max)116 AG_NumericalNewLdblR(void *parent, Uint flags, const char *unit, const char *label, long double *v, long double min, long double max)
117 {
118 	AG_Numerical *num;
119 	num = AG_NumericalNewS(parent, flags, unit, label);
120 	AG_BindLongDouble(num, "value", v);
121 	AG_SetLongDouble(num, "min", min);
122 	AG_SetLongDouble(num, "max", max);
123 	return (num);
124 }
125 #endif /* HAVE_LONG_DOUBLE */
126 
127 AG_Numerical *
AG_NumericalNewFlt(void * parent,Uint flags,const char * unit,const char * label,float * v)128 AG_NumericalNewFlt(void *parent, Uint flags, const char *unit, const char *label, float *v)
129 {
130 	AG_Numerical *num;
131 	num = AG_NumericalNewS(parent, flags, unit, label);
132 	AG_BindFloat(num, "value", v);
133 	return (num);
134 }
135 AG_Numerical *
AG_NumericalNewFltR(void * parent,Uint flags,const char * unit,const char * label,float * v,float min,float max)136 AG_NumericalNewFltR(void *parent, Uint flags, const char *unit, const char *label, float *v, float min, float max)
137 {
138 	AG_Numerical *num;
139 	num = AG_NumericalNewS(parent, flags, unit, label);
140 	AG_BindFloat(num, "value", v);
141 	AG_SetFloat(num, "min", min);
142 	AG_SetFloat(num, "max", max);
143 	return (num);
144 }
145 
146 AG_Numerical *
AG_NumericalNewInt(void * parent,Uint flags,const char * unit,const char * label,int * v)147 AG_NumericalNewInt(void *parent, Uint flags, const char *unit, const char *label, int *v)
148 {
149 	AG_Numerical *num;
150 	num = AG_NumericalNewS(parent, flags, unit, label);
151 	AG_BindInt(num, "value", v);
152 	return (num);
153 }
154 AG_Numerical *
AG_NumericalNewIntR(void * parent,Uint flags,const char * unit,const char * label,int * v,int min,int max)155 AG_NumericalNewIntR(void *parent, Uint flags, const char *unit, const char *label, int *v, int min, int max)
156 {
157 	AG_Numerical *num;
158 	num = AG_NumericalNewS(parent, flags, unit, label);
159 	AG_BindInt(num, "value", v);
160 	AG_SetInt(num, "min", min);
161 	AG_SetInt(num, "max", max);
162 	return (num);
163 }
164 AG_Numerical *
AG_NumericalNewUint(void * parent,Uint flags,const char * unit,const char * label,Uint * v)165 AG_NumericalNewUint(void *parent, Uint flags, const char *unit, const char *label, Uint *v)
166 {
167 	AG_Numerical *num;
168 	num = AG_NumericalNewS(parent, flags, unit, label);
169 	AG_BindUint(num, "value", v);
170 	return (num);
171 }
172 AG_Numerical *
AG_NumericalNewUintR(void * parent,Uint flags,const char * unit,const char * label,Uint * v,Uint min,Uint max)173 AG_NumericalNewUintR(void *parent, Uint flags, const char *unit, const char *label, Uint *v, Uint min, Uint max)
174 {
175 	AG_Numerical *num;
176 	num = AG_NumericalNewS(parent, flags, unit, label);
177 	AG_BindUint(num, "value", v);
178 	AG_SetUint(num, "min", min);
179 	AG_SetUint(num, "max", max);
180 	return (num);
181 }
182 
183 #ifdef AG_LEGACY
184 
185 AG_Numerical *
AG_NumericalNewUint8(void * parent,Uint flags,const char * unit,const char * label,Uint8 * v)186 AG_NumericalNewUint8(void *parent, Uint flags, const char *unit, const char *label, Uint8 *v)
187 {
188 	AG_Numerical *num;
189 	num = AG_NumericalNewS(parent, flags, unit, label);
190 	AG_BindUint8(num, "value", v);
191 	return (num);
192 }
193 AG_Numerical *
AG_NumericalNewUint8R(void * parent,Uint flags,const char * unit,const char * label,Uint8 * v,Uint8 min,Uint8 max)194 AG_NumericalNewUint8R(void *parent, Uint flags, const char *unit, const char *label, Uint8 *v, Uint8 min, Uint8 max)
195 {
196 	AG_Numerical *num;
197 	num = AG_NumericalNewS(parent, flags, unit, label);
198 	AG_BindUint8(num, "value", v);
199 	AG_SetUint8(num, "min", min);
200 	AG_SetUint8(num, "max", max);
201 	return (num);
202 }
203 AG_Numerical *
AG_NumericalNewSint8(void * parent,Uint flags,const char * unit,const char * label,Sint8 * v)204 AG_NumericalNewSint8(void *parent, Uint flags, const char *unit, const char *label, Sint8 *v)
205 {
206 	AG_Numerical *num;
207 	num = AG_NumericalNewS(parent, flags, unit, label);
208 	AG_BindSint8(num, "value", v);
209 	return (num);
210 }
211 AG_Numerical *
AG_NumericalNewSint8R(void * parent,Uint flags,const char * unit,const char * label,Sint8 * v,Sint8 min,Sint8 max)212 AG_NumericalNewSint8R(void *parent, Uint flags, const char *unit, const char *label, Sint8 *v, Sint8 min, Sint8 max)
213 {
214 	AG_Numerical *num;
215 	num = AG_NumericalNewS(parent, flags, unit, label);
216 	AG_BindSint8(num, "value", v);
217 	AG_SetSint8(num, "min", min);
218 	AG_SetSint8(num, "max", max);
219 	return (num);
220 }
221 AG_Numerical *
AG_NumericalNewUint16(void * parent,Uint flags,const char * unit,const char * label,Uint16 * v)222 AG_NumericalNewUint16(void *parent, Uint flags, const char *unit, const char *label, Uint16 *v)
223 {
224 	AG_Numerical *num;
225 	num = AG_NumericalNewS(parent, flags, unit, label);
226 	AG_BindUint16(num, "value", v);
227 	return (num);
228 }
229 AG_Numerical *
AG_NumericalNewUint16R(void * parent,Uint flags,const char * unit,const char * label,Uint16 * v,Uint16 min,Uint16 max)230 AG_NumericalNewUint16R(void *parent, Uint flags, const char *unit, const char *label, Uint16 *v, Uint16 min, Uint16 max)
231 {
232 	AG_Numerical *num;
233 	num = AG_NumericalNewS(parent, flags, unit, label);
234 	AG_BindUint16(num, "value", v);
235 	AG_SetUint16(num, "min", min);
236 	AG_SetUint16(num, "max", max);
237 	return (num);
238 }
239 AG_Numerical *
AG_NumericalNewSint16(void * parent,Uint flags,const char * unit,const char * label,Sint16 * v)240 AG_NumericalNewSint16(void *parent, Uint flags, const char *unit, const char *label, Sint16 *v)
241 {
242 	AG_Numerical *num;
243 	num = AG_NumericalNewS(parent, flags, unit, label);
244 	AG_BindSint16(num, "value", v);
245 	return (num);
246 }
247 AG_Numerical *
AG_NumericalNewSint16R(void * parent,Uint flags,const char * unit,const char * label,Sint16 * v,Sint16 min,Sint16 max)248 AG_NumericalNewSint16R(void *parent, Uint flags, const char *unit, const char *label, Sint16 *v, Sint16 min, Sint16 max)
249 {
250 	AG_Numerical *num;
251 	num = AG_NumericalNewS(parent, flags, unit, label);
252 	AG_BindSint16(num, "value", v);
253 	AG_SetSint16(num, "min", min);
254 	AG_SetSint16(num, "max", max);
255 	return (num);
256 }
257 AG_Numerical *
AG_NumericalNewUint32(void * parent,Uint flags,const char * unit,const char * label,Uint32 * v)258 AG_NumericalNewUint32(void *parent, Uint flags, const char *unit, const char *label, Uint32 *v)
259 {
260 	AG_Numerical *num;
261 	num = AG_NumericalNewS(parent, flags, unit, label);
262 	AG_BindUint32(num, "value", v);
263 	return (num);
264 }
265 AG_Numerical *
AG_NumericalNewUint32R(void * parent,Uint flags,const char * unit,const char * label,Uint32 * v,Uint32 min,Uint32 max)266 AG_NumericalNewUint32R(void *parent, Uint flags, const char *unit, const char *label, Uint32 *v, Uint32 min, Uint32 max)
267 {
268 	AG_Numerical *num;
269 	num = AG_NumericalNewS(parent, flags, unit, label);
270 	AG_BindUint32(num, "value", v);
271 	AG_SetUint32(num, "min", min);
272 	AG_SetUint32(num, "max", max);
273 	return (num);
274 }
275 AG_Numerical *
AG_NumericalNewSint32(void * parent,Uint flags,const char * unit,const char * label,Sint32 * v)276 AG_NumericalNewSint32(void *parent, Uint flags, const char *unit, const char *label, Sint32 *v)
277 {
278 	AG_Numerical *num;
279 	num = AG_NumericalNewS(parent, flags, unit, label);
280 	AG_BindSint32(num, "value", v);
281 	return (num);
282 }
283 AG_Numerical *
AG_NumericalNewSint32R(void * parent,Uint flags,const char * unit,const char * label,Sint32 * v,Sint32 min,Sint32 max)284 AG_NumericalNewSint32R(void *parent, Uint flags, const char *unit, const char *label, Sint32 *v, Sint32 min, Sint32 max)
285 {
286 	AG_Numerical *num;
287 	num = AG_NumericalNewS(parent, flags, unit, label);
288 	AG_BindSint32(num, "value", v);
289 	AG_SetSint32(num, "min", min);
290 	AG_SetSint32(num, "max", max);
291 	return (num);
292 }
293 #endif /* AG_LEGACY */
294 
295 static Uint32
UpdateTimeout(AG_Timer * to,AG_Event * event)296 UpdateTimeout(AG_Timer *to, AG_Event *event)
297 {
298 	AG_Numerical *num = AG_SELF();
299 
300 	if (!AG_WidgetIsFocused(num)) {
301 		AG_NumericalUpdate(num);
302 	}
303 	return (to->ival);
304 }
305 
306 #undef SET_DEF
307 #define SET_DEF(fn,dmin,dmax,dinc) { 					\
308 	if (!AG_Defined(num, "min")) { fn(num, "min", dmin); }		\
309 	if (!AG_Defined(num, "max")) { fn(num, "max", dmax); }		\
310 	if (!AG_Defined(num, "inc")) { fn(num, "inc", dinc); }		\
311 }
312 static void
OnShow(AG_Event * event)313 OnShow(AG_Event *event)
314 {
315 	AG_Numerical *num = AG_SELF();
316 	AG_Variable *V;
317 
318 	if ((num->flags & AG_NUMERICAL_EXCL) == 0) {
319 		AG_AddTimer(num, &num->updateTo, 250, UpdateTimeout, NULL);
320 	}
321 	if ((V = AG_GetVariableLocked(num, "value")) == NULL) {
322 		if (num->flags & AG_NUMERICAL_INT) {
323 			V = AG_SetInt(num, "value", 0);
324 		} else {
325 			V = AG_SetDouble(num, "value", 0.0);
326 		}
327 		if (V == NULL) {
328 			return;
329 		}
330 		AG_LockVariable(V);
331 	}
332 	switch (AG_VARIABLE_TYPE(V)) {
333 	case AG_VARIABLE_FLOAT:  SET_DEF(AG_SetFloat, -AG_FLT_MAX, AG_FLT_MAX, 0.1f); break;
334 	case AG_VARIABLE_DOUBLE: SET_DEF(AG_SetDouble, -AG_DBL_MAX, AG_DBL_MAX, 0.1); break;
335 #ifdef HAVE_LONG_DOUBLE
336 	case AG_VARIABLE_LONG_DOUBLE: SET_DEF(AG_SetLongDouble, -AG_LDBL_MAX, AG_LDBL_MAX, 0.1l); break;
337 #endif
338 	case AG_VARIABLE_INT:    SET_DEF(AG_SetInt, AG_INT_MIN+1, AG_INT_MAX-1, 1); break;
339 	case AG_VARIABLE_UINT:   SET_DEF(AG_SetUint, 0U, AG_UINT_MAX-1, 1U); break;
340 	case AG_VARIABLE_UINT8:  SET_DEF(AG_SetUint8, 0U, 0xffU, 1U); break;
341 	case AG_VARIABLE_SINT8:  SET_DEF(AG_SetSint8, -0x7f, 0x7f, 1); break;
342 	case AG_VARIABLE_UINT16: SET_DEF(AG_SetUint16, 0U, 0xffffU, 1U); break;
343 	case AG_VARIABLE_SINT16: SET_DEF(AG_SetSint16, -0x7fff, 0x7fff, 1); break;
344 	case AG_VARIABLE_UINT32: SET_DEF(AG_SetUint32, 0UL, 0xffffffffUL, 1UL); break;
345 	case AG_VARIABLE_SINT32: SET_DEF(AG_SetSint32, -0x7fffffffL, 0x7fffffffL, 1L); break;
346 #ifdef HAVE_64BIT
347 	case AG_VARIABLE_UINT64: SET_DEF(AG_SetUint64, 0ULL, 0xffffffffffffffffULL, 1ULL); break;
348 	case AG_VARIABLE_SINT64: SET_DEF(AG_SetSint64, -0x7fffffffffffffffLL, 0x7fffffffffffffffLL, 1LL); break;
349 #endif
350 	default: break;
351 	}
352 	switch (AG_VARIABLE_TYPE(V)) {
353 	case AG_VARIABLE_FLOAT:
354 	case AG_VARIABLE_DOUBLE:
355 	case AG_VARIABLE_LONG_DOUBLE:
356 		AG_TextboxSetFltOnly(num->input, 1);
357 		break;
358 	default:
359 		AG_TextboxSetIntOnly(num->input, 1);
360 		break;
361 	}
362 	AG_UnlockVariable(V);
363 	AG_NumericalUpdate(num);
364 }
365 
366 static void
KeyDown(AG_Event * event)367 KeyDown(AG_Event *event)
368 {
369 	AG_Numerical *num = AG_SELF();
370 	int keysym = AG_INT(1);
371 
372 	switch (keysym) {
373 	case AG_KEY_UP:
374 		AG_NumericalIncrement(num);
375 		break;
376 	case AG_KEY_DOWN:
377 		AG_NumericalDecrement(num);
378 		break;
379 	default:
380 		break;
381 	}
382 }
383 
384 /*
385  * Update the numerical value from the textbox.
386  */
387 #define SET_NUM(TYPE,expr) {						\
388     	TYPE val = (TYPE)(expr);					\
389 	*(TYPE *)value = val < *(TYPE *)min ? *(TYPE *)min :		\
390                          val > *(TYPE *)max ? *(TYPE *)max : val;	\
391 }
392 static void
UpdateFromText(AG_Event * event)393 UpdateFromText(AG_Event *event)
394 {
395 	AG_Numerical *num = AG_PTR(1);
396 	int unfocus = AG_INT(2);
397 	AG_Variable *valueb, *minb, *maxb;
398 	void *value, *min, *max;
399 
400 	valueb = AG_GetVariable(num, "value", &value);
401 	minb = AG_GetVariable(num, "min", &min);
402 	maxb = AG_GetVariable(num, "max", &max);
403 
404 	switch (AG_VARIABLE_TYPE(valueb)) {
405 	case AG_VARIABLE_FLOAT:
406 		SET_NUM(float, AG_Unit2Base(strtod(num->inTxt,NULL),num->unit));
407 		break;
408 	case AG_VARIABLE_DOUBLE:
409 		SET_NUM(double, AG_Unit2Base(strtod(num->inTxt,NULL),num->unit));
410 		break;
411 #ifdef HAVE_LONG_DOUBLE
412 	case AG_VARIABLE_LONG_DOUBLE:
413 # ifdef _MK_HAVE_STRTOLD
414 		SET_NUM(long double, AG_Unit2BaseLDBL(strtold(num->inTxt,NULL),num->unit));
415 # else
416 		SET_NUM(long double, AG_Unit2BaseLDBL((long double)strtod(num->inTxt,NULL),num->unit));
417 # endif
418 		break;
419 #endif
420 	case AG_VARIABLE_INT:    SET_NUM(int, strtol(num->inTxt,NULL,10));	break;
421 	case AG_VARIABLE_UINT:   SET_NUM(Uint, strtoul(num->inTxt,NULL,10));	break;
422 	case AG_VARIABLE_UINT8:  SET_NUM(Uint8, strtoul(num->inTxt,NULL,10));	break;
423 	case AG_VARIABLE_SINT8:  SET_NUM(Sint8, strtol(num->inTxt,NULL,10));	break;
424 	case AG_VARIABLE_UINT16: SET_NUM(Uint16, strtoul(num->inTxt,NULL,10));	break;
425 	case AG_VARIABLE_SINT16: SET_NUM(Sint16, strtol(num->inTxt,NULL,10));	break;
426 	case AG_VARIABLE_UINT32: SET_NUM(Uint32, strtoul(num->inTxt,NULL,10));	break;
427 	case AG_VARIABLE_SINT32: SET_NUM(Sint32, strtol(num->inTxt,NULL,10));	break;
428 #ifdef HAVE_64BIT
429 	case AG_VARIABLE_UINT64:
430 # ifdef _MK_HAVE_STRTOLL
431 		SET_NUM(Uint64, strtoull(num->inTxt,NULL,10));
432 # else
433 		SET_NUM(Uint64, strtoul(num->inTxt,NULL,10));
434 # endif
435 		break;
436 	case AG_VARIABLE_SINT64:
437 # ifdef _MK_HAVE_STRTOLL
438 		SET_NUM(Sint64, strtoll(num->inTxt,NULL,10));
439 # else
440 		SET_NUM(Sint64, strtol(num->inTxt,NULL,10));
441 # endif
442 		break;
443 #endif
444 	default:
445 		break;
446 	}
447 
448 	AG_PostEvent(NULL, num, "numerical-changed", NULL);
449 
450 	AG_UnlockVariable(valueb);
451 	AG_UnlockVariable(minb);
452 	AG_UnlockVariable(maxb);
453 
454 	if (unfocus) {
455 		AG_WidgetUnfocus(num->input);
456 	}
457 	AG_PostEvent(NULL, num, "numerical-return", NULL);
458 }
459 #undef SET_NUM
460 
461 static void
IncrementValue(AG_Event * event)462 IncrementValue(AG_Event *event)
463 {
464 	AG_Numerical *num = AG_PTR(1);
465 	AG_NumericalIncrement(num);
466 }
467 static void
DecrementValue(AG_Event * event)468 DecrementValue(AG_Event *event)
469 {
470 	AG_Numerical *num = AG_PTR(1);
471 	AG_NumericalDecrement(num);
472 }
473 
474 static void
UpdateUnitSelector(AG_Numerical * num)475 UpdateUnitSelector(AG_Numerical *num)
476 {
477 	AG_ButtonTextS(num->units->button, AG_UnitAbbr(num->unit));
478 	if (WIDGET(num)->window != NULL &&
479 	    WIDGET(num)->window->visible)
480 		AG_NumericalUpdate(num);
481 }
482 
483 static void
UnitSelected(AG_Event * event)484 UnitSelected(AG_Event *event)
485 {
486 	AG_Numerical *num = AG_PTR(1);
487 	AG_TlistItem *ti = AG_PTR(2);
488 
489 	AG_ObjectLock(num);
490 	num->unit = (const AG_Unit *)ti->p1;
491 	UpdateUnitSelector(num);
492 	AG_ObjectUnlock(num);
493 }
494 
495 int
AG_NumericalSetUnitSystem(AG_Numerical * num,const char * unit_key)496 AG_NumericalSetUnitSystem(AG_Numerical *num, const char *unit_key)
497 {
498 	const AG_Unit *unit = NULL;
499 	const AG_Unit *ugroup = NULL;
500 	int found = 0, i;
501 	int w, h, nUnits = 0;
502 
503 	AG_ObjectLock(num);
504 
505 	for (i = 0; i < agnUnitGroups; i++) {
506 		ugroup = agUnitGroups[i];
507 		for (unit = &ugroup[0]; unit->key != NULL; unit++) {
508 			if (strcmp(unit->key, unit_key) == 0) {
509 				found++;
510 				break;
511 			}
512 		}
513 		if (found)
514 			break;
515 	}
516 	if (!found) {
517 		AG_SetError(_("No such unit: %s"), unit_key);
518 		AG_ObjectUnlock(num);
519 		return (-1);
520 	}
521 	num->unit = unit;
522 	UpdateUnitSelector(num);
523 
524 	num->wUnitSel = 0;
525 	num->hUnitSel = 0;
526 	num->wPreUnit = 0;
527 
528 	AG_ObjectLock(num->units->list);
529 	AG_TlistDeselectAll(num->units->list);
530 	AG_TlistBegin(num->units->list);
531 	for (unit = &ugroup[0]; unit->key != NULL; unit++) {
532 		AG_TlistItem *it;
533 
534 		AG_TextSize(AG_UnitAbbr(unit), &w, &h);
535 		if (w > num->wUnitSel) { num->wUnitSel = w; }
536 		if (h > num->hUnitSel) { num->hUnitSel = h; }
537 
538 		AG_TextSize(unit->name, &w, NULL);
539 		if (w > num->wPreUnit) { num->wPreUnit = w; }
540 
541 		it = AG_TlistAddPtr(num->units->list, NULL, _(unit->name),
542 		    (void *)unit);
543 		if (unit == num->unit)
544 			it->selected++;
545 
546 		nUnits++;
547 	}
548 	AG_TlistEnd(num->units->list);
549 	AG_TlistSizeHintLargest(num->units->list, 5);
550 	AG_ObjectUnlock(num->units->list);
551 
552 	if (num->wPreUnit > 0) { num->wPreUnit += 8; }
553 	AG_UComboSizeHintPixels(num->units, num->wPreUnit,
554 	    nUnits<6 ? (nUnits + 1) : 6);
555 
556 	AG_WidgetUpdate(num);
557 	AG_ObjectUnlock(num);
558 	return (0);
559 }
560 
561 /* Update the input text from the binding value. */
562 void
AG_NumericalUpdate(AG_Numerical * num)563 AG_NumericalUpdate(AG_Numerical *num)
564 {
565 	char s[64];
566 	AG_Variable *valueb;
567 	void *value;
568 
569 	valueb = AG_GetVariable(num, "value", &value);
570 	switch (AG_VARIABLE_TYPE(valueb)) {
571 	case AG_VARIABLE_DOUBLE:
572 		Snprintf(s, sizeof(s), num->format,
573 		    AG_Base2Unit(*(double *)value, num->unit));
574 		break;
575 	case AG_VARIABLE_FLOAT:
576 		Snprintf(s, sizeof(s), num->format,
577 		    AG_Base2Unit(*(float *)value, num->unit));
578 		break;
579 	case AG_VARIABLE_INT:	 StrlcpyInt(s, *(int *)value, sizeof(s));	break;
580 	case AG_VARIABLE_UINT:	 StrlcpyUint(s, *(Uint *)value, sizeof(s));	break;
581 	case AG_VARIABLE_UINT8:	 StrlcpyUint(s, *(Uint8 *)value, sizeof(s));	break;
582 	case AG_VARIABLE_SINT8:	 StrlcpyInt(s, *(Sint8 *)value, sizeof(s));	break;
583 	case AG_VARIABLE_UINT16: StrlcpyUint(s, *(Uint16 *)value, sizeof(s));	break;
584 	case AG_VARIABLE_SINT16: StrlcpyInt(s, *(Sint16 *)value, sizeof(s));	break;
585 	case AG_VARIABLE_UINT32: Snprintf(s, sizeof(s), "%lu", (unsigned long)*(Uint32 *)value); break;
586 	case AG_VARIABLE_SINT32: Snprintf(s, sizeof(s), "%ld", (long)*(Sint32 *)value);          break;
587 #ifdef HAVE_64BIT
588 	case AG_VARIABLE_UINT64: Snprintf(s, sizeof(s), "%llu", (unsigned long long)*(Uint64 *)value); break;
589 	case AG_VARIABLE_SINT64: Snprintf(s, sizeof(s), "%lld", (long long)*(Sint64 *)value);          break;
590 #endif
591 	default: break;
592 	}
593 	if (strcmp(num->inTxt, s) != 0) {
594 		AG_TextboxSetString(num->input, s);
595 	}
596 	AG_UnlockVariable(valueb);
597 }
598 
599 static void
Init(void * obj)600 Init(void *obj)
601 {
602 	AG_Numerical *num = obj;
603 
604 	WIDGET(num)->flags |= AG_WIDGET_FOCUSABLE|
605 	                      AG_WIDGET_TABLE_EMBEDDABLE;
606 
607 	num->flags = 0;
608 	num->writeable = 1;
609 	num->wUnitSel = 0;
610 	num->hUnitSel = 0;
611 	num->inTxt[0] = '\0';
612 	Strlcpy(num->format, "%.02f", sizeof(num->format));
613 	AG_InitTimer(&num->updateTo, "update", 0);
614 
615 	num->input = AG_TextboxNewS(num, AG_TEXTBOX_EXCL, NULL);
616 	AG_TextboxBindASCII(num->input, num->inTxt, sizeof(num->inTxt));
617 	AG_TextboxSizeHint(num->input, "8888.88");
618 
619 	num->unit = AG_FindUnit("identity");
620 	num->units = NULL;
621 
622 	num->incbu = AG_ButtonNewS(num, AG_BUTTON_REPEAT, _("+"));
623 	AG_ButtonSetPadding(num->incbu, 0,0,0,0);
624 	AG_LabelSetPadding(num->incbu->lbl, 0,0,0,0);
625 	AG_WidgetSetFocusable(num->incbu, 0);
626 
627 	num->decbu = AG_ButtonNewS(num, AG_BUTTON_REPEAT, _("-"));
628 	AG_ButtonSetPadding(num->decbu, 0,0,0,0);
629 	AG_LabelSetPadding(num->decbu->lbl, 0,0,0,0);
630 	AG_WidgetSetFocusable(num->decbu, 0);
631 
632 	AG_AddEvent(num, "widget-shown", OnShow, NULL);
633 	AG_SetEvent(num, "key-down", KeyDown, NULL);
634 	AG_SetEvent(num->incbu, "button-pushed", IncrementValue, "%p", num);
635 	AG_SetEvent(num->decbu, "button-pushed", DecrementValue, "%p", num);
636 	AG_SetEvent(num->input, "textbox-return", UpdateFromText, "%p,%i", num, 1);
637 	AG_SetEvent(num->input, "textbox-changed", UpdateFromText, "%p,%i", num, 0);
638 	AG_WidgetForwardFocus(num, num->input);
639 }
640 
641 void
AG_NumericalSizeHint(AG_Numerical * num,const char * text)642 AG_NumericalSizeHint(AG_Numerical *num, const char *text)
643 {
644 	AG_ObjectLock(num);
645 	AG_TextboxSizeHint(num->input, text);
646 	AG_ObjectUnlock(num);
647 }
648 
649 static void
SizeRequest(void * obj,AG_SizeReq * r)650 SizeRequest(void *obj, AG_SizeReq *r)
651 {
652 	AG_Numerical *num = obj;
653 	AG_SizeReq rChld, rInc, rDec;
654 
655 	AG_WidgetSizeReq(num->input, &rChld);
656 	r->w = rChld.w + num->wUnitSel + 4;
657 	r->h = MAX(rChld.h, num->hUnitSel);
658 
659 	AG_WidgetSizeReq(num->incbu, &rInc);
660 	AG_WidgetSizeReq(num->decbu, &rDec);
661 	r->w += MAX(rInc.w, rDec.w) + 4;
662 }
663 
664 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)665 SizeAllocate(void *obj, const AG_SizeAlloc *a)
666 {
667 	AG_Numerical *num = obj;
668 	AG_SizeAlloc aChld;
669 	int szBtn = a->h/2;
670 	int wUnitSel = num->wUnitSel + 4;
671 	int hUnitSel = num->hUnitSel;
672 
673 	if (a->h < 4 || a->w < szBtn+4)
674 		return (-1);
675 
676 	if (num->units != NULL) {
677 		if (wUnitSel > a->w - szBtn-4) {
678 			wUnitSel = a->w - szBtn-4;
679 		}
680 		if (hUnitSel > a->h) {
681 			hUnitSel = a->h;
682 		}
683 	} else {
684 		wUnitSel = 0;
685 		hUnitSel = 0;
686 	}
687 
688 	/* Size input textbox */
689 	aChld.x = 0;
690 	aChld.y = 0;
691 	aChld.w = a->w - wUnitSel - szBtn - 4;
692 	aChld.h = a->h;
693 	AG_WidgetSizeAlloc(num->input, &aChld);
694 	aChld.x += aChld.w + 2;
695 
696 	/* Size unit selector */
697 	if (num->units != NULL) {
698 		aChld.w = wUnitSel;
699 		aChld.h = a->h;
700 		AG_WidgetSizeAlloc(num->units, &aChld);
701 		aChld.x += aChld.w + 2;
702 	}
703 
704 	/* Size increment buttons */
705 	aChld.w = szBtn;
706 	aChld.h = szBtn;
707 	AG_WidgetSizeAlloc(num->incbu, &aChld);
708 	aChld.y += aChld.h;
709 	if (aChld.h*2 < a->h) {
710 		aChld.h++;
711 	}
712 	AG_WidgetSizeAlloc(num->decbu, &aChld);
713 	return (0);
714 }
715 
716 static void
Draw(void * obj)717 Draw(void *obj)
718 {
719 	AG_Numerical *num = obj;
720 
721 	AG_WidgetDraw(num->input);
722 	if (num->units != NULL) { AG_WidgetDraw(num->units); }
723 	AG_WidgetDraw(num->incbu);
724 	AG_WidgetDraw(num->decbu);
725 }
726 
727 /*
728  * Type-independent increment operation.
729  */
730 #undef ADD_INT
731 #define ADD_INT(TYPE) {							\
732 	TYPE v = *(TYPE *)value;					\
733 	if ((v + *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; }	\
734 	else if ((v + *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
735 	else { v += *(TYPE *)inc; }					\
736 	*(TYPE *)value = v;						\
737 }
738 #undef ADD_REAL
739 #define ADD_REAL(TYPE) {						\
740 	TYPE v = AG_Base2Unit((double)*(TYPE *)value, num->unit);	\
741 	if ((v + *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; } \
742 	else if ((v + *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
743 	else { v += *(TYPE *)inc; }					\
744 	*(TYPE *)value = AG_Unit2Base((double)v, num->unit);		\
745 }
746 #undef ADD_LDBL
747 #define ADD_LDBL(TYPE) {						\
748 	TYPE v;								\
749 	v = AG_Base2UnitLDBL(*(TYPE *)value, num->unit);		\
750 	if ((v + *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; }	\
751 	else if ((v + *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
752 	else { v += *(TYPE *)inc; }					\
753 	*(TYPE *)value = AG_Unit2BaseLDBL((long double)v, num->unit);	\
754 }
755 void
AG_NumericalIncrement(AG_Numerical * num)756 AG_NumericalIncrement(AG_Numerical *num)
757 {
758 	AG_Variable *valueb, *minb, *maxb, *incb;
759 	void *value, *min, *max, *inc;
760 
761 	AG_ObjectLock(num);
762 	valueb = AG_GetVariable(num, "value", &value);
763 	minb = AG_GetVariable(num, "min", &min);
764 	maxb = AG_GetVariable(num, "max", &max);
765 	incb = AG_GetVariable(num, "inc", &inc);
766 
767 	switch (AG_VARIABLE_TYPE(valueb)) {
768 	case AG_VARIABLE_FLOAT:		ADD_REAL(float);	break;
769 	case AG_VARIABLE_DOUBLE:	ADD_REAL(double);	break;
770 #ifdef HAVE_LONG_DOUBLE
771 	case AG_VARIABLE_LONG_DOUBLE:	ADD_LDBL(long double);	break;
772 #endif
773 	case AG_VARIABLE_INT:		ADD_INT(int);		break;
774 	case AG_VARIABLE_UINT:		ADD_INT(Uint);		break;
775 	case AG_VARIABLE_UINT8:		ADD_INT(Uint8);		break;
776 	case AG_VARIABLE_SINT8:		ADD_INT(Sint8);		break;
777 	case AG_VARIABLE_UINT16:	ADD_INT(Uint16);	break;
778 	case AG_VARIABLE_SINT16:	ADD_INT(Sint16);	break;
779 	case AG_VARIABLE_UINT32:	ADD_INT(Uint32);	break;
780 	case AG_VARIABLE_SINT32:	ADD_INT(Sint32);	break;
781 #ifdef HAVE_64BIT
782 	case AG_VARIABLE_UINT64:	ADD_INT(Uint64);	break;
783 	case AG_VARIABLE_SINT64:	ADD_INT(Sint64);	break;
784 #endif
785 	default:						break;
786 	}
787 
788 	AG_PostEvent(NULL, num, "numerical-changed", NULL);
789 
790 	AG_UnlockVariable(valueb);
791 	AG_UnlockVariable(minb);
792 	AG_UnlockVariable(maxb);
793 	AG_UnlockVariable(incb);
794 
795 	AG_NumericalUpdate(num);
796 	AG_ObjectUnlock(num);
797 }
798 #undef ADD_INT
799 #undef ADD_REAL
800 #undef ADD_LDBL
801 
802 /*
803  * Type-independent decrement operation.
804  */
805 #undef SUB_INT
806 #define SUB_INT(TYPE) {							\
807 	TYPE v = *(TYPE *)value;					\
808 	if ((v - *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; }	\
809 	else if ((v - *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
810 	else { v -= *(TYPE *)inc; }					\
811 	*(TYPE *)value = v;						\
812 }
813 #undef SUB_REAL
814 #define SUB_REAL(TYPE) {						\
815 	TYPE v = AG_Base2Unit((double)*(TYPE *)value, num->unit);	\
816 	if ((v - *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; }	\
817 	else if ((v - *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
818 	else { v -= *(TYPE *)inc; }					\
819 	*(TYPE *)value = AG_Unit2Base((double)v, num->unit);		\
820 }
821 #undef SUB_LDBL
822 #define SUB_LDBL(TYPE) {						\
823 	TYPE v = AG_Base2UnitLDBL((long double)*(TYPE *)value, num->unit); \
824 	if ((v - *(TYPE *)inc) < *(TYPE *)min) { v = *(TYPE *)min; }	\
825 	else if ((v - *(TYPE *)inc) > *(TYPE *)max) { v = *(TYPE *)max; } \
826 	else { v -= *(TYPE *)inc; }					\
827 	*(TYPE *)value = AG_Unit2BaseLDBL((long double)v, num->unit);	\
828 }
829 void
AG_NumericalDecrement(AG_Numerical * num)830 AG_NumericalDecrement(AG_Numerical *num)
831 {
832 	AG_Variable *valueb, *minb, *maxb, *incb;
833 	void *value, *min, *max, *inc;
834 
835 	AG_ObjectLock(num);
836 	valueb = AG_GetVariable(num, "value", &value);
837 	minb = AG_GetVariable(num, "min", &min);
838 	maxb = AG_GetVariable(num, "max", &max);
839 	incb = AG_GetVariable(num, "inc", &inc);
840 
841 	switch (AG_VARIABLE_TYPE(valueb)) {
842 	case AG_VARIABLE_FLOAT:		SUB_REAL(float);	break;
843 	case AG_VARIABLE_DOUBLE:	SUB_REAL(double);	break;
844 #ifdef HAVE_LONG_DOUBLE
845 	case AG_VARIABLE_LONG_DOUBLE:	SUB_LDBL(long double);	break;
846 #endif
847 	case AG_VARIABLE_INT:		SUB_INT(int);		break;
848 	case AG_VARIABLE_UINT:		SUB_INT(Uint);		break;
849 	case AG_VARIABLE_UINT8:		SUB_INT(Uint8);		break;
850 	case AG_VARIABLE_SINT8:		SUB_INT(Sint8);		break;
851 	case AG_VARIABLE_UINT16:	SUB_INT(Uint16);	break;
852 	case AG_VARIABLE_SINT16:	SUB_INT(Sint16);	break;
853 	case AG_VARIABLE_UINT32:	SUB_INT(Uint32);	break;
854 	case AG_VARIABLE_SINT32:	SUB_INT(Sint32);	break;
855 #ifdef HAVE_64BIT
856 	case AG_VARIABLE_UINT64:	SUB_INT(Uint64);	break;
857 	case AG_VARIABLE_SINT64:	SUB_INT(Sint64);	break;
858 #endif
859 	default:						break;
860 	}
861 	AG_PostEvent(NULL, num, "numerical-changed", NULL);
862 
863 	AG_UnlockVariable(valueb);
864 	AG_UnlockVariable(minb);
865 	AG_UnlockVariable(maxb);
866 	AG_UnlockVariable(incb);
867 
868 	AG_NumericalUpdate(num);
869 	AG_ObjectUnlock(num);
870 }
871 #undef SUB_INT
872 #undef SUB_REAL
873 #undef SUB_LDBL
874 
875 void
AG_NumericalSetPrecision(AG_Numerical * num,const char * mode,int precision)876 AG_NumericalSetPrecision(AG_Numerical *num, const char *mode,
877     int precision)
878 {
879 	AG_ObjectLock(num);
880 	num->format[0] = '%';
881 	num->format[1] = '.';
882 	num->format[2] = '\0';
883 	StrlcatInt(num->format, precision, sizeof(num->format));
884 	Strlcat(num->format, mode, sizeof(num->format));
885 	AG_NumericalUpdate(num);
886 	AG_ObjectUnlock(num);
887 }
888 
889 void
AG_NumericalSelectUnit(AG_Numerical * num,const char * uname)890 AG_NumericalSelectUnit(AG_Numerical *num, const char *uname)
891 {
892 	AG_TlistItem *it;
893 
894 	AG_ObjectLock(num);
895 	AG_ObjectLock(num->units->list);
896 	AG_TlistDeselectAll(num->units->list);
897 	TAILQ_FOREACH(it, &num->units->list->items, items) {
898 		const AG_Unit *unit = it->p1;
899 
900 		if (strcmp(unit->key, uname) == 0) {
901 			it->selected++;
902 			num->unit = unit;
903 			UpdateUnitSelector(num);
904 			break;
905 		}
906 	}
907 	AG_ObjectUnlock(num->units->list);
908 	AG_ObjectUnlock(num);
909 }
910 
911 void
AG_NumericalSetWriteable(AG_Numerical * num,int writeable)912 AG_NumericalSetWriteable(AG_Numerical *num, int writeable)
913 {
914 	AG_ObjectLock(num);
915 	num->writeable = writeable;
916 	if (writeable) {
917 		AG_WidgetEnable(num->incbu);
918 		AG_WidgetEnable(num->decbu);
919 		AG_WidgetEnable(num->input);
920 	} else {
921 		AG_WidgetDisable(num->incbu);
922 		AG_WidgetDisable(num->decbu);
923 		AG_WidgetDisable(num->input);
924 	}
925 	AG_ObjectUnlock(num);
926 }
927 
928 /* Convert the bound value to a float. */
929 float
AG_NumericalGetFlt(AG_Numerical * num)930 AG_NumericalGetFlt(AG_Numerical *num)
931 {
932 	AG_Variable *bValue;
933 	void *value;
934 
935 	bValue = AG_GetVariable(num, "value", &value);
936 	switch (AG_VARIABLE_TYPE(bValue)) {
937 	case AG_VARIABLE_FLOAT:		return *(float *)value;
938 	case AG_VARIABLE_DOUBLE:	return (float)(*(double *)value);
939 #ifdef HAVE_LONG_DOUBLE
940 	case AG_VARIABLE_LONG_DOUBLE:	return (float)(*(long double *)value);
941 #endif
942 	case AG_VARIABLE_INT:		return (float)(*(int *)value);
943 	case AG_VARIABLE_UINT:		return (float)(*(Uint *)value);
944 	case AG_VARIABLE_UINT8:		return (float)(*(Uint8 *)value);
945 	case AG_VARIABLE_UINT16:	return (float)(*(Uint16 *)value);
946 	case AG_VARIABLE_UINT32:	return (float)(*(Uint32 *)value);
947 	case AG_VARIABLE_SINT8:		return (float)(*(Sint8 *)value);
948 	case AG_VARIABLE_SINT16:	return (float)(*(Sint16 *)value);
949 	case AG_VARIABLE_SINT32:	return (float)(*(Sint32 *)value);
950 #ifdef HAVE_64BIT
951 	case AG_VARIABLE_UINT64:	return (float)(*(Uint64 *)value);
952 	case AG_VARIABLE_SINT64:	return (float)(*(Sint64 *)value);
953 #endif
954 	default:			return (0.0f);
955 	}
956 }
957 
958 /* Convert the bound value to a double. */
959 double
AG_NumericalGetDbl(AG_Numerical * num)960 AG_NumericalGetDbl(AG_Numerical *num)
961 {
962 	AG_Variable *bValue;
963 	void *value;
964 
965 	bValue = AG_GetVariable(num, "value", &value);
966 	switch (AG_VARIABLE_TYPE(bValue)) {
967 	case AG_VARIABLE_FLOAT:		return (double)(*(float *)value);
968 	case AG_VARIABLE_DOUBLE:	return *(double *)value;
969 #ifdef HAVE_LONG_DOUBLE
970 	case AG_VARIABLE_LONG_DOUBLE:	return (double)(*(long double *)value);
971 #endif
972 	case AG_VARIABLE_INT:		return (double)(*(int *)value);
973 	case AG_VARIABLE_UINT:		return (double)(*(Uint *)value);
974 	case AG_VARIABLE_UINT8:		return (double)(*(Uint8 *)value);
975 	case AG_VARIABLE_UINT16:	return (double)(*(Uint16 *)value);
976 	case AG_VARIABLE_UINT32:	return (double)(*(Uint32 *)value);
977 	case AG_VARIABLE_SINT8:		return (double)(*(Sint8 *)value);
978 	case AG_VARIABLE_SINT16:	return (double)(*(Sint16 *)value);
979 	case AG_VARIABLE_SINT32:	return (double)(*(Sint32 *)value);
980 #ifdef HAVE_64BIT
981 	case AG_VARIABLE_UINT64:	return (double)(*(Uint64 *)value);
982 	case AG_VARIABLE_SINT64:	return (double)(*(Sint64 *)value);
983 #endif
984 	default:			return (0.0);
985 	}
986 }
987 
988 #ifdef HAVE_LONG_DOUBLE
989 /* Convert the bound value to a long double. */
990 long double
AG_NumericalGetLdbl(AG_Numerical * num)991 AG_NumericalGetLdbl(AG_Numerical *num)
992 {
993 	AG_Variable *bValue;
994 	void *value;
995 
996 	bValue = AG_GetVariable(num, "value", &value);
997 	switch (AG_VARIABLE_TYPE(bValue)) {
998 	case AG_VARIABLE_FLOAT:		return (long double)(*(float *)value);
999 	case AG_VARIABLE_DOUBLE:	return (long double)(*(double *)value);
1000 	case AG_VARIABLE_LONG_DOUBLE:	return *(long double *)value;
1001 	case AG_VARIABLE_INT:		return (long double)(*(int *)value);
1002 	case AG_VARIABLE_UINT:		return (long double)(*(Uint *)value);
1003 	case AG_VARIABLE_UINT8:		return (long double)(*(Uint8 *)value);
1004 	case AG_VARIABLE_UINT16:	return (long double)(*(Uint16 *)value);
1005 	case AG_VARIABLE_UINT32:	return (long double)(*(Uint32 *)value);
1006 	case AG_VARIABLE_SINT8:		return (long double)(*(Sint8 *)value);
1007 	case AG_VARIABLE_SINT16:	return (long double)(*(Sint16 *)value);
1008 	case AG_VARIABLE_SINT32:	return (long double)(*(Sint32 *)value);
1009 #ifdef HAVE_64BIT
1010 	case AG_VARIABLE_UINT64:	return (long double)(*(Uint64 *)value);
1011 	case AG_VARIABLE_SINT64:	return (long double)(*(Sint64 *)value);
1012 #endif
1013 	default:			return (0.0L);
1014 	}
1015 }
1016 #endif /* HAVE_LONG_DOUBLE */
1017 
1018 /* Convert the bound value to a natural integer. */
1019 int
AG_NumericalGetInt(AG_Numerical * num)1020 AG_NumericalGetInt(AG_Numerical *num)
1021 {
1022 	AG_Variable *bValue;
1023 	void *value;
1024 
1025 	bValue = AG_GetVariable(num, "value", &value);
1026 	switch (AG_VARIABLE_TYPE(bValue)) {
1027 	case AG_VARIABLE_FLOAT:		return (int)(*(float *)value);
1028 	case AG_VARIABLE_DOUBLE:	return (int)(*(double *)value);
1029 #ifdef HAVE_LONG_DOUBLE
1030 	case AG_VARIABLE_LONG_DOUBLE:	return (int)(*(long double *)value);
1031 #endif
1032 	case AG_VARIABLE_INT:		return *(int *)value;
1033 	case AG_VARIABLE_UINT:		return (int)(*(Uint *)value);
1034 	case AG_VARIABLE_UINT8:		return (int)(*(Uint8 *)value);
1035 	case AG_VARIABLE_UINT16:	return (int)(*(Uint16 *)value);
1036 	case AG_VARIABLE_UINT32:	return (int)(*(Uint32 *)value);
1037 	case AG_VARIABLE_SINT8:		return (int)(*(Sint8 *)value);
1038 	case AG_VARIABLE_SINT16:	return (int)(*(Sint16 *)value);
1039 	case AG_VARIABLE_SINT32:	return (int)(*(Sint32 *)value);
1040 #ifdef HAVE_64BIT
1041 	case AG_VARIABLE_UINT64:	return (int)(*(Uint64 *)value);
1042 	case AG_VARIABLE_SINT64:	return (int)(*(Sint64 *)value);
1043 #endif
1044 	default:			return (0);
1045 	}
1046 }
1047 
1048 /* Convert the bound value to a 32-bit integer. */
1049 Uint32
AG_NumericalGetUint32(AG_Numerical * num)1050 AG_NumericalGetUint32(AG_Numerical *num)
1051 {
1052 	AG_Variable *bValue;
1053 	void *value;
1054 
1055 	bValue = AG_GetVariable(num, "value", &value);
1056 	switch (AG_VARIABLE_TYPE(bValue)) {
1057 	case AG_VARIABLE_FLOAT:		return (Uint32)(*(float *)value);
1058 	case AG_VARIABLE_DOUBLE:	return (Uint32)(*(double *)value);
1059 #ifdef HAVE_LONG_DOUBLE
1060 	case AG_VARIABLE_LONG_DOUBLE:	return (Uint32)(*(long double *)value);
1061 #endif
1062 	case AG_VARIABLE_INT:		return (Uint32)(*(int *)value);
1063 	case AG_VARIABLE_UINT:		return (Uint32)(*(Uint *)value);
1064 	case AG_VARIABLE_UINT8:		return (Uint32)(*(Uint8 *)value);
1065 	case AG_VARIABLE_UINT16:	return (Uint32)(*(Uint16 *)value);
1066 	case AG_VARIABLE_UINT32:	return *(Uint32 *)value;
1067 	case AG_VARIABLE_SINT8:		return (Uint32)(*(Sint8 *)value);
1068 	case AG_VARIABLE_SINT16:	return (Uint32)(*(Sint16 *)value);
1069 	case AG_VARIABLE_SINT32:	return (Uint32)(*(Sint32 *)value);
1070 #ifdef HAVE_64BIT
1071 	case AG_VARIABLE_UINT64:	return (Uint32)(*(Uint64 *)value);
1072 	case AG_VARIABLE_SINT64:	return (Uint32)(*(Sint64 *)value);
1073 #endif
1074 	default:			return (0UL);
1075 	}
1076 }
1077 
1078 #ifdef HAVE_64BIT
1079 /* Convert the bound value to a 64-bit integer. */
1080 Uint64
AG_NumericalGetUint64(AG_Numerical * num)1081 AG_NumericalGetUint64(AG_Numerical *num)
1082 {
1083 	AG_Variable *bValue;
1084 	void *value;
1085 
1086 	bValue = AG_GetVariable(num, "value", &value);
1087 	switch (AG_VARIABLE_TYPE(bValue)) {
1088 	case AG_VARIABLE_FLOAT:		return (Uint64)(*(float *)value);
1089 	case AG_VARIABLE_DOUBLE:	return (Uint64)(*(double *)value);
1090 #ifdef HAVE_LONG_DOUBLE
1091 	case AG_VARIABLE_LONG_DOUBLE:	return (Uint64)(*(long double *)value);
1092 #endif
1093 	case AG_VARIABLE_INT:		return (Uint64)(*(int *)value);
1094 	case AG_VARIABLE_UINT:		return (Uint64)(*(Uint *)value);
1095 	case AG_VARIABLE_UINT8:		return (Uint64)(*(Uint8 *)value);
1096 	case AG_VARIABLE_UINT16:	return (Uint64)(*(Uint16 *)value);
1097 	case AG_VARIABLE_UINT32:	return (Uint64)(*(Uint32 *)value);
1098 	case AG_VARIABLE_UINT64:	return *(Uint64 *)value;
1099 	case AG_VARIABLE_SINT8:		return (Uint64)(*(Sint8 *)value);
1100 	case AG_VARIABLE_SINT16:	return (Uint64)(*(Sint16 *)value);
1101 	case AG_VARIABLE_SINT32:	return (Uint64)(*(Sint32 *)value);
1102 	case AG_VARIABLE_SINT64:	return (Uint64)(*(Sint64 *)value);
1103 	default:			return (0ULL);
1104 	}
1105 }
1106 #endif /* HAVE_64BIT */
1107 
1108 #ifdef AG_LEGACY
1109 void
AG_NumericalSetIncrement(AG_Numerical * num,double inc)1110 AG_NumericalSetIncrement(AG_Numerical *num, double inc)
1111 {
1112 	AG_Variable *V;
1113 
1114 	AG_ObjectLock(num);
1115 
1116 	if ((V = AG_GetVariableLocked(num, "value")) == NULL) {
1117 		goto out;
1118 	}
1119 	switch (AG_VARIABLE_TYPE(V)) {
1120 	case AG_VARIABLE_INT:    AG_SetInt(num, "inc", (int)inc);	break;
1121 	case AG_VARIABLE_UINT:   AG_SetUint(num, "inc", (Uint)inc);	break;
1122 	case AG_VARIABLE_UINT8:  AG_SetUint8(num, "inc", (Uint8)inc);	break;
1123 	case AG_VARIABLE_SINT8:  AG_SetSint8(num, "inc", (Sint8)inc);	break;
1124 	case AG_VARIABLE_UINT16: AG_SetUint16(num, "inc", (Uint16)inc);	break;
1125 	case AG_VARIABLE_SINT16: AG_SetSint16(num, "inc", (Sint16)inc);	break;
1126 	case AG_VARIABLE_UINT32: AG_SetUint32(num, "inc", (Uint32)inc);	break;
1127 	case AG_VARIABLE_SINT32: AG_SetSint32(num, "inc", (Sint32)inc);	break;
1128 #ifdef HAVE_64BIT
1129 	case AG_VARIABLE_UINT64: AG_SetUint64(num, "inc", (Uint64)inc);	break;
1130 	case AG_VARIABLE_SINT64: AG_SetSint64(num, "inc", (Sint64)inc);	break;
1131 #endif
1132 	case AG_VARIABLE_FLOAT:  AG_SetFloat(num, "inc", (float)inc);	break;
1133 	case AG_VARIABLE_DOUBLE: AG_SetDouble(num, "inc", inc);		break;
1134 #ifdef HAVE_LONG_DOUBLE
1135 	case AG_VARIABLE_LONG_DOUBLE: AG_SetLongDouble(num, "inc", (long double)inc); break;
1136 #endif
1137 	default:							break;
1138 	}
1139 	AG_UnlockVariable(V);
1140 out:
1141 	AG_ObjectUnlock(num);
1142 }
1143 void
AG_NumericalSetRangeInt(AG_Numerical * num,int min,int max)1144 AG_NumericalSetRangeInt(AG_Numerical *num, int min, int max)
1145 {
1146 	AG_SetInt(num, "min", min);
1147 	AG_SetInt(num, "max", max);
1148 }
1149 void
AG_NumericalSetRangeFlt(AG_Numerical * num,float min,float max)1150 AG_NumericalSetRangeFlt(AG_Numerical *num, float min, float max)
1151 {
1152 	AG_SetFloat(num, "min", min);
1153 	AG_SetFloat(num, "max", max);
1154 }
1155 void
AG_NumericalSetRangeDbl(AG_Numerical * num,double min,double max)1156 AG_NumericalSetRangeDbl(AG_Numerical *num, double min, double max)
1157 {
1158 	AG_SetDouble(num, "min", min);
1159 	AG_SetDouble(num, "max", max);
1160 }
AG_NumericalSetMin(AG_Numerical * num,double min)1161 void AG_NumericalSetMin(AG_Numerical *num, double min)
1162 {
1163 	AG_SetDouble(num, "min", min);
1164 }
AG_NumericalSetMax(AG_Numerical * num,double max)1165 void AG_NumericalSetMax(AG_Numerical *num, double max)
1166 {
1167 	AG_SetDouble(num, "max", max);
1168 }
1169 #endif /* AG_LEGACY */
1170 
1171 AG_WidgetClass agNumericalClass = {
1172 	{
1173 		"Agar(Widget:Numerical)",
1174 		sizeof(AG_Numerical),
1175 		{ 0,0 },
1176 		Init,
1177 		NULL,			/* free */
1178 		NULL,			/* destroy */
1179 		NULL,			/* load */
1180 		NULL,			/* save */
1181 		NULL			/* edit */
1182 	},
1183 	Draw,
1184 	SizeRequest,
1185 	SizeAllocate
1186 };
1187 
1188