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