1 /*
2 * Copyright (c) 2002-2007 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/checkbox.h>
28 #include <agar/gui/window.h>
29 #include <agar/gui/primitive.h>
30 #include <agar/gui/text.h>
31
32 #include <stdarg.h>
33 #include <string.h>
34
35 AG_Checkbox *
AG_CheckboxNew(void * parent,Uint flags,const char * fmt,...)36 AG_CheckboxNew(void *parent, Uint flags, const char *fmt, ...)
37 {
38 char s[AG_LABEL_MAX];
39 va_list ap;
40
41 if (fmt != NULL) {
42 va_start(ap, fmt);
43 Vsnprintf(s, sizeof(s), fmt, ap);
44 va_end(ap);
45 return AG_CheckboxNewS(parent, flags, s);
46 } else {
47 return AG_CheckboxNewS(parent, flags, NULL);
48 }
49 }
50
51 AG_Checkbox *
AG_CheckboxNewS(void * parent,Uint flags,const char * label)52 AG_CheckboxNewS(void *parent, Uint flags, const char *label)
53 {
54 AG_Checkbox *cb;
55
56 cb = Malloc(sizeof(AG_Checkbox));
57 AG_ObjectInit(cb, &agCheckboxClass);
58 cb->flags |= flags;
59
60 if (label != NULL) {
61 cb->lbl = AG_LabelNewS(cb, 0, label);
62 AG_LabelValign(cb->lbl, AG_TEXT_MIDDLE);
63 }
64 if (flags & AG_CHECKBOX_SET) {
65 cb->state = 1;
66 }
67 AG_ObjectAttach(parent, cb);
68 return (cb);
69 }
70
71 AG_Checkbox *
AG_CheckboxNewFn(void * parent,Uint flags,const char * label,AG_EventFn fn,const char * fmt,...)72 AG_CheckboxNewFn(void *parent, Uint flags, const char *label, AG_EventFn fn,
73 const char *fmt, ...)
74 {
75 AG_Checkbox *cb;
76 AG_Event *ev;
77
78 cb = AG_CheckboxNewS(parent, flags, label);
79 ev = AG_SetEvent(cb, "checkbox-changed", fn, NULL);
80 AG_EVENT_GET_ARGS(ev, fmt);
81 return (cb);
82 }
83
84 AG_Checkbox *
AG_CheckboxNewInt(void * parent,Uint flags,const char * label,int * pVal)85 AG_CheckboxNewInt(void *parent, Uint flags, const char *label, int *pVal)
86 {
87 AG_Checkbox *cb;
88 cb = AG_CheckboxNewS(parent, flags, label);
89 AG_BindInt(cb, "state", pVal);
90 return (cb);
91 }
92
93 AG_Checkbox *
AG_CheckboxNewFlag(void * parent,Uint flags,const char * label,Uint * pFlags,Uint bitmask)94 AG_CheckboxNewFlag(void *parent, Uint flags, const char *label, Uint *pFlags,
95 Uint bitmask)
96 {
97 AG_Checkbox *cb;
98 cb = AG_CheckboxNewS(parent, flags, label);
99 AG_BindFlag(cb, "state", pFlags, bitmask);
100 return (cb);
101 }
102
103 AG_Checkbox *
AG_CheckboxNewFlag32(void * parent,Uint flags,const char * label,Uint32 * pFlags,Uint32 bitmask)104 AG_CheckboxNewFlag32(void *parent, Uint flags, const char *label,
105 Uint32 *pFlags, Uint32 bitmask)
106 {
107 AG_Checkbox *cb;
108 cb = AG_CheckboxNewS(parent, flags, label);
109 AG_BindFlag32(cb, "state", pFlags, bitmask);
110 return (cb);
111 }
112
113 /* Create a set of checkboxes for the given set of flags. */
114 void
AG_CheckboxSetFromFlags(void * parent,Uint flags,Uint * pFlags,const AG_FlagDescr * fdSet)115 AG_CheckboxSetFromFlags(void *parent, Uint flags, Uint *pFlags,
116 const AG_FlagDescr *fdSet)
117 {
118 const AG_FlagDescr *fd;
119 AG_Checkbox *cb;
120 int i;
121
122 for (i = 0; fdSet[i].bitmask != 0; i++) {
123 fd = &fdSet[i];
124 cb = AG_CheckboxNewFlag(parent, flags, fd->descr,
125 pFlags, fd->bitmask);
126 if (!fd->writeable)
127 AG_WidgetDisable(cb);
128 }
129 }
130
131 /* Create a set of checkboxes for the given set of flags. */
132 void
AG_CheckboxSetFromFlags32(void * parent,Uint flags,Uint32 * pFlags,const AG_FlagDescr * fdSet)133 AG_CheckboxSetFromFlags32(void *parent, Uint flags, Uint32 *pFlags,
134 const AG_FlagDescr *fdSet)
135 {
136 const AG_FlagDescr *fd;
137 AG_Checkbox *cb;
138 int i;
139
140 for (i = 0; fdSet[i].bitmask != 0; i++) {
141 fd = &fdSet[i];
142 cb = AG_CheckboxNewFlag32(parent, flags, fd->descr,
143 pFlags, (Uint32)fd->bitmask);
144 if (!fd->writeable)
145 AG_WidgetDisable(cb);
146 }
147 }
148
149 static void
MouseButtonDown(AG_Event * event)150 MouseButtonDown(AG_Event *event)
151 {
152 AG_Checkbox *cb = AG_SELF();
153 int button = AG_INT(1);
154
155 if (!AG_WidgetEnabled(cb))
156 return;
157
158 if (button == AG_MOUSE_LEFT) {
159 if (!AG_WidgetIsFocused(cb)) {
160 AG_WidgetFocus(cb);
161 }
162 AG_CheckboxToggle(cb);
163 }
164 }
165
166 static void
KeyDown(AG_Event * event)167 KeyDown(AG_Event *event)
168 {
169 AG_Checkbox *cb = AG_SELF();
170 int key = AG_INT(1);
171
172 if (!AG_WidgetEnabled(cb))
173 return;
174
175 switch (key) {
176 case AG_KEY_RETURN:
177 case AG_KEY_KP_ENTER:
178 case AG_KEY_SPACE:
179 AG_CheckboxToggle(cb);
180 break;
181 }
182 }
183
184 static void
Init(void * obj)185 Init(void *obj)
186 {
187 AG_Checkbox *cb = obj;
188
189 WIDGET(cb)->flags |= AG_WIDGET_FOCUSABLE|
190 AG_WIDGET_UNFOCUSED_MOTION|
191 AG_WIDGET_TABLE_EMBEDDABLE|
192 AG_WIDGET_USE_TEXT|
193 AG_WIDGET_USE_MOUSEOVER;
194
195 AG_BindInt(cb, "state", &cb->state);
196 AG_RedrawOnChange(cb, 100, "state");
197
198 cb->flags = 0;
199 cb->state = 0;
200 cb->lbl = NULL;
201 cb->spacing = 4;
202
203 AG_SetEvent(cb, "mouse-button-down", MouseButtonDown, NULL);
204 AG_SetEvent(cb, "key-down", KeyDown, NULL);
205 }
206
207 static void
Draw(void * obj)208 Draw(void *obj)
209 {
210 AG_Checkbox *cb = obj;
211 AG_Font *font = WIDGET(cb)->font;
212 AG_Variable *stateb;
213 void *p;
214 int state;
215
216 stateb = AG_GetVariable(cb, "state", &p);
217 switch (AG_VARIABLE_TYPE(stateb)) {
218 case AG_VARIABLE_INT:
219 case AG_VARIABLE_UINT:
220 state = *(int *)p;
221 break;
222 case AG_VARIABLE_P_FLAG:
223 state = *(int *)p & (int)stateb->info.bitmask;
224 break;
225 case AG_VARIABLE_P_FLAG8:
226 state = *(Uint8 *)p & (Uint8)stateb->info.bitmask;
227 break;
228 case AG_VARIABLE_P_FLAG16:
229 state = *(Uint16 *)p & (Uint16)stateb->info.bitmask;
230 break;
231 case AG_VARIABLE_P_FLAG32:
232 state = *(Uint32 *)p & (Uint32)stateb->info.bitmask;
233 break;
234 case AG_VARIABLE_UINT8:
235 case AG_VARIABLE_SINT8:
236 state = *(Uint8 *)p;
237 break;
238 case AG_VARIABLE_UINT16:
239 case AG_VARIABLE_SINT16:
240 state = *(Uint16 *)p;
241 break;
242 case AG_VARIABLE_UINT32:
243 case AG_VARIABLE_SINT32:
244 state = *(Uint32 *)p;
245 break;
246 default:
247 state = 0;
248 break;
249 }
250
251 if (WIDGET(cb)->flags & AG_WIDGET_MOUSEOVER) {
252 AG_Rect r = AG_RECT(0,0,WIDGET(cb)->w,WIDGET(cb)->h-1);
253 AG_DrawRectBlended(cb, r,
254 AG_ColorRGBA(255,255,255,25),
255 AG_ALPHA_SRC);
256 }
257
258 if (AG_WidgetEnabled(cb)) {
259 AG_DrawBox(cb,
260 AG_RECT(0, 0, font->height, font->height),
261 state ? -1 : 1,
262 WCOLOR(cb,0));
263 } else {
264 AG_DrawBoxDisabled(cb,
265 AG_RECT(0, 0, font->height, font->height),
266 state ? -1 : 1,
267 WCOLOR(cb,0),
268 WCOLOR_DIS(cb,0));
269 }
270
271 if (cb->lbl != NULL) {
272 AG_WidgetDraw(cb->lbl);
273 }
274 AG_UnlockVariable(stateb);
275 }
276
277 static void
SizeRequest(void * obj,AG_SizeReq * r)278 SizeRequest(void *obj, AG_SizeReq *r)
279 {
280 AG_Checkbox *cb = obj;
281 AG_Font *font = WIDGET(cb)->font;
282 AG_SizeReq rLbl;
283
284 r->h = font->height;
285 r->w = font->height;
286
287 if (cb->lbl != NULL) {
288 AG_WidgetSizeReq(cb->lbl, &rLbl);
289 r->w += cb->spacing + rLbl.w;
290 r->h = MAX(r->h, rLbl.h);
291 }
292 }
293
294 static int
SizeAllocate(void * obj,const AG_SizeAlloc * a)295 SizeAllocate(void *obj, const AG_SizeAlloc *a)
296 {
297 AG_Checkbox *cb = obj;
298 AG_Font *font = WIDGET(cb)->font;
299 AG_SizeAlloc aLbl;
300
301 if (a->w < font->height) {
302 return (-1);
303 }
304 if (cb->lbl != NULL) {
305 aLbl.x = font->height + cb->spacing;
306 aLbl.y = 0;
307 aLbl.w = a->w - aLbl.x;
308 aLbl.h = a->h;
309 AG_WidgetSizeAlloc(cb->lbl, &aLbl);
310 }
311 return (0);
312 }
313
314 /* Toggle the checkbox state. */
315 void
AG_CheckboxToggle(AG_Checkbox * cb)316 AG_CheckboxToggle(AG_Checkbox *cb)
317 {
318 AG_Variable *stateb;
319 void *p;
320
321 AG_ObjectLock(cb);
322 stateb = AG_GetVariable(cb, "state", &p);
323 switch (AG_VARIABLE_TYPE(stateb)) {
324 case AG_VARIABLE_INT:
325 case AG_VARIABLE_UINT:
326 {
327 int *state = (int *)p;
328 *state = !(*state);
329 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
330 *state);
331 }
332 break;
333 case AG_VARIABLE_P_FLAG:
334 {
335 int *state = (int *)p;
336 AG_INVFLAGS(*state, (int)stateb->info.bitmask);
337 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
338 (int)*state);
339 }
340 break;
341 case AG_VARIABLE_P_FLAG8:
342 {
343 Uint8 *state = (Uint8 *)p;
344 AG_INVFLAGS(*state, (Uint8)stateb->info.bitmask);
345 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
346 (Uint8)*state);
347 }
348 break;
349 case AG_VARIABLE_P_FLAG16:
350 {
351 Uint16 *state = (Uint16 *)p;
352 AG_INVFLAGS(*state, (Uint16)stateb->info.bitmask);
353 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
354 (Uint16)*state);
355 }
356 break;
357 case AG_VARIABLE_P_FLAG32:
358 {
359 Uint32 *state = (Uint32 *)p;
360 AG_INVFLAGS(*state, (Uint32)stateb->info.bitmask);
361 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
362 (Uint32)*state);
363 }
364 break;
365 case AG_VARIABLE_UINT8:
366 case AG_VARIABLE_SINT8:
367 {
368 Uint8 *state = (Uint8 *)p;
369 *state = !(*state);
370 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
371 (int)*state);
372 }
373 break;
374 case AG_VARIABLE_UINT16:
375 case AG_VARIABLE_SINT16:
376 {
377 Uint16 *state = (Uint16 *)p;
378 *state = !(*state);
379 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
380 (int)*state);
381 }
382 break;
383 case AG_VARIABLE_UINT32:
384 case AG_VARIABLE_SINT32:
385 {
386 Uint32 *state = (Uint32 *)p;
387 *state = !(*state);
388 AG_PostEvent(NULL, cb, "checkbox-changed", "%i",
389 (int)*state);
390 }
391 break;
392 default:
393 break;
394 }
395 AG_UnlockVariable(stateb);
396 AG_ObjectUnlock(cb);
397 AG_Redraw(cb);
398 }
399
400 AG_WidgetClass agCheckboxClass = {
401 {
402 "Agar(Widget:Checkbox)",
403 sizeof(AG_Checkbox),
404 { 0,0 },
405 Init,
406 NULL, /* free */
407 NULL, /* destroy */
408 NULL, /* load */
409 NULL, /* save */
410 NULL /* edit */
411 },
412 Draw,
413 SizeRequest,
414 SizeAllocate
415 };
416