1 /*
2 * Copyright (c) 2002-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
28 #include <agar/gui/button.h>
29 #include <agar/gui/primitive.h>
30 #include <agar/gui/window.h>
31
32 #include <stdarg.h>
33
34 static int GetState(AG_Button *, AG_Variable *, void *);
35 static void SetState(AG_Button *, AG_Variable *, void *, int);
36
37 AG_Button *
AG_ButtonNew(void * parent,Uint flags,const char * fmt,...)38 AG_ButtonNew(void *parent, Uint flags, const char *fmt, ...)
39 {
40 AG_Button *bu;
41 va_list ap;
42 char *s;
43
44 if (fmt != NULL) {
45 va_start(ap, fmt);
46 Vasprintf(&s, fmt, ap);
47 va_end(ap);
48 } else {
49 s = NULL;
50 }
51 bu = AG_ButtonNewS(parent, flags, s);
52 Free(s);
53 return (bu);
54 }
55
56 AG_Button *
AG_ButtonNewS(void * parent,Uint flags,const char * label)57 AG_ButtonNewS(void *parent, Uint flags, const char *label)
58 {
59 AG_Button *bu;
60
61 bu = Malloc(sizeof(AG_Button));
62 AG_ObjectInit(bu, &agButtonClass);
63
64 if (label != NULL) {
65 bu->lbl = AG_LabelNewS(bu, 0, label);
66 AG_LabelJustify(bu->lbl, bu->justify);
67 AG_LabelValign(bu->lbl, bu->valign);
68 }
69 bu->flags |= flags;
70
71 if (flags & AG_BUTTON_HFILL) { AG_ExpandHoriz(bu); }
72 if (flags & AG_BUTTON_VFILL) { AG_ExpandVert(bu); }
73
74 AG_ObjectAttach(parent, bu);
75 return (bu);
76 }
77
78 AG_Button *
AG_ButtonNewFn(void * parent,Uint flags,const char * caption,AG_EventFn fn,const char * fmt,...)79 AG_ButtonNewFn(void *parent, Uint flags, const char *caption, AG_EventFn fn,
80 const char *fmt, ...)
81 {
82 AG_Button *bu;
83 AG_Event *ev;
84
85 if (!(flags & AG_BUTTON_NOEXCL)) { flags |= AG_BUTTON_EXCL; }
86 bu = AG_ButtonNewS(parent, flags, caption);
87 ev = AG_SetEvent(bu, "button-pushed", fn, NULL);
88 AG_EVENT_GET_ARGS(ev, fmt);
89 return (bu);
90 }
91
92 AG_Button *
AG_ButtonNewInt(void * parent,Uint flags,const char * caption,int * v)93 AG_ButtonNewInt(void *parent, Uint flags, const char *caption, int *v)
94 {
95 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
96 AG_BindInt(bu, "state", v);
97 return (bu);
98 }
99
100 AG_Button *
AG_ButtonNewUint8(void * parent,Uint flags,const char * caption,Uint8 * v)101 AG_ButtonNewUint8(void *parent, Uint flags, const char *caption, Uint8 *v)
102 {
103 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
104 AG_BindUint8(bu, "state", v);
105 return (bu);
106 }
107
108 AG_Button *
AG_ButtonNewUint16(void * parent,Uint flags,const char * caption,Uint16 * v)109 AG_ButtonNewUint16(void *parent, Uint flags, const char *caption, Uint16 *v)
110 {
111 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
112 AG_BindUint16(bu, "state", v);
113 return (bu);
114 }
115
116 AG_Button *
AG_ButtonNewUint32(void * parent,Uint flags,const char * caption,Uint32 * v)117 AG_ButtonNewUint32(void *parent, Uint flags, const char *caption, Uint32 *v)
118 {
119 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
120 AG_BindUint32(bu, "state", v);
121 return (bu);
122 }
123
124 AG_Button *
AG_ButtonNewFlag(void * parent,Uint flags,const char * caption,Uint * p,Uint bitmask)125 AG_ButtonNewFlag(void *parent, Uint flags, const char *caption,
126 Uint *p, Uint bitmask)
127 {
128 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
129 AG_BindFlag(bu, "state", p, bitmask);
130 return (bu);
131 }
132
133 AG_Button *
AG_ButtonNewFlag8(void * parent,Uint flags,const char * caption,Uint8 * p,Uint8 bitmask)134 AG_ButtonNewFlag8(void *parent, Uint flags, const char *caption,
135 Uint8 *p, Uint8 bitmask)
136 {
137 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
138 AG_BindFlag8(bu, "state", p, bitmask);
139 return (bu);
140 }
141
142 AG_Button *
AG_ButtonNewFlag16(void * parent,Uint flags,const char * caption,Uint16 * p,Uint16 bitmask)143 AG_ButtonNewFlag16(void *parent, Uint flags, const char *caption,
144 Uint16 *p, Uint16 bitmask)
145 {
146 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
147 AG_BindFlag16(bu, "state", p, bitmask);
148 return (bu);
149 }
150
151 AG_Button *
AG_ButtonNewFlag32(void * parent,Uint flags,const char * caption,Uint32 * p,Uint32 bitmask)152 AG_ButtonNewFlag32(void *parent, Uint flags, const char *caption,
153 Uint32 *p, Uint32 bitmask)
154 {
155 AG_Button *bu = AG_ButtonNewS(parent, flags, caption);
156 AG_BindFlag32(bu, "state", p, bitmask);
157 return (bu);
158 }
159
160 /* Delay/repeat timer callbacks for AG_BUTTON_REPEAT */
161 static Uint32
ExpireRepeat(AG_Timer * to,AG_Event * event)162 ExpireRepeat(AG_Timer *to, AG_Event *event)
163 {
164 AG_Button *bu = AG_SELF();
165
166 AG_PostEvent(NULL, bu, "button-pushed", "%i", 1);
167 return (to->ival);
168 }
169 static Uint32
ExpireDelay(AG_Timer * to,AG_Event * event)170 ExpireDelay(AG_Timer *to, AG_Event *event)
171 {
172 AG_Button *bu = AG_SELF();
173 int repeatIval = AG_INT(1);
174
175 AG_AddTimer(bu, &bu->repeatTo, repeatIval, ExpireRepeat, NULL);
176 return (0);
177 }
178
179 static void
MouseButtonUp(AG_Event * event)180 MouseButtonUp(AG_Event *event)
181 {
182 AG_Button *bu = AG_SELF();
183 int button = AG_INT(1);
184 AG_Variable *binding;
185 void *pState;
186 int x = AG_INT(2);
187 int y = AG_INT(3);
188
189 if (bu->flags & AG_BUTTON_REPEAT) {
190 AG_DelTimer(bu, &bu->repeatTo);
191 AG_DelTimer(bu, &bu->delayTo);
192 return;
193 }
194
195 if (AG_WidgetDisabled(bu) ||
196 x < 0 || y < 0 ||
197 x > WIDGET(bu)->w || y > WIDGET(bu)->h) {
198 return;
199 }
200
201 binding = AG_GetVariable(bu, "state", &pState);
202 if (GetState(bu, binding, pState) && button == AG_MOUSE_LEFT &&
203 !(bu->flags & AG_BUTTON_STICKY)) {
204 SetState(bu, binding, pState, 0);
205 AG_PostEvent(NULL, bu, "button-pushed", "%i", 0);
206 }
207 AG_UnlockVariable(binding);
208 }
209
210 static void
MouseButtonDown(AG_Event * event)211 MouseButtonDown(AG_Event *event)
212 {
213 AG_Button *bu = AG_SELF();
214 int button = AG_INT(1);
215 AG_Variable *binding;
216 void *pState;
217 int newState;
218
219 if (AG_WidgetDisabled(bu))
220 return;
221
222 if (!AG_WidgetIsFocused(bu))
223 AG_WidgetFocus(bu);
224
225 if (button != AG_MOUSE_LEFT)
226 return;
227
228 binding = AG_GetVariable(bu, "state", &pState);
229 if (!(bu->flags & AG_BUTTON_STICKY)) {
230 SetState(bu, binding, pState, 1);
231 } else {
232 newState = !GetState(bu, binding, pState);
233 SetState(bu, binding, pState, newState);
234 AG_PostEvent(NULL, bu, "button-pushed", "%i", newState);
235 }
236 AG_UnlockVariable(binding);
237
238 if (bu->flags & AG_BUTTON_REPEAT) {
239 AG_DelTimer(bu, &bu->repeatTo);
240 AG_PostEvent(NULL, bu, "button-pushed", "%i", 1);
241 AG_AddTimer(bu, &bu->delayTo, agMouseSpinDelay,
242 ExpireDelay, "%i", agMouseSpinIval);
243 }
244 }
245
246 static void
MouseMotion(AG_Event * event)247 MouseMotion(AG_Event *event)
248 {
249 AG_Button *bu = AG_SELF();
250 AG_Variable *binding;
251 int x = AG_INT(1);
252 int y = AG_INT(2);
253 void *pState;
254
255 if (AG_WidgetDisabled(bu))
256 return;
257
258 binding = AG_GetVariable(bu, "state", &pState);
259 if (!AG_WidgetRelativeArea(bu, x, y)) {
260 if ((bu->flags & AG_BUTTON_STICKY) == 0 &&
261 GetState(bu, binding, pState) == 1) {
262 SetState(bu, binding, pState, 0);
263 }
264 }
265 AG_UnlockVariable(binding);
266 }
267
268 static void
KeyUp(AG_Event * event)269 KeyUp(AG_Event *event)
270 {
271 AG_Button *bu = AG_SELF();
272 AG_Variable *binding;
273 void *pState;
274 int keysym = AG_INT(1);
275
276 if (AG_WidgetDisabled(bu)) {
277 return;
278 }
279 if (bu->flags & AG_BUTTON_REPEAT) {
280 AG_DelTimer(bu, &bu->delayTo);
281 AG_DelTimer(bu, &bu->repeatTo);
282 }
283 if (keysym != AG_KEY_RETURN && /* TODO AG_Action */
284 keysym != AG_KEY_KP_ENTER &&
285 keysym != AG_KEY_SPACE) {
286 return;
287 }
288 binding = AG_GetVariable(bu, "state", &pState);
289 SetState(bu, binding, pState, 0);
290 AG_UnlockVariable(binding);
291
292 if (bu->flags & AG_BUTTON_KEYDOWN) {
293 bu->flags &= ~(AG_BUTTON_KEYDOWN);
294 AG_PostEvent(NULL, bu, "button-pushed", "%i", 0);
295 }
296 }
297
298 static void
KeyDown(AG_Event * event)299 KeyDown(AG_Event *event)
300 {
301 AG_Button *bu = AG_SELF();
302 AG_Variable *binding;
303 void *pState;
304 int keysym = AG_INT(1);
305
306 if (AG_WidgetDisabled(bu))
307 return;
308 if (keysym != AG_KEY_RETURN && /* TODO AG_Action */
309 keysym != AG_KEY_KP_ENTER &&
310 keysym != AG_KEY_SPACE) {
311 return;
312 }
313 binding = AG_GetVariable(bu, "state", &pState);
314 SetState(bu, binding, pState, 1);
315 AG_PostEvent(NULL, bu, "button-pushed", "%i", 1);
316 bu->flags |= AG_BUTTON_KEYDOWN;
317
318 if (bu->flags & AG_BUTTON_REPEAT) {
319 AG_DelTimer(bu, &bu->repeatTo);
320 AG_AddTimer(bu, &bu->delayTo, agKbdDelay,
321 ExpireDelay, "%i", agKbdRepeat);
322 }
323 AG_UnlockVariable(binding);
324 }
325
326 static void
OnShow(AG_Event * event)327 OnShow(AG_Event *event)
328 {
329 AG_Button *bu = AG_SELF();
330
331 if ((bu->flags & AG_BUTTON_EXCL) == 0)
332 AG_RedrawOnChange(bu, 100, "state");
333 }
334
335 static void
Init(void * obj)336 Init(void *obj)
337 {
338 AG_Button *bu = obj;
339
340 WIDGET(bu)->flags |= AG_WIDGET_FOCUSABLE|
341 AG_WIDGET_UNFOCUSED_MOTION|
342 AG_WIDGET_UNFOCUSED_BUTTONUP|
343 AG_WIDGET_TABLE_EMBEDDABLE|
344 AG_WIDGET_USE_TEXT|
345 AG_WIDGET_USE_MOUSEOVER;
346
347 bu->flags = 0;
348 bu->lbl = NULL;
349 bu->surface = -1;
350 bu->state = 0;
351 bu->justify = AG_TEXT_CENTER;
352 bu->valign = AG_TEXT_MIDDLE;
353 bu->lPad = 4;
354 bu->rPad = 4;
355 bu->tPad = 3;
356 bu->bPad = 3;
357 AG_InitTimer(&bu->delayTo, "delay", 0);
358 AG_InitTimer(&bu->repeatTo, "repeat", 0);
359
360 AG_AddEvent(bu, "widget-shown", OnShow, NULL);
361 AG_SetEvent(bu, "mouse-button-up", MouseButtonUp, NULL);
362 AG_SetEvent(bu, "mouse-button-down", MouseButtonDown, NULL);
363 AG_SetEvent(bu, "mouse-motion", MouseMotion, NULL);
364 AG_SetEvent(bu, "key-up", KeyUp, NULL);
365 AG_SetEvent(bu, "key-down", KeyDown, NULL);
366
367 AG_BindInt(bu, "state", &bu->state);
368 }
369
370 static void
SizeRequest(void * p,AG_SizeReq * r)371 SizeRequest(void *p, AG_SizeReq *r)
372 {
373 AG_Button *bu = p;
374 AG_SizeReq rLbl;
375
376 r->w = bu->lPad + bu->rPad;
377 r->h = bu->tPad + bu->bPad;
378
379 if (bu->surface != -1) {
380 r->w += WSURFACE(bu,bu->surface)->w;
381 r->h += WSURFACE(bu,bu->surface)->h;
382 } else {
383 if (bu->lbl != NULL) {
384 AG_WidgetSizeReq(bu->lbl, &rLbl);
385 r->w += rLbl.w;
386 }
387 r->h += WIDGET(bu)->font->height;
388 }
389 }
390
391 static int
SizeAllocate(void * p,const AG_SizeAlloc * a)392 SizeAllocate(void *p, const AG_SizeAlloc *a)
393 {
394 AG_Button *bu = p;
395 AG_SizeAlloc aLbl;
396
397 if (a->w < 2 || a->h < 2) {
398 return (-1);
399 }
400 if (bu->lbl != NULL) {
401 aLbl.x = bu->lPad;
402 aLbl.y = bu->tPad;
403 aLbl.w = a->w - (bu->lPad+bu->rPad);
404 aLbl.h = a->h - (bu->tPad+bu->bPad);
405 AG_WidgetSizeAlloc(bu->lbl, &aLbl);
406 }
407 return (0);
408 }
409
410 static int
GetState(AG_Button * bu,AG_Variable * binding,void * p)411 GetState(AG_Button *bu, AG_Variable *binding, void *p)
412 {
413 int v;
414
415 switch (AG_VARIABLE_TYPE(binding)) {
416 case AG_VARIABLE_INT:
417 v = *(int *)p;
418 break;
419 case AG_VARIABLE_UINT8:
420 v = (int)(*(Uint8 *)p);
421 break;
422 case AG_VARIABLE_UINT16:
423 v = (int)(*(Uint16 *)p);
424 break;
425 case AG_VARIABLE_UINT32:
426 v = (int)(*(Uint32 *)p);
427 break;
428 case AG_VARIABLE_P_FLAG:
429 v = (*(int *)p & (int)binding->info.bitmask);
430 break;
431 case AG_VARIABLE_P_FLAG8:
432 v = (int)(*(Uint8 *)p & (Uint8)binding->info.bitmask);
433 break;
434 case AG_VARIABLE_P_FLAG16:
435 v = (int)(*(Uint16 *)p & (Uint16)binding->info.bitmask);
436 break;
437 case AG_VARIABLE_P_FLAG32:
438 v = (int)(*(Uint32 *)p & (Uint32)binding->info.bitmask);
439 break;
440 default:
441 v = 0;
442 break;
443 }
444 if (bu->flags & AG_BUTTON_INVSTATE) {
445 v = !v;
446 }
447 return (v);
448 }
449
450 static void
SetState(AG_Button * bu,AG_Variable * binding,void * p,int v)451 SetState(AG_Button *bu, AG_Variable *binding, void *p, int v)
452 {
453 switch (AG_VARIABLE_TYPE(binding)) {
454 case AG_VARIABLE_INT:
455 *(int *)p = v;
456 break;
457 case AG_VARIABLE_UINT8:
458 *(Uint8 *)p = v;
459 break;
460 case AG_VARIABLE_UINT16:
461 *(Uint16 *)p = v;
462 break;
463 case AG_VARIABLE_UINT32:
464 *(Uint32 *)p = v;
465 break;
466 case AG_VARIABLE_P_FLAG:
467 AG_SETFLAGS(*(int *)p, (int)binding->info.bitmask, v);
468 break;
469 case AG_VARIABLE_P_FLAG8:
470 AG_SETFLAGS(*(Uint8 *)p, (Uint8)binding->info.bitmask, v);
471 break;
472 case AG_VARIABLE_P_FLAG16:
473 AG_SETFLAGS(*(Uint16 *)p, (Uint16)binding->info.bitmask, v);
474 break;
475 case AG_VARIABLE_P_FLAG32:
476 AG_SETFLAGS(*(Uint32 *)p, (Uint32)binding->info.bitmask, v);
477 break;
478 default:
479 break;
480 }
481 AG_Redraw(bu);
482 }
483
484 static void
Draw(void * p)485 Draw(void *p)
486 {
487 AG_Button *bu = p;
488 AG_Variable *binding;
489 void *pState;
490 int pressed;
491
492 binding = AG_GetVariable(bu, "state", &pState);
493 pressed = GetState(bu, binding, pState);
494 AG_UnlockVariable(binding);
495
496 if (AG_WidgetEnabled(bu)) {
497 AG_DrawBox(bu,
498 AG_RECT(0, 0, WIDTH(bu), HEIGHT(bu)),
499 pressed ? -1 : 1,
500 WCOLOR(bu,0));
501 } else {
502 AG_DrawBoxDisabled(bu,
503 AG_RECT(0, 0, WIDTH(bu), HEIGHT(bu)),
504 pressed ? -1 : 1,
505 WCOLOR_DEF(bu,0),
506 WCOLOR_DIS(bu,0));
507 }
508
509 if (bu->lbl != NULL) {
510 AG_WidgetDraw(bu->lbl);
511 } else if (bu->surface != -1) {
512 int w = WSURFACE(bu,bu->surface)->w;
513 int h = WSURFACE(bu,bu->surface)->h;
514 int x = 0, y = 0;
515
516 switch (bu->justify) {
517 case AG_TEXT_LEFT: x = bu->lPad; break;
518 case AG_TEXT_CENTER: x = WIDTH(bu)/2 - w/2; break;
519 case AG_TEXT_RIGHT: x = WIDTH(bu) - w - bu->rPad; break;
520 }
521 switch (bu->valign) {
522 case AG_TEXT_TOP: y = bu->tPad; break;
523 case AG_TEXT_MIDDLE: y = HEIGHT(bu)/2 - h/2; break;
524 case AG_TEXT_BOTTOM: y = HEIGHT(bu) - h - bu->bPad; break;
525 }
526 if (pressed) {
527 x++;
528 y++;
529 }
530 AG_WidgetBlitSurface(bu, bu->surface, x, y);
531 }
532 }
533
534 void
AG_ButtonSetPadding(AG_Button * bu,int lPad,int rPad,int tPad,int bPad)535 AG_ButtonSetPadding(AG_Button *bu, int lPad, int rPad, int tPad, int bPad)
536 {
537 AG_ObjectLock(bu);
538 if (lPad != -1) { bu->lPad = lPad; }
539 if (rPad != -1) { bu->rPad = rPad; }
540 if (tPad != -1) { bu->tPad = tPad; }
541 if (bPad != -1) { bu->bPad = bPad; }
542 AG_ObjectUnlock(bu);
543 AG_Redraw(bu);
544 }
545
546 void
AG_ButtonSetFocusable(AG_Button * bu,int focusable)547 AG_ButtonSetFocusable(AG_Button *bu, int focusable)
548 {
549 AG_ObjectLock(bu);
550 if (focusable) {
551 WIDGET(bu)->flags |= AG_WIDGET_FOCUSABLE;
552 WIDGET(bu)->flags &= ~(AG_WIDGET_UNFOCUSED_BUTTONUP);
553 } else {
554 WIDGET(bu)->flags &= ~(AG_WIDGET_FOCUSABLE);
555 WIDGET(bu)->flags |= AG_WIDGET_UNFOCUSED_BUTTONUP;
556 }
557 AG_ObjectUnlock(bu);
558 }
559
560 void
AG_ButtonSetSticky(AG_Button * bu,int flag)561 AG_ButtonSetSticky(AG_Button *bu, int flag)
562 {
563 AG_ObjectLock(bu);
564 AG_SETFLAGS(bu->flags, AG_BUTTON_STICKY, flag);
565 AG_ObjectUnlock(bu);
566 }
567
568 void
AG_ButtonInvertState(AG_Button * bu,int flag)569 AG_ButtonInvertState(AG_Button *bu, int flag)
570 {
571 AG_ObjectLock(bu);
572 AG_SETFLAGS(bu->flags, AG_BUTTON_INVSTATE, flag);
573 AG_ObjectUnlock(bu);
574 }
575
576 void
AG_ButtonJustify(AG_Button * bu,enum ag_text_justify jus)577 AG_ButtonJustify(AG_Button *bu, enum ag_text_justify jus)
578 {
579 AG_ObjectLock(bu);
580 bu->justify = jus;
581 if (bu->lbl != NULL) {
582 AG_LabelJustify(bu->lbl, jus);
583 }
584 AG_ObjectUnlock(bu);
585 }
586
587 void
AG_ButtonValign(AG_Button * bu,enum ag_text_valign va)588 AG_ButtonValign(AG_Button *bu, enum ag_text_valign va)
589 {
590 AG_ObjectLock(bu);
591 bu->valign = va;
592 if (bu->lbl != NULL) {
593 AG_LabelValign(bu->lbl, va);
594 }
595 AG_ObjectUnlock(bu);
596 }
597
598 void
AG_ButtonSurface(AG_Button * bu,const AG_Surface * su)599 AG_ButtonSurface(AG_Button *bu, const AG_Surface *su)
600 {
601 AG_Surface *suDup = (su != NULL) ? AG_SurfaceDup(su) : NULL;
602
603 AG_ObjectLock(bu);
604 if (bu->lbl != NULL) {
605 AG_ObjectDetach(bu->lbl);
606 AG_ObjectDestroy(bu->lbl);
607 bu->lbl = NULL;
608 }
609 if (bu->surface != -1) {
610 AG_WidgetReplaceSurface(bu, bu->surface, suDup);
611 } else {
612 bu->surface = AG_WidgetMapSurface(bu, suDup);
613 }
614 AG_ObjectUnlock(bu);
615 AG_Redraw(bu);
616 }
617
618 void
AG_ButtonSurfaceNODUP(AG_Button * bu,AG_Surface * su)619 AG_ButtonSurfaceNODUP(AG_Button *bu, AG_Surface *su)
620 {
621 AG_ObjectLock(bu);
622 if (bu->lbl != NULL) {
623 AG_ObjectDetach(bu->lbl);
624 AG_ObjectDestroy(bu->lbl);
625 bu->lbl = NULL;
626 }
627 if (bu->surface != -1) {
628 AG_WidgetReplaceSurfaceNODUP(bu, bu->surface, su);
629 } else {
630 bu->surface = AG_WidgetMapSurfaceNODUP(bu, su);
631 }
632 AG_ObjectUnlock(bu);
633 AG_Redraw(bu);
634 }
635
636 void
AG_ButtonSetRepeatMode(AG_Button * bu,int repeat)637 AG_ButtonSetRepeatMode(AG_Button *bu, int repeat)
638 {
639 AG_ObjectLock(bu);
640 if (repeat) {
641 bu->flags |= (AG_BUTTON_REPEAT);
642 } else {
643 AG_DelTimer(bu, &bu->repeatTo);
644 AG_DelTimer(bu, &bu->delayTo);
645 bu->flags &= ~(AG_BUTTON_REPEAT);
646 }
647 AG_ObjectUnlock(bu);
648 }
649
650 /* Set the label text (C string). */
651 void
AG_ButtonTextS(AG_Button * bu,const char * label)652 AG_ButtonTextS(AG_Button *bu, const char *label)
653 {
654 AG_ObjectLock(bu);
655 if (bu->surface != -1) {
656 AG_ButtonSurface(bu, NULL);
657 }
658 if (bu->lbl == NULL) {
659 bu->lbl = AG_LabelNewS(bu, 0, label);
660 AG_LabelJustify(bu->lbl, bu->justify);
661 AG_LabelValign(bu->lbl, bu->valign);
662 } else {
663 AG_LabelTextS(bu->lbl, label);
664 }
665 AG_ObjectUnlock(bu);
666 AG_Redraw(bu);
667 }
668
669 /* Set the label text (format string). */
670 void
AG_ButtonText(AG_Button * bu,const char * fmt,...)671 AG_ButtonText(AG_Button *bu, const char *fmt, ...)
672 {
673 char s[AG_LABEL_MAX];
674 va_list ap;
675
676 va_start(ap, fmt);
677 Vsnprintf(s, sizeof(s), fmt, ap);
678 va_end(ap);
679 AG_ButtonTextS(bu, s);
680 }
681
682 AG_WidgetClass agButtonClass = {
683 {
684 "Agar(Widget:Button)",
685 sizeof(AG_Button),
686 { 0,0 },
687 Init,
688 NULL, /* free */
689 NULL, /* destroy */
690 NULL, /* load */
691 NULL, /* save */
692 NULL /* edit */
693 },
694 Draw,
695 SizeRequest,
696 SizeAllocate
697 };
698