1 /*
2 * Copyright (c) 2004-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 #include <agar/core/core.h>
27 #include <agar/gui/mfspinbutton.h>
28 #include <agar/gui/window.h>
29
30 #include <string.h>
31
32 static void SelectedUnit(AG_Event *);
33 static void InitUnitSystem(AG_MFSpinbutton *, const char *);
34
35 AG_MFSpinbutton *
AG_MFSpinbuttonNew(void * parent,Uint flags,const char * unit,const char * sep,const char * label)36 AG_MFSpinbuttonNew(void *parent, Uint flags, const char *unit, const char *sep,
37 const char *label)
38 {
39 AG_MFSpinbutton *fsu;
40
41 fsu = Malloc(sizeof(AG_MFSpinbutton));
42 AG_ObjectInit(fsu, &agMFSpinbuttonClass);
43 fsu->sep = sep;
44 fsu->flags |= flags;
45 if (!(flags & AG_MFSPINBUTTON_NOHFILL)) { AG_ExpandHoriz(fsu); }
46 if ( flags & AG_MFSPINBUTTON_VFILL) { AG_ExpandVert(fsu); }
47
48 if (label != NULL) {
49 AG_TextboxSetLabelS(fsu->input, label);
50 }
51 if (unit != NULL) {
52 fsu->units = AG_UComboNew(fsu, 0);
53 AG_SetEvent(fsu->units, "ucombo-selected",
54 SelectedUnit, "%p", fsu);
55 InitUnitSystem(fsu, unit);
56 AG_WidgetSetFocusable(fsu->units, 0);
57 }
58
59 AG_ObjectAttach(parent, fsu);
60 return (fsu);
61 }
62
63 static Uint32
UpdateTimeout(AG_Timer * to,AG_Event * event)64 UpdateTimeout(AG_Timer *to, AG_Event *event)
65 {
66 AG_MFSpinbutton *fsu = AG_SELF();
67
68 if (!AG_WidgetIsFocused(fsu)) {
69 AG_MFSpinbuttonUpdate(fsu);
70 }
71 return (to->ival);
72 }
73
74 static void
OnShow(AG_Event * event)75 OnShow(AG_Event *event)
76 {
77 AG_MFSpinbutton *fsu = AG_SELF();
78 AG_Variable *Vx, *Vy;
79
80 if ((fsu->flags & AG_MFSPINBUTTON_EXCL) == 0) {
81 AG_AddTimer(fsu, &fsu->updateTo, 250, UpdateTimeout, NULL);
82 }
83 if ((Vx = AG_GetVariableLocked(fsu, "xvalue")) == NULL) {
84 fsu->xvalue = 0.0;
85 Vx = AG_BindDouble(fsu, "xvalue", &fsu->xvalue);
86 if (Vx == NULL) {
87 return;
88 }
89 AG_LockVariable(Vx);
90 }
91 if ((Vy = AG_GetVariableLocked(fsu, "yvalue")) == NULL) {
92 fsu->yvalue = 0.0;
93 Vy = AG_BindDouble(fsu, "yvalue", &fsu->yvalue);
94 if (Vy == NULL) {
95 return;
96 }
97 AG_LockVariable(Vy);
98 }
99 if (Vx->type != Vy->type) {
100 AG_FatalError("MFSpinbutton xvalue/yvalue types disagree");
101 }
102 switch (Vx->type) {
103 case AG_VARIABLE_P_FLOAT:
104 if (!AG_Defined(fsu, "min")) {
105 fsu->minFlt = -AG_FLT_MAX+1;
106 AG_BindFloat(fsu, "min", &fsu->minFlt);
107 }
108 if (!AG_Defined(fsu, "max")) {
109 fsu->maxFlt = +AG_FLT_MAX-1;
110 AG_BindFloat(fsu, "max", &fsu->maxFlt);
111 }
112 break;
113 case AG_VARIABLE_P_DOUBLE:
114 if (!AG_Defined(fsu, "min")) {
115 fsu->min = -AG_DBL_MAX+1;
116 AG_BindDouble(fsu, "min", &fsu->min);
117 }
118 if (!AG_Defined(fsu, "max")) {
119 fsu->max = +AG_DBL_MAX-1;
120 AG_BindDouble(fsu, "max", &fsu->max);
121 }
122 break;
123 default:
124 break;
125 }
126 AG_MFSpinbuttonUpdate(fsu);
127 }
128
129 /* Update the input text from the binding values. */
130 void
AG_MFSpinbuttonUpdate(AG_MFSpinbutton * fsu)131 AG_MFSpinbuttonUpdate(AG_MFSpinbutton *fsu)
132 {
133 char sx[64], sy[64], s[128];
134 AG_Variable *valueb;
135 void *value;
136
137 /* Get X value */
138 valueb = AG_GetVariable(fsu, "xvalue", &value);
139 switch (AG_VARIABLE_TYPE(valueb)) {
140 case AG_VARIABLE_DOUBLE:
141 Snprintf(sx, sizeof(sx), fsu->format,
142 AG_Base2Unit(*(double *)value, fsu->unit));
143 break;
144 case AG_VARIABLE_FLOAT:
145 Snprintf(sx, sizeof(sx), fsu->format,
146 AG_Base2Unit(*(float *)value, fsu->unit));
147 break;
148 case AG_VARIABLE_INT:
149 StrlcpyInt(sx, *(int *)value, sizeof(sx));
150 break;
151 case AG_VARIABLE_UINT:
152 StrlcpyUint(sx, *(Uint *)value, sizeof(sx));
153 break;
154 case AG_VARIABLE_UINT8:
155 StrlcpyUint(sx, (unsigned)(*(Uint8 *)value), sizeof(sx));
156 break;
157 case AG_VARIABLE_SINT8:
158 StrlcpyInt(sx, (int)(*(Sint8 *)value), sizeof(sx));
159 break;
160 case AG_VARIABLE_UINT16:
161 StrlcpyUint(sx, (unsigned)(*(Uint16 *)value), sizeof(sx));
162 break;
163 case AG_VARIABLE_SINT16:
164 StrlcpyInt(sx, (int)(*(Sint16 *)value), sizeof(sx));
165 break;
166 case AG_VARIABLE_UINT32:
167 StrlcpyUint(sx, (unsigned)(*(Uint32 *)value), sizeof(sx));
168 break;
169 case AG_VARIABLE_SINT32:
170 StrlcpyInt(sx, (int)(*(Sint32 *)value), sizeof(sx));
171 break;
172 default:
173 break;
174 }
175 AG_UnlockVariable(valueb);
176
177 /* Get Y value */
178 valueb = AG_GetVariable(fsu, "yvalue", &value);
179 switch (AG_VARIABLE_TYPE(valueb)) {
180 case AG_VARIABLE_DOUBLE:
181 Snprintf(sy, sizeof(sy), fsu->format,
182 AG_Base2Unit(*(double *)value, fsu->unit));
183 break;
184 case AG_VARIABLE_FLOAT:
185 Snprintf(sy, sizeof(sy), fsu->format,
186 AG_Base2Unit(*(float *)value, fsu->unit));
187 break;
188 case AG_VARIABLE_INT:
189 StrlcpyInt(sy, *(int *)value, sizeof(sy));
190 break;
191 case AG_VARIABLE_UINT:
192 StrlcpyUint(sy, *(Uint *)value, sizeof(sy));
193 break;
194 case AG_VARIABLE_UINT8:
195 StrlcpyUint(sy, (unsigned)(*(Uint8 *)value), sizeof(sy));
196 break;
197 case AG_VARIABLE_SINT8:
198 StrlcpyInt(sy, (int)(*(Sint8 *)value), sizeof(sy));
199 break;
200 case AG_VARIABLE_UINT16:
201 StrlcpyUint(sy, (unsigned)(*(Uint16 *)value), sizeof(sy));
202 break;
203 case AG_VARIABLE_SINT16:
204 StrlcpyInt(sy, (int)(*(Sint16 *)value), sizeof(sy));
205 break;
206 case AG_VARIABLE_UINT32:
207 StrlcpyUint(sy, (unsigned)(*(Uint32 *)value), sizeof(sy));
208 break;
209 case AG_VARIABLE_SINT32:
210 StrlcpyInt(sy, (int)(*(Sint32 *)value), sizeof(sy));
211 break;
212 default:
213 break;
214 }
215 Strlcpy(s, sx, sizeof(s));
216 Strlcat(s, fsu->sep, sizeof(s));
217 Strlcat(s, sy, sizeof(s));
218 if (strcmp(s, fsu->inTxt) != 0) {
219 AG_TextboxSetString(fsu->input, s);
220 }
221 AG_UnlockVariable(valueb);
222 }
223
224 static void
KeyDown(AG_Event * event)225 KeyDown(AG_Event *event)
226 {
227 AG_MFSpinbutton *fsu = AG_SELF();
228 int keysym = AG_INT(1);
229
230 switch (keysym) {
231 case AG_KEY_LEFT:
232 AG_MFSpinbuttonAddValue(fsu, "xvalue", -fsu->inc);
233 break;
234 case AG_KEY_RIGHT:
235 AG_MFSpinbuttonAddValue(fsu, "xvalue", fsu->inc);
236 break;
237 case AG_KEY_UP:
238 AG_MFSpinbuttonAddValue(fsu, "yvalue", -fsu->inc);
239 break;
240 case AG_KEY_DOWN:
241 AG_MFSpinbuttonAddValue(fsu, "yvalue", fsu->inc);
242 break;
243 default:
244 break;
245 }
246 }
247
248 static void
TextChanged(AG_Event * event)249 TextChanged(AG_Event *event)
250 {
251 AG_MFSpinbutton *fsu = AG_PTR(1);
252 int unfocus = AG_INT(2);
253 char inTxt[128];
254 char *tp = &inTxt[0], *s;
255
256 AG_ObjectLock(fsu);
257 Strlcpy(inTxt, fsu->inTxt, sizeof(inTxt));
258
259 if ((s = AG_Strsep(&tp, fsu->sep)) != NULL) {
260 AG_MFSpinbuttonSetValue(fsu, "xvalue",
261 strtod(s, NULL)*fsu->unit->divider);
262 }
263 if ((s = AG_Strsep(&tp, fsu->sep)) != NULL) {
264 AG_MFSpinbuttonSetValue(fsu, "yvalue",
265 strtod(s, NULL)*fsu->unit->divider);
266 }
267
268 AG_PostEvent(NULL, fsu, "mfspinbutton-return", NULL);
269
270 if (unfocus)
271 AG_WidgetUnfocus(fsu->input);
272
273 AG_MFSpinbuttonUpdate(fsu);
274 AG_ObjectUnlock(fsu);
275 }
276
277 static void
DecrementY(AG_Event * event)278 DecrementY(AG_Event *event)
279 {
280 AG_MFSpinbutton *fsu = AG_PTR(1);
281
282 AG_ObjectLock(fsu);
283 AG_MFSpinbuttonAddValue(fsu, "yvalue", -fsu->inc);
284 AG_ObjectUnlock(fsu);
285 }
286
287 static void
IncrementY(AG_Event * event)288 IncrementY(AG_Event *event)
289 {
290 AG_MFSpinbutton *fsu = AG_PTR(1);
291
292 AG_ObjectLock(fsu);
293 AG_MFSpinbuttonAddValue(fsu, "yvalue", fsu->inc);
294 AG_ObjectUnlock(fsu);
295 }
296
297 static void
DecrementX(AG_Event * event)298 DecrementX(AG_Event *event)
299 {
300 AG_MFSpinbutton *fsu = AG_PTR(1);
301
302 AG_ObjectLock(fsu);
303 AG_MFSpinbuttonAddValue(fsu, "xvalue", -fsu->inc);
304 AG_ObjectUnlock(fsu);
305 }
306
307 static void
IncrementX(AG_Event * event)308 IncrementX(AG_Event *event)
309 {
310 AG_MFSpinbutton *fsu = AG_PTR(1);
311
312 AG_ObjectLock(fsu);
313 AG_MFSpinbuttonAddValue(fsu, "xvalue", fsu->inc);
314 AG_ObjectUnlock(fsu);
315 }
316
317 /* Widget must be locked. */
318 static void
UpdateUnitSelector(AG_MFSpinbutton * fsu)319 UpdateUnitSelector(AG_MFSpinbutton *fsu)
320 {
321 AG_ButtonTextS(fsu->units->button, AG_UnitAbbr(fsu->unit));
322 AG_MFSpinbuttonUpdate(fsu);
323 }
324
325 static void
SelectedUnit(AG_Event * event)326 SelectedUnit(AG_Event *event)
327 {
328 AG_MFSpinbutton *fsu = AG_PTR(1);
329 AG_TlistItem *ti = AG_PTR(2);
330
331 AG_ObjectLock(fsu);
332 fsu->unit = (const AG_Unit *)ti->p1;
333 UpdateUnitSelector(fsu);
334 AG_ObjectUnlock(fsu);
335 }
336
337 static void
InitUnitSystem(AG_MFSpinbutton * fsu,const char * unit_key)338 InitUnitSystem(AG_MFSpinbutton *fsu, const char *unit_key)
339 {
340 const AG_Unit *unit = NULL;
341 const AG_Unit *ugroup = NULL;
342 int found = 0;
343 int i;
344
345 for (i = 0; i < agnUnitGroups; i++) {
346 ugroup = agUnitGroups[i];
347 for (unit = &ugroup[0]; unit->key != NULL; unit++) {
348 if (strcmp(unit->key, unit_key) == 0) {
349 found++;
350 break;
351 }
352 }
353 if (found)
354 break;
355 }
356 if (!found) {
357 AG_FatalError("AG_MFSpinbutton: No such unit: %s", unit_key);
358 }
359 fsu->unit = unit;
360 UpdateUnitSelector(fsu);
361
362 AG_ObjectLock(fsu->units->list);
363 AG_TlistDeselectAll(fsu->units->list);
364 for (unit = &ugroup[0]; unit->key != NULL; unit++) {
365 AG_TlistItem *it;
366
367 it = AG_TlistAddPtr(fsu->units->list, NULL, _(unit->name),
368 (void *)unit);
369 if (unit == fsu->unit)
370 it->selected++;
371 }
372 AG_TlistSizeHintLargest(fsu->units->list, 5);
373 AG_ObjectUnlock(fsu->units->list);
374 }
375
376 static void
Init(void * obj)377 Init(void *obj)
378 {
379 AG_MFSpinbutton *fsu = obj;
380 AG_Button *b[4];
381 int i;
382
383 WIDGET(fsu)->flags |= AG_WIDGET_FOCUSABLE|
384 AG_WIDGET_TABLE_EMBEDDABLE;
385
386 fsu->inc = 1.0;
387 fsu->writeable = 1;
388 fsu->sep = ",";
389 fsu->inTxt[0] = '\0';
390 Strlcpy(fsu->format, "%.02f", sizeof(fsu->format));
391
392 fsu->input = AG_TextboxNewS(fsu, AG_TEXTBOX_EXCL, NULL);
393 AG_TextboxBindASCII(fsu->input, fsu->inTxt, sizeof(fsu->inTxt));
394 AG_TextboxSizeHint(fsu->input, "888.88");
395
396 fsu->unit = AG_FindUnit("identity");
397 fsu->units = NULL;
398
399 fsu->xincbu = b[0] = AG_ButtonNewS(fsu, AG_BUTTON_REPEAT, _("+"));
400 fsu->xdecbu = b[1] = AG_ButtonNewS(fsu, AG_BUTTON_REPEAT, _("-"));
401 fsu->yincbu = b[2] = AG_ButtonNewS(fsu, AG_BUTTON_REPEAT, _("+"));
402 fsu->ydecbu = b[3] = AG_ButtonNewS(fsu, AG_BUTTON_REPEAT, _("-"));
403 AG_SetEvent(fsu->xincbu, "button-pushed", IncrementX, "%p", fsu);
404 AG_SetEvent(fsu->xdecbu, "button-pushed", DecrementX, "%p", fsu);
405 AG_SetEvent(fsu->yincbu, "button-pushed", IncrementY, "%p", fsu);
406 AG_SetEvent(fsu->ydecbu, "button-pushed", DecrementY, "%p", fsu);
407 for (i = 0; i < 4; i++) {
408 AG_ButtonSetPadding(b[i], 0,0,0,0);
409 AG_LabelSetPadding(b[i]->lbl, 0,0,0,0);
410 AG_WidgetSetFocusable(b[i], 0);
411 }
412
413 AG_InitTimer(&fsu->updateTo, "update", 0);
414
415 AG_AddEvent(fsu, "widget-shown", OnShow, NULL);
416 AG_SetEvent(fsu, "key-down", KeyDown, NULL);
417 AG_SetEvent(fsu->input, "textbox-return", TextChanged, "%p,%i",fsu,1);
418 AG_SetEvent(fsu->input, "textbox-changed", TextChanged, "%p,%i",fsu,0);
419 }
420
421 static void
SizeRequest(void * obj,AG_SizeReq * r)422 SizeRequest(void *obj, AG_SizeReq *r)
423 {
424 AG_MFSpinbutton *fsu = obj;
425 AG_SizeReq rChld, rYinc, rYdec;
426
427 AG_WidgetSizeReq(fsu->input, &rChld);
428 r->w = rChld.w;
429 r->h = rChld.h;
430 if (fsu->units != NULL) {
431 AG_WidgetSizeReq(fsu->units, &rChld);
432 r->w += rChld.w+4;
433 }
434 AG_WidgetSizeReq(fsu->xdecbu, &rChld);
435 r->w += rChld.w;
436 AG_WidgetSizeReq(fsu->xincbu, &rChld);
437 r->w += rChld.w;
438 AG_WidgetSizeReq(fsu->yincbu, &rYinc);
439 AG_WidgetSizeReq(fsu->ydecbu, &rYdec);
440 r->w += MAX(rYinc.w,rYdec.w);
441 }
442
443 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)444 SizeAllocate(void *obj, const AG_SizeAlloc *a)
445 {
446 AG_MFSpinbutton *fsu = obj;
447 int szBtn = a->h/2;
448 int wUnitBox = (fsu->units != NULL) ? 25 : 0;
449 int x = 0, y = 0;
450 AG_SizeAlloc aChld;
451
452 if (a->w < szBtn*3 + wUnitBox + 4)
453 return (-1);
454
455 /* Input textbox */
456 aChld.x = x;
457 aChld.y = y;
458 aChld.w = a->w - 2 - wUnitBox - 2 - szBtn*3;
459 aChld.h = a->h;
460 AG_WidgetSizeAlloc(fsu->input, &aChld);
461 x += aChld.w + 2;
462
463 /* Unit selector */
464 if (fsu->units != NULL) {
465 aChld.x = x;
466 aChld.y = y;
467 aChld.w = wUnitBox;
468 aChld.h = a->h;
469 AG_WidgetSizeAlloc(fsu->units, &aChld);
470 x += aChld.w + 2;
471 }
472
473 /* Increment buttons */
474 aChld.w = szBtn;
475 aChld.h = szBtn;
476 aChld.x = x;
477 aChld.y = y + szBtn/2;
478 AG_WidgetSizeAlloc(fsu->xdecbu, &aChld);
479 aChld.x = x + szBtn*2;
480 aChld.y = y + szBtn/2;
481 AG_WidgetSizeAlloc(fsu->xincbu, &aChld);
482 aChld.x = x + szBtn;
483 aChld.y = y;
484 AG_WidgetSizeAlloc(fsu->ydecbu, &aChld);
485 aChld.x = x + szBtn;
486 aChld.y = y + szBtn;
487 AG_WidgetSizeAlloc(fsu->yincbu, &aChld);
488 return (0);
489 }
490
491 static void
Draw(void * obj)492 Draw(void *obj)
493 {
494 AG_MFSpinbutton *fsu = obj;
495 AG_Variable *xvalueb, *yvalueb;
496 double *xvalue, *yvalue;
497
498 AG_WidgetDraw(fsu->input);
499 if (fsu->units != NULL) { AG_WidgetDraw(fsu->units); }
500 AG_WidgetDraw(fsu->xincbu);
501 AG_WidgetDraw(fsu->yincbu);
502 AG_WidgetDraw(fsu->xdecbu);
503 AG_WidgetDraw(fsu->ydecbu);
504
505 xvalueb = AG_GetVariable(fsu, "xvalue", &xvalue);
506 yvalueb = AG_GetVariable(fsu, "yvalue", &yvalue);
507
508 Snprintf(fsu->inTxt, sizeof(fsu->inTxt), fsu->format,
509 *xvalue/fsu->unit->divider,
510 *yvalue/fsu->unit->divider);
511
512 AG_UnlockVariable(xvalueb);
513 AG_UnlockVariable(yvalueb);
514 }
515
516 void
AG_MFSpinbuttonAddValue(AG_MFSpinbutton * fsu,const char * which,double inc)517 AG_MFSpinbuttonAddValue(AG_MFSpinbutton *fsu, const char *which, double inc)
518 {
519 AG_Variable *valueb, *minb, *maxb;
520 void *value;
521 double *min, *max;
522
523 AG_ObjectLock(fsu);
524
525 inc *= fsu->unit->divider;
526 valueb = AG_GetVariable(fsu, which, &value);
527 minb = AG_GetVariable(fsu, "min", &min);
528 maxb = AG_GetVariable(fsu, "max", &max);
529
530 switch (AG_VARIABLE_TYPE(valueb)) {
531 case AG_VARIABLE_DOUBLE:
532 *(double *)value = *(double *)value+inc < *min ? *min :
533 *(double *)value+inc > *max ? *max :
534 *(double *)value+inc;
535 break;
536 case AG_VARIABLE_FLOAT:
537 *(float *)value = *(float *)value+inc < *min ? *min :
538 *(float *)value+inc > *max ? *max :
539 *(float *)value+inc;
540 break;
541 default:
542 break;
543 }
544 AG_PostEvent(NULL, fsu, "mfspinbutton-changed", "%s", which);
545
546 AG_UnlockVariable(valueb);
547 AG_UnlockVariable(minb);
548 AG_UnlockVariable(maxb);
549
550 AG_MFSpinbuttonUpdate(fsu);
551 AG_ObjectUnlock(fsu);
552 }
553
554 void
AG_MFSpinbuttonSetValue(AG_MFSpinbutton * fsu,const char * which,double nvalue)555 AG_MFSpinbuttonSetValue(AG_MFSpinbutton *fsu, const char *which,
556 double nvalue)
557 {
558 AG_Variable *valueb, *minb, *maxb;
559 void *value;
560 double *min, *max;
561
562 AG_ObjectLock(fsu);
563
564 valueb = AG_GetVariable(fsu, which, &value);
565 minb = AG_GetVariable(fsu, "min", &min);
566 maxb = AG_GetVariable(fsu, "max", &max);
567
568 switch (AG_VARIABLE_TYPE(valueb)) {
569 case AG_VARIABLE_DOUBLE:
570 *(double *)value = nvalue < *min ? *min :
571 nvalue > *max ? *max :
572 nvalue;
573 break;
574 case AG_VARIABLE_FLOAT:
575 *(float *)value = nvalue < *min ? *min :
576 nvalue > *max ? *max :
577 (float)nvalue;
578 break;
579 default:
580 break;
581 }
582 AG_PostEvent(NULL, fsu, "mfspinbutton-changed", "%s", which);
583
584 AG_UnlockVariable(valueb);
585 AG_UnlockVariable(minb);
586 AG_UnlockVariable(maxb);
587
588 AG_MFSpinbuttonUpdate(fsu);
589 AG_ObjectUnlock(fsu);
590 }
591
592 void
AG_MFSpinbuttonSetMin(AG_MFSpinbutton * fsu,double nmin)593 AG_MFSpinbuttonSetMin(AG_MFSpinbutton *fsu, double nmin)
594 {
595 AG_Variable *minb;
596 void *min;
597
598 AG_ObjectLock(fsu);
599 minb = AG_GetVariable(fsu, "min", &min);
600 switch (AG_VARIABLE_TYPE(minb)) {
601 case AG_VARIABLE_DOUBLE:
602 *(double *)min = nmin;
603 break;
604 case AG_VARIABLE_FLOAT:
605 *(float *)min = (float)nmin;
606 break;
607 default:
608 break;
609 }
610 AG_UnlockVariable(minb);
611 AG_ObjectUnlock(fsu);
612 }
613
614 void
AG_MFSpinbuttonSetMax(AG_MFSpinbutton * fsu,double nmax)615 AG_MFSpinbuttonSetMax(AG_MFSpinbutton *fsu, double nmax)
616 {
617 AG_Variable *maxb;
618 void *max;
619
620 AG_ObjectLock(fsu);
621 maxb = AG_GetVariable(fsu, "max", &max);
622 switch (AG_VARIABLE_TYPE(maxb)) {
623 case AG_VARIABLE_DOUBLE:
624 *(double *)max = nmax;
625 break;
626 case AG_VARIABLE_FLOAT:
627 *(float *)max = (float)nmax;
628 break;
629 default:
630 break;
631 }
632 AG_UnlockVariable(maxb);
633 AG_ObjectUnlock(fsu);
634 }
635
636 void
AG_MFSpinbuttonSetIncrement(AG_MFSpinbutton * fsu,double inc)637 AG_MFSpinbuttonSetIncrement(AG_MFSpinbutton *fsu, double inc)
638 {
639 AG_ObjectLock(fsu);
640 fsu->inc = inc;
641 AG_ObjectUnlock(fsu);
642 }
643
644 void
AG_MFSpinbuttonSetPrecision(AG_MFSpinbutton * fsu,const char * mode,int precision)645 AG_MFSpinbuttonSetPrecision(AG_MFSpinbutton *fsu, const char *mode,
646 int precision)
647 {
648 AG_ObjectLock(fsu);
649 Snprintf(fsu->format, sizeof(fsu->format), "%%.%d%s", precision, mode);
650 AG_MFSpinbuttonUpdate(fsu);
651 AG_ObjectUnlock(fsu);
652 }
653
654 void
AG_MFSpinbuttonSelectUnit(AG_MFSpinbutton * fsu,const char * uname)655 AG_MFSpinbuttonSelectUnit(AG_MFSpinbutton *fsu, const char *uname)
656 {
657 AG_TlistItem *it;
658
659 AG_ObjectLock(fsu);
660 AG_ObjectLock(fsu->units->list);
661 AG_TlistDeselectAll(fsu->units->list);
662 TAILQ_FOREACH(it, &fsu->units->list->items, items) {
663 const AG_Unit *u = it->p1;
664
665 if (strcmp(u->key, uname) == 0) {
666 it->selected++;
667 fsu->unit = u;
668 UpdateUnitSelector(fsu);
669 break;
670 }
671 }
672 AG_ObjectUnlock(fsu->units->list);
673 AG_ObjectUnlock(fsu);
674 }
675
676 void
AG_MFSpinbuttonSetWriteable(AG_MFSpinbutton * fsu,int writeable)677 AG_MFSpinbuttonSetWriteable(AG_MFSpinbutton *fsu, int writeable)
678 {
679 AG_ObjectLock(fsu);
680 fsu->writeable = writeable;
681 if (writeable) {
682 AG_WidgetEnable(fsu->xincbu);
683 AG_WidgetEnable(fsu->xdecbu);
684 AG_WidgetEnable(fsu->yincbu);
685 AG_WidgetEnable(fsu->ydecbu);
686 AG_WidgetEnable(fsu->input);
687 } else {
688 AG_WidgetDisable(fsu->xincbu);
689 AG_WidgetDisable(fsu->xdecbu);
690 AG_WidgetDisable(fsu->yincbu);
691 AG_WidgetDisable(fsu->ydecbu);
692 AG_WidgetDisable(fsu->input);
693 }
694 AG_ObjectUnlock(fsu);
695 AG_Redraw(fsu);
696 }
697
698 void
AG_MFSpinbuttonSetRange(AG_MFSpinbutton * fsu,double min,double max)699 AG_MFSpinbuttonSetRange(AG_MFSpinbutton *fsu, double min, double max)
700 {
701 AG_ObjectLock(fsu);
702 AG_MFSpinbuttonSetMin(fsu, min);
703 AG_MFSpinbuttonSetMax(fsu, max);
704 AG_ObjectUnlock(fsu);
705 }
706
707 AG_WidgetClass agMFSpinbuttonClass = {
708 {
709 "Agar(Widget:MFSpinbutton)",
710 sizeof(AG_MFSpinbutton),
711 { 0,0 },
712 Init,
713 NULL, /* free */
714 NULL, /* destroy */
715 NULL, /* load */
716 NULL, /* save */
717 NULL /* edit */
718 },
719 Draw,
720 SizeRequest,
721 SizeAllocate
722 };
723