1 /*
2 * Copyright (c) 2001-2015 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 /*
27 * Implementation of the AG_Widget(3) object.
28 */
29
30 #include <agar/core/core.h>
31 #include <agar/gui/gui.h>
32 #include <agar/gui/widget.h>
33 #include <agar/gui/window.h>
34 #include <agar/gui/cursors.h>
35 #include <agar/gui/primitive.h>
36 #include <agar/gui/gui_math.h>
37 #include <agar/gui/opengl.h>
38 #include <agar/gui/text_cache.h>
39
40 #include <stdarg.h>
41 #include <string.h>
42 #include <ctype.h>
43
44 const char *agWidgetPropNames[] = {
45 "font-family",
46 "font-size",
47 "font-weight",
48 "font-style",
49 "color",
50 "text-color",
51 "line-color",
52 "shape-color",
53 "border-color",
54 NULL
55 };
56 const char *agWidgetStateNames[] = {
57 "",
58 "#disabled",
59 "#focused",
60 "#hover",
61 "#selected",
62 NULL
63 };
64 const char *agWidgetColorNames[] = {
65 "color",
66 "text-color",
67 "line-color",
68 "shape-color",
69 "border-color",
70 NULL
71 };
72 AG_WidgetPalette agDefaultPalette = {{
73 /* "color" "text-color" "line-color" "shape-color" "border-color" */
74 /*def*/ { {125,125,125,255}, {240,240,240,255}, {50,50,50,255}, {200,200,200,255}, {100,100,100,255} },
75 /*dis*/ { {160,160,160,255}, {240,240,240,255}, {70,70,70,255}, {150,150,150,255}, {100,100,100,255} },
76 /*foc*/ { {125,125,125,255}, {240,240,240,255}, {50,50,50,255}, {200,200,200,255}, {100,100,100,255} },
77 /*hov*/ { {130,130,130,255}, {240,240,240,255}, {50,50,50,255}, {220,220,220,255}, {100,100,100,255} },
78 /*sel*/ { {50,50,120,255}, {255,255,255,255}, {50,50,60,255}, {50,50,50,255}, {100,100,100,255} },
79 }};
80
81 /* Set the parent window/driver pointers on a widget and its children. */
82 static void
SetParentWindow(AG_Widget * wid,AG_Window * win)83 SetParentWindow(AG_Widget *wid, AG_Window *win)
84 {
85 AG_Widget *chld;
86 AG_CursorArea *ca, *caNext;
87
88 wid->window = win;
89
90 if (win != NULL) {
91 wid->drv = (AG_Driver *)OBJECT(win)->parent;
92 wid->drvOps = AGDRIVER_CLASS(wid->drv);
93
94 /*
95 * Commit any previously deferred AG_MapStockCursor()
96 * operation.
97 */
98 for (ca = TAILQ_FIRST(&wid->cursorAreas);
99 ca != TAILQ_END(&wid->cursorAreas);
100 ca = caNext) {
101 caNext = TAILQ_NEXT(ca, cursorAreas);
102 if (ca->stock >= 0 &&
103 ca->stock < wid->drv->nCursors) {
104 AG_Cursor *ac;
105 int i = 0;
106
107 TAILQ_FOREACH(ac, &wid->drv->cursors, cursors) {
108 if (i++ == ca->stock)
109 break;
110 }
111 if (ac != NULL) {
112 ca->c = ac;
113 TAILQ_INSERT_TAIL(&win->cursorAreas,
114 ca, cursorAreas);
115 } else {
116 free(ca);
117 }
118 } else {
119 free(ca);
120 }
121 }
122 TAILQ_INIT(&wid->cursorAreas);
123 } else {
124 wid->drv = NULL;
125 wid->drvOps = NULL;
126 }
127 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
128 SetParentWindow(chld, win);
129 }
130
131 /* Set the parent driver pointers on a widget and its children. */
132 static void
SetParentDriver(AG_Widget * wid,AG_Driver * drv)133 SetParentDriver(AG_Widget *wid, AG_Driver *drv)
134 {
135 AG_Widget *chld;
136
137 if (drv != NULL) {
138 wid->drv = (AG_Driver *)drv;
139 wid->drvOps = AGDRIVER_CLASS(drv);
140 } else {
141 wid->drv = NULL;
142 wid->drvOps = NULL;
143 }
144 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
145 SetParentDriver(chld, drv);
146 }
147
148 static void
OnAttach(AG_Event * event)149 OnAttach(AG_Event *event)
150 {
151 void *parent = AG_SENDER();
152 AG_Widget *w = AG_SELF();
153
154 if (AG_OfClass(parent, "AG_Widget:AG_Window:*") &&
155 AG_OfClass(w, "AG_Widget:*")) {
156 AG_Widget *widParent = (AG_Widget *)parent;
157 Uint i;
158
159 Debug(w, "Attach to window (%s)\n", OBJECT(parent)->name);
160
161 SetParentWindow(w, AGWINDOW(widParent));
162 if (AGWINDOW(widParent)->visible) {
163 w->flags |= AG_WIDGET_UPDATE_WINDOW;
164 AG_PostEvent(NULL, w, "widget-shown", NULL);
165 }
166
167 /*
168 * Widget may have previously been detached from another
169 * driver; textures may need regenerating.
170 */
171 for (i = 0; i < w->nsurfaces; i++) {
172 w->textures[i] = 0;
173 }
174 } else if (AG_OfClass(parent, "AG_Widget:*") &&
175 AG_OfClass(w, "AG_Widget:*")) {
176 AG_Widget *widParent = (AG_Widget *)parent;
177
178 Debug(w, "Attach to widget (%s in %s)\n", OBJECT(parent)->name,
179 widParent->window != NULL ? OBJECT(widParent->window)->name : "NULL");
180
181 SetParentWindow(w, widParent->window);
182 if (widParent->window != NULL &&
183 widParent->window->visible) {
184 AG_PostEvent(NULL, w, "widget-shown", NULL);
185 }
186 } else if (AG_OfClass(parent, "AG_Driver:*") &&
187 AG_OfClass(w, "AG_Widget:AG_Window:*")) {
188 AG_Driver *drvParent = (AG_Driver *)parent;
189
190 Debug(w, "Attach to driver (%s)\n", OBJECT(parent)->name);
191 SetParentDriver(w, drvParent);
192 } else {
193 AG_FatalError("Inconsistent widget attach");
194 }
195 }
196
197 static void
OnDetach(AG_Event * event)198 OnDetach(AG_Event *event)
199 {
200 void *parent = AG_SENDER();
201 AG_Widget *w = AG_SELF();
202
203 if (AG_OfClass(parent, "AG_Widget:*") &&
204 AG_OfClass(w, "AG_Widget:*")) {
205 if (w->window != NULL) {
206 AG_UnmapAllCursors(w->window, w);
207 }
208 SetParentWindow(w, NULL);
209 } else if (AG_OfClass(parent, "AG_Driver:*") &&
210 AG_OfClass(w, "AG_Widget:AG_Window:*")) {
211 SetParentDriver(w, NULL);
212 } else {
213 AG_FatalError("Inconsistent widget detach");
214 }
215 }
216
217 /* Timer callback for AG_RedrawOnTick(). */
218 static Uint32
RedrawOnTickTimeout(AG_Timer * to,AG_Event * event)219 RedrawOnTickTimeout(AG_Timer *to, AG_Event *event)
220 {
221 AG_Widget *wid = event->argv[0].data.p;
222
223 if (wid->window != NULL) {
224 wid->window->dirty = 1;
225 }
226 return (to->ival);
227 }
228
229 /* Timer callback for AG_RedrawOnChange(). */
230 static Uint32
RedrawOnChangeTimeout(AG_Timer * to,AG_Event * event)231 RedrawOnChangeTimeout(AG_Timer *to, AG_Event *event)
232 {
233 AG_Widget *wid = AG_SELF();
234 AG_RedrawTie *rt = AG_PTR(1);
235 AG_Variable *V, Vd;
236 void *p;
237
238 if ((V = AG_GetVariable(wid, rt->name, &p)) == NULL) {
239 return (to->ival);
240 }
241 AG_DerefVariable(&Vd, V);
242 if (!rt->VlastInited || AG_CompareVariables(&Vd, &rt->Vlast) != 0) {
243 if (wid->window != NULL) {
244 wid->window->dirty = 1;
245 }
246 AG_CopyVariable(&rt->Vlast, &Vd);
247 rt->VlastInited = 1;
248 }
249 AG_UnlockVariable(V);
250 return (to->ival);
251 }
252
253 static void
OnShow(AG_Event * event)254 OnShow(AG_Event *event)
255 {
256 AG_Widget *wid = AG_SELF();
257 AG_RedrawTie *rt;
258
259 if (wid->font == NULL)
260 AG_FatalError("%s style not compiled", OBJECT(wid)->name);
261
262 wid->flags |= AG_WIDGET_VISIBLE;
263
264 TAILQ_FOREACH(rt, &wid->redrawTies, redrawTies) {
265 switch (rt->type) {
266 case AG_REDRAW_ON_TICK:
267 AG_AddTimer(wid, &rt->to, rt->ival,
268 RedrawOnTickTimeout, NULL);
269 break;
270 case AG_REDRAW_ON_CHANGE:
271 AG_AddTimer(wid, &rt->to, rt->ival,
272 RedrawOnChangeTimeout, "%p", rt);
273 break;
274 }
275 }
276 }
277
278 static void
OnHide(AG_Event * event)279 OnHide(AG_Event *event)
280 {
281 AG_Widget *wid = AG_SELF();
282 AG_RedrawTie *rt;
283
284 wid->flags &= ~(AG_WIDGET_VISIBLE);
285
286 TAILQ_FOREACH(rt, &wid->redrawTies, redrawTies) {
287 switch (rt->type) {
288 case AG_REDRAW_ON_TICK:
289 case AG_REDRAW_ON_CHANGE:
290 AG_DelTimer(wid, &rt->to);
291 break;
292 }
293 }
294 }
295
296 static void
Init(void * obj)297 Init(void *obj)
298 {
299 AG_Widget *wid = obj;
300 AG_Event *ev;
301
302 OBJECT(wid)->save_pfx = "/widgets";
303 OBJECT(wid)->flags |= AG_OBJECT_NAME_ONATTACH;
304
305 wid->flags = 0;
306 wid->rView = AG_RECT2(-1,-1,-1,-1);
307 wid->rSens = AG_RECT2(0,0,0,0);
308 wid->x = -1;
309 wid->y = -1;
310 wid->w = -1;
311 wid->h = -1;
312 wid->focusFwd = NULL;
313 wid->window = NULL;
314 wid->drv = NULL;
315 wid->drvOps = NULL;
316 wid->nsurfaces = 0;
317 wid->surfaces = NULL;
318 wid->surfaceFlags = NULL;
319 wid->textures = NULL;
320 wid->texcoords = NULL;
321 AG_TblInit(&wid->actions, 32, 0);
322 TAILQ_INIT(&wid->mouseActions);
323 TAILQ_INIT(&wid->keyActions);
324
325 wid->css = NULL;
326 wid->cState = AG_DEFAULT_STATE;
327 wid->font = agDefaultFont;
328 wid->pal = agDefaultPalette;
329
330 AG_SetEvent(wid, "attached", OnAttach, NULL);
331 AG_SetEvent(wid, "detached", OnDetach, NULL);
332 ev = AG_SetEvent(wid, "widget-shown", OnShow, NULL);
333 ev->flags |= AG_EVENT_PROPAGATE;
334 ev = AG_SetEvent(wid, "widget-hidden", OnHide, NULL);
335 ev->flags |= AG_EVENT_PROPAGATE;
336
337 TAILQ_INIT(&wid->redrawTies);
338 TAILQ_INIT(&wid->cursorAreas);
339 }
340
341 /* Arrange for a redraw whenever a given binding value changes. */
342 void
AG_RedrawOnChange(void * obj,int refresh_ms,const char * name)343 AG_RedrawOnChange(void *obj, int refresh_ms, const char *name)
344 {
345 AG_Widget *wid = obj;
346 AG_RedrawTie *rt;
347
348 TAILQ_FOREACH(rt, &wid->redrawTies, redrawTies) {
349 if (rt->type == AG_REDRAW_ON_CHANGE &&
350 strcmp(rt->name, name) == 0 &&
351 rt->ival == refresh_ms)
352 break;
353 }
354 if (rt != NULL) {
355 AG_ResetTimer(wid, &rt->to, refresh_ms);
356 return;
357 }
358
359 rt = Malloc(sizeof(AG_RedrawTie));
360 rt->type = AG_REDRAW_ON_CHANGE;
361 rt->ival = refresh_ms;
362 rt->VlastInited = 0;
363 Strlcpy(rt->name, name, sizeof(rt->name));
364 AG_InitTimer(&rt->to, "redrawTie-", 0);
365 #ifdef AG_DEBUG
366 Strlcat(rt->to.name, name, sizeof(rt->to.name));
367 #endif
368 TAILQ_INSERT_TAIL(&wid->redrawTies, rt, redrawTies);
369
370 if (wid->flags & AG_WIDGET_VISIBLE) {
371 AG_AddTimer(wid, &rt->to, rt->ival, RedrawOnChangeTimeout, "%p", rt);
372 } else {
373 /* Fire from OnShow() */
374 }
375 }
376
377 /* Arrange for an unconditional redraw at a periodic interval. */
378 void
AG_RedrawOnTick(void * obj,int refresh_ms)379 AG_RedrawOnTick(void *obj, int refresh_ms)
380 {
381 AG_Widget *wid = obj;
382 AG_RedrawTie *rt;
383
384 if (refresh_ms == -1) {
385 TAILQ_FOREACH(rt, &wid->redrawTies, redrawTies) {
386 if (rt->type == AG_REDRAW_ON_TICK)
387 break;
388 }
389 if (rt != NULL) {
390 TAILQ_REMOVE(&wid->redrawTies, rt, redrawTies);
391 AG_DelTimer(wid, &rt->to);
392 free(rt);
393 }
394 return;
395 }
396
397 rt = Malloc(sizeof(AG_RedrawTie));
398 rt->type = AG_REDRAW_ON_TICK;
399 rt->ival = refresh_ms;
400 rt->name[0] = '\0';
401 AG_InitTimer(&rt->to, "redrawTick", 0);
402 TAILQ_INSERT_TAIL(&wid->redrawTies, rt, redrawTies);
403
404 if (wid->flags & AG_WIDGET_VISIBLE) {
405 AG_AddTimer(wid, &rt->to, rt->ival, RedrawOnTickTimeout, NULL);
406 } else {
407 /* Fire from OnShow() */
408 }
409 }
410
411 /* Default event handler for "key-down" (for widgets using Actions). */
412 void
AG_WidgetStdKeyDown(AG_Event * event)413 AG_WidgetStdKeyDown(AG_Event *event)
414 {
415 AG_Widget *wid = AG_SELF();
416 int sym = AG_INT(1);
417 int mod = AG_INT(2);
418
419 AG_ExecKeyAction(wid, AG_ACTION_ON_KEYDOWN, sym, mod);
420 }
421
422 /* Default event handler for "key-up" (for widgets using Actions). */
423 void
AG_WidgetStdKeyUp(AG_Event * event)424 AG_WidgetStdKeyUp(AG_Event *event)
425 {
426 AG_Widget *wid = AG_SELF();
427 int sym = AG_INT(1);
428 int mod = AG_INT(2);
429
430 AG_ExecKeyAction(wid, AG_ACTION_ON_KEYUP, sym, mod);
431 }
432
433 /* Default event handler for "mouse-button-down" (for widgets using Actions). */
434 void
AG_WidgetStdMouseButtonDown(AG_Event * event)435 AG_WidgetStdMouseButtonDown(AG_Event *event)
436 {
437 AG_Widget *wid = AG_SELF();
438 int btn = AG_INT(1);
439 int x = AG_INT(2);
440 int y = AG_INT(3);
441
442 if (!AG_WidgetIsFocused(wid)) {
443 AG_WidgetFocus(wid);
444 }
445 AG_ExecMouseAction(wid, AG_ACTION_ON_BUTTONDOWN, btn, x, y);
446 }
447
448 /* Default event handler for "mouse-button-up" (for widgets using Actions). */
449 void
AG_WidgetStdMouseButtonUp(AG_Event * event)450 AG_WidgetStdMouseButtonUp(AG_Event *event)
451 {
452 AG_Widget *wid = AG_SELF();
453 int btn = AG_INT(1);
454 int x = AG_INT(2);
455 int y = AG_INT(3);
456
457 AG_ExecMouseAction(wid, AG_ACTION_ON_BUTTONUP, btn, x, y);
458 }
459
460 /* Tie an action to a mouse-button-down event. */
461 void
AG_ActionOnButtonDown(void * obj,int button,const char * action)462 AG_ActionOnButtonDown(void *obj, int button, const char *action)
463 {
464 AG_Widget *wid = obj;
465 AG_ActionTie *at;
466
467 at = Malloc(sizeof(AG_ActionTie));
468 at->type = AG_ACTION_ON_BUTTONDOWN;
469 at->data.button = (AG_MouseButton)button;
470 Strlcpy(at->action, action, sizeof(at->action));
471 TAILQ_INSERT_TAIL(&wid->mouseActions, at, ties);
472
473 if (AG_FindEventHandler(wid, "mouse-button-down") == NULL)
474 AG_SetEvent(wid, "mouse-button-down", AG_WidgetStdMouseButtonDown, NULL);
475 }
476
477 /* Tie an action to a mouse-button-up event. */
478 void
AG_ActionOnButtonUp(void * obj,int button,const char * action)479 AG_ActionOnButtonUp(void *obj, int button, const char *action)
480 {
481 AG_Widget *wid = obj;
482 AG_ActionTie *at;
483
484 at = Malloc(sizeof(AG_ActionTie));
485 at->type = AG_ACTION_ON_BUTTONUP;
486 at->data.button = (AG_MouseButton)button;
487 Strlcpy(at->action, action, sizeof(at->action));
488 TAILQ_INSERT_TAIL(&wid->mouseActions, at, ties);
489
490 if (AG_FindEventHandler(wid, "mouse-button-up") == NULL)
491 AG_SetEvent(wid, "mouse-button-up", AG_WidgetStdMouseButtonUp, NULL);
492 }
493
494 /* Tie an action to a key-down event. */
495 void
AG_ActionOnKeyDown(void * obj,AG_KeySym sym,AG_KeyMod mod,const char * action)496 AG_ActionOnKeyDown(void *obj, AG_KeySym sym, AG_KeyMod mod, const char *action)
497 {
498 AG_Widget *wid = obj;
499 AG_ActionTie *at;
500
501 at = Malloc(sizeof(AG_ActionTie));
502 at->type = AG_ACTION_ON_KEYDOWN;
503 at->data.key.sym = sym;
504 at->data.key.mod = mod;
505 Strlcpy(at->action, action, sizeof(at->action));
506 TAILQ_INSERT_TAIL(&wid->keyActions, at, ties);
507
508 if (AG_FindEventHandler(wid, "key-down") == NULL)
509 AG_SetEvent(wid, "key-down", AG_WidgetStdKeyDown, NULL);
510 }
511
512 /* Tie an action to a key-up event. */
513 void
AG_ActionOnKeyUp(void * obj,AG_KeySym sym,AG_KeyMod mod,const char * action)514 AG_ActionOnKeyUp(void *obj, AG_KeySym sym, AG_KeyMod mod, const char *action)
515 {
516 AG_Widget *wid = obj;
517 AG_ActionTie *at;
518
519 at = Malloc(sizeof(AG_ActionTie));
520 at->type = AG_ACTION_ON_KEYUP;
521 at->data.key.sym = sym;
522 at->data.key.mod = mod;
523 Strlcpy(at->action, action, sizeof(at->action));
524 TAILQ_INSERT_TAIL(&wid->keyActions, at, ties);
525
526 if (AG_FindEventHandler(wid, "key-up") == NULL)
527 AG_SetEvent(wid, "key-up", AG_WidgetStdKeyUp, NULL);
528 }
529
530 /* Timer callback for AG_ACTION_ON_KEYREPEAT actions. */
531 static Uint32
ActionKeyRepeatTimeout(AG_Timer * to,AG_Event * event)532 ActionKeyRepeatTimeout(AG_Timer *to, AG_Event *event)
533 {
534 AG_Widget *wid = AG_SELF();
535 AG_ActionTie *at = AG_PTR(1);
536 AG_Action *a;
537
538 if (AG_TblLookupPointer(&wid->actions, at->action, (void *)&a) == -1 ||
539 a == NULL) {
540 return (0);
541 }
542 (void)AG_ExecAction(wid, a);
543 return (agKbdRepeat);
544 }
545
546 /* Tie an action to a key-down event, with key repeat. */
547 void
AG_ActionOnKey(void * obj,AG_KeySym sym,AG_KeyMod mod,const char * action)548 AG_ActionOnKey(void *obj, AG_KeySym sym, AG_KeyMod mod, const char *action)
549 {
550 AG_Widget *wid = obj;
551 AG_ActionTie *at;
552
553 at = Malloc(sizeof(AG_ActionTie));
554 at->type = AG_ACTION_ON_KEYREPEAT;
555 at->data.key.sym = sym;
556 at->data.key.mod = mod;
557 AG_InitTimer(&at->data.key.toRepeat, "actionKeyRepeat-", 0);
558 #ifdef AG_DEBUG
559 Strlcat(at->data.key.toRepeat.name, action,
560 sizeof(at->data.key.toRepeat.name));
561 #endif
562 Strlcpy(at->action, action, sizeof(at->action));
563 TAILQ_INSERT_TAIL(&wid->keyActions, at, ties);
564
565 if (AG_FindEventHandler(wid, "key-up") == NULL &&
566 AG_FindEventHandler(wid, "key-down") == NULL) {
567 AG_SetEvent(wid, "key-up", AG_WidgetStdKeyUp, NULL);
568 AG_SetEvent(wid, "key-down", AG_WidgetStdKeyDown, NULL);
569 }
570 }
571
572 /* Configure a widget action. */
573 AG_Action *
AG_ActionFn(void * obj,const char * name,AG_EventFn fn,const char * fnArgs,...)574 AG_ActionFn(void *obj, const char *name, AG_EventFn fn, const char *fnArgs,...)
575 {
576 AG_Widget *w = obj;
577 AG_Action *a;
578
579 AG_ObjectLock(w);
580 a = Malloc(sizeof(AG_Action));
581 a->type = AG_ACTION_FN;
582 a->widget = w;
583 a->fn = AG_SetEvent(w, NULL, fn, NULL);
584 AG_EVENT_GET_ARGS(a->fn, fnArgs);
585 AG_TblInsertPointer(&w->actions, name, a);
586 AG_ObjectUnlock(w);
587 return (a);
588 }
589
590 /* Configure a widget action for setting an integer flag. */
591 AG_Action *
AG_ActionSetInt(void * obj,const char * name,int * p,int val)592 AG_ActionSetInt(void *obj, const char *name, int *p, int val)
593 {
594 AG_Widget *w = obj;
595 AG_Action *a;
596
597 AG_ObjectLock(w);
598 a = Malloc(sizeof(AG_Action));
599 a->type = AG_ACTION_SET_INT;
600 a->widget = w;
601 a->fn = NULL;
602 a->p = (void *)p;
603 a->val = val;
604 AG_TblInsertPointer(&w->actions, name, a);
605 AG_ObjectUnlock(w);
606 return (a);
607 }
608
609 /* Configure a widget action for toggling an integer flag. */
610 AG_Action *
AG_ActionToggleInt(void * obj,const char * name,int * p)611 AG_ActionToggleInt(void *obj, const char *name, int *p)
612 {
613 AG_Widget *w = obj;
614 AG_Action *a;
615
616 AG_ObjectLock(w);
617 a = Malloc(sizeof(AG_Action));
618 a->type = AG_ACTION_TOGGLE_INT;
619 a->widget = w;
620 a->fn = NULL;
621 a->p = (void *)p;
622 AG_TblInsertPointer(&w->actions, name, a);
623 AG_ObjectUnlock(w);
624 return (a);
625 }
626
627 /* Configure a widget action for setting bitwise flags. */
628 AG_Action *
AG_ActionSetFlag(void * obj,const char * name,Uint * p,Uint bitmask,int val)629 AG_ActionSetFlag(void *obj, const char *name, Uint *p, Uint bitmask, int val)
630 {
631 AG_Widget *w = obj;
632 AG_Action *a;
633
634 AG_ObjectLock(w);
635 a = Malloc(sizeof(AG_Action));
636 a->type = AG_ACTION_SET_INT;
637 a->widget = w;
638 a->fn = NULL;
639 a->p = (void *)p;
640 a->bitmask = bitmask;
641 a->val = val;
642 AG_TblInsertPointer(&w->actions, name, a);
643 AG_ObjectUnlock(w);
644 return (a);
645 }
646
647 /* Configure a widget action for toggling bitwise flags. */
648 AG_Action *
AG_ActionToggleFlag(void * obj,const char * name,Uint * p,Uint bitmask)649 AG_ActionToggleFlag(void *obj, const char *name, Uint *p, Uint bitmask)
650 {
651 AG_Widget *w = obj;
652 AG_Action *a;
653
654 AG_ObjectLock(w);
655 a = Malloc(sizeof(AG_Action));
656 a->type = AG_ACTION_TOGGLE_FLAG;
657 a->widget = w;
658 a->fn = NULL;
659 a->p = (void *)p;
660 a->bitmask = bitmask;
661 AG_TblInsertPointer(&w->actions, name, a);
662 AG_ObjectUnlock(w);
663 return (a);
664 }
665
666 /* Execute an action (usually called internally from AG_ExecFooAction()) */
667 int
AG_ExecAction(void * obj,AG_Action * a)668 AG_ExecAction(void *obj, AG_Action *a)
669 {
670 switch (a->type) {
671 case AG_ACTION_FN:
672 if (a->fn != NULL) {
673 a->fn->fn.fnVoid(a->fn);
674 return (1);
675 }
676 return (0);
677 case AG_ACTION_SET_INT:
678 *(int *)a->p = a->val;
679 return (1);
680 case AG_ACTION_TOGGLE_INT:
681 *(int *)a->p = !(*(int *)a->p);
682 return (1);
683 case AG_ACTION_SET_FLAG:
684 if (a->val) {
685 *(Uint *)a->p |= a->bitmask;
686 } else {
687 *(Uint *)a->p &= ~(a->bitmask);
688 }
689 return (1);
690 case AG_ACTION_TOGGLE_FLAG:
691 if (*(Uint *)a->p & a->bitmask) {
692 *(Uint *)a->p &= ~(a->bitmask);
693 } else {
694 *(Uint *)a->p |= a->bitmask;
695 }
696 return (1);
697 }
698 return (0);
699 }
700
701 /*
702 * Run any action tied to a mouse-button event. Exceptionally, we pass
703 * mouse event arguments to the function.
704 */
705 int
AG_ExecMouseAction(void * obj,AG_ActionEventType et,int button,int xCurs,int yCurs)706 AG_ExecMouseAction(void *obj, AG_ActionEventType et, int button,
707 int xCurs, int yCurs)
708 {
709 AG_Widget *wid = obj;
710 AG_ActionTie *at;
711 AG_Action *a;
712
713 #ifdef AG_DEBUG
714 if (et != AG_ACTION_ON_BUTTONDOWN &&
715 et != AG_ACTION_ON_BUTTONUP)
716 AG_FatalError("Invalid type arg to AG_ExecMouseAction()");
717 #endif
718 TAILQ_FOREACH(at, &wid->mouseActions, ties) {
719 if (at->type == et &&
720 ((button == at->data.button) ||
721 (at->data.button == AG_MOUSE_ANY)))
722 break;
723 }
724 if (at == NULL) {
725 return (0);
726 }
727 if (AG_TblLookupPointer(&wid->actions, at->action, (void *)&a) == -1 ||
728 a == NULL) {
729 return (0);
730 }
731 if (a->fn != NULL) {
732 AG_PostEventByPtr(NULL, wid, a->fn, "%i,%i,%i", button,
733 xCurs, yCurs);
734 return (1);
735 }
736 return (0);
737 }
738
739 /* Run any action tied to a key-down event. */
740 int
AG_ExecKeyAction(void * obj,AG_ActionEventType et,AG_KeySym sym,AG_KeyMod mod)741 AG_ExecKeyAction(void *obj, AG_ActionEventType et, AG_KeySym sym, AG_KeyMod mod)
742 {
743 AG_Widget *wid = obj;
744 AG_ActionTie *at;
745 AG_Action *a;
746 int rv;
747
748 #ifdef AG_DEBUG
749 if (et != AG_ACTION_ON_KEYDOWN &&
750 et != AG_ACTION_ON_KEYUP)
751 AG_FatalError("AG_ExecKeyAction() type");
752 #endif
753 TAILQ_FOREACH(at, &wid->keyActions, ties) {
754 if (at->type != et &&
755 at->type != AG_ACTION_ON_KEYREPEAT) {
756 continue;
757 }
758 if ((at->data.key.mod == AG_KEYMOD_ANY ||
759 at->data.key.mod & mod) &&
760 (at->data.key.sym == AG_KEY_ANY ||
761 at->data.key.sym == sym))
762 break;
763 }
764 if (at == NULL) {
765 return (0);
766 }
767 if (AG_TblLookupPointer(&wid->actions, at->action, (void *)&a) == -1 ||
768 a == NULL)
769 return (0);
770
771 rv = AG_ExecAction(wid, a);
772
773 if (at->type == AG_ACTION_ON_KEYREPEAT) {
774 if (et == AG_ACTION_ON_KEYDOWN) {
775 AG_AddTimer(wid, &at->data.key.toRepeat, agKbdDelay,
776 ActionKeyRepeatTimeout, "%p", at);
777 } else {
778 AG_DelTimer(wid, &at->data.key.toRepeat);
779 }
780 }
781 return (rv);
782 }
783
784 static void *
WidgetFindPath(const AG_Object * parent,const char * name)785 WidgetFindPath(const AG_Object *parent, const char *name)
786 {
787 char node_name[AG_OBJECT_PATH_MAX];
788 void *rv;
789 char *s;
790 AG_Object *chld;
791
792 Strlcpy(node_name, name, sizeof(node_name));
793 if ((s = strchr(node_name, '/')) != NULL) {
794 *s = '\0';
795 }
796 if (AG_OfClass(parent, "AG_View:*")) {
797 AG_Driver *drv = AGDRIVER(parent);
798 AG_Window *win;
799
800 AG_FOREACH_WINDOW(win, drv) {
801 if (strcmp(AGOBJECT(win)->name, node_name) != 0) {
802 continue;
803 }
804 if ((s = strchr(name, '/')) != NULL) {
805 rv = WidgetFindPath(AGOBJECT(win), &s[1]);
806 if (rv != NULL) {
807 return (rv);
808 } else {
809 return (NULL);
810 }
811 }
812 return (win);
813 }
814 } else {
815 TAILQ_FOREACH(chld, &parent->children, cobjs) {
816 if (strcmp(chld->name, node_name) != 0) {
817 continue;
818 }
819 if ((s = strchr(name, '/')) != NULL) {
820 rv = WidgetFindPath(chld, &s[1]);
821 if (rv != NULL) {
822 return (rv);
823 } else {
824 return (NULL);
825 }
826 }
827 return (chld);
828 }
829 }
830 return (NULL);
831 }
832
833 /*
834 * Find a widget by name (e.g., "Window/Widget1/Widget2"). This works
835 * similarly to the more general AG_ObjectFind(3). Return value is only
836 * valid as long as the Driver VFS is locked.
837 */
838 void *
AG_WidgetFind(void * obj,const char * name)839 AG_WidgetFind(void *obj, const char *name)
840 {
841 AG_Driver *drv = obj;
842 void *rv;
843
844 #ifdef AG_DEBUG
845 if (name[0] != '/')
846 AG_FatalError("WidgetFind: Not an absolute path: %s", name);
847 #endif
848 AG_LockVFS(drv);
849 rv = WidgetFindPath(OBJECT(drv), &name[1]);
850 AG_UnlockVFS(drv);
851 if (rv == NULL) {
852 AG_SetError(_("The widget `%s' does not exist."), name);
853 }
854 return (rv);
855 }
856
857 /* Set the FOCUSABLE flag on a widget. */
858 void
AG_WidgetSetFocusable(void * obj,int flag)859 AG_WidgetSetFocusable(void *obj, int flag)
860 {
861 AG_Widget *wid = obj;
862
863 AG_ObjectLock(wid);
864 AG_SETFLAGS(wid->flags, AG_WIDGET_FOCUSABLE, flag);
865 AG_ObjectUnlock(wid);
866 }
867
868 /* Set widget to "enabled" state for input. */
869 void
AG_WidgetEnable(void * obj)870 AG_WidgetEnable(void *obj)
871 {
872 AG_Widget *wid = obj;
873
874 AG_ObjectLock(wid);
875 if (wid->flags & AG_WIDGET_DISABLED) {
876 wid->flags &= ~(AG_WIDGET_DISABLED);
877 AG_PostEvent(NULL, wid, "widget-enabled", NULL);
878 AG_Redraw(wid);
879 }
880 AG_ObjectUnlock(wid);
881 }
882
883 /* Set widget to "disabled" state for input. */
884 void
AG_WidgetDisable(void * obj)885 AG_WidgetDisable(void *obj)
886 {
887 AG_Widget *wid = obj;
888
889 AG_ObjectLock(wid);
890 if (!(wid->flags & AG_WIDGET_DISABLED)) {
891 wid->flags |= AG_WIDGET_DISABLED;
892 AG_PostEvent(NULL, wid, "widget-disabled", NULL);
893 AG_Redraw(wid);
894 }
895 AG_ObjectUnlock(wid);
896 }
897
898 /* Arrange for a widget to automatically forward focus to another widget. */
899 void
AG_WidgetForwardFocus(void * obj,void * objFwd)900 AG_WidgetForwardFocus(void *obj, void *objFwd)
901 {
902 AG_Widget *wid = obj;
903
904 AG_ObjectLock(wid);
905 if (objFwd != NULL) {
906 wid->flags |= AG_WIDGET_FOCUSABLE;
907 wid->focusFwd = WIDGET(objFwd);
908 } else {
909 wid->flags &= ~(AG_WIDGET_FOCUSABLE);
910 wid->focusFwd = NULL;
911 }
912 AG_ObjectUnlock(wid);
913 }
914
915 static void
Destroy(void * obj)916 Destroy(void *obj)
917 {
918 AG_Widget *wid = obj;
919 AG_CursorArea *ca, *caNext;
920 AG_RedrawTie *rt, *rtNext;
921 AG_ActionTie *at, *atNext;
922 AG_Variable *V;
923 Uint i, j;
924
925 for (ca = TAILQ_FIRST(&wid->cursorAreas);
926 ca != TAILQ_END(&wid->cursorAreas);
927 ca = caNext) {
928 caNext = TAILQ_NEXT(ca, cursorAreas);
929 free(ca);
930 }
931 for (rt = TAILQ_FIRST(&wid->redrawTies);
932 rt != TAILQ_END(&wid->redrawTies);
933 rt = rtNext) {
934 rtNext = TAILQ_NEXT(rt, redrawTies);
935 free(rt);
936 }
937 for (at = TAILQ_FIRST(&wid->mouseActions);
938 at != TAILQ_END(&wid->mouseActions);
939 at = atNext) {
940 atNext = TAILQ_NEXT(at, ties);
941 free(at);
942 }
943 for (at = TAILQ_FIRST(&wid->keyActions);
944 at != TAILQ_END(&wid->keyActions);
945 at = atNext) {
946 atNext = TAILQ_NEXT(at, ties);
947 free(at);
948 }
949
950 /* Free the action tables. */
951 AG_TBL_FOREACH(V, i,j, &wid->actions) {
952 Free(V->data.p);
953 }
954 AG_TblDestroy(&wid->actions);
955
956 /*
957 * Free surfaces. We can assume that drivers have already deleted
958 * any associated resources.
959 */
960 for (i = 0; i < wid->nsurfaces; i++) {
961 if (wid->surfaces[i] != NULL && !WSURFACE_NODUP(wid,i))
962 AG_SurfaceFree(wid->surfaces[i]);
963 }
964 Free(wid->surfaces);
965 Free(wid->surfaceFlags);
966 Free(wid->textures);
967 Free(wid->texcoords);
968 }
969
970 #ifdef HAVE_OPENGL
971 /*
972 * Variants of AG_WidgetBlit*() without explicit source or destination
973 * rectangle parameter (for OpenGL-only widgets).
974 */
975 void
AG_WidgetBlitGL(void * obj,AG_Surface * su,float w,float h)976 AG_WidgetBlitGL(void *obj, AG_Surface *su, float w, float h)
977 {
978 AG_Widget *wid = obj;
979 wid->drvOps->blitSurfaceGL(wid->drv, wid, su, w, h);
980 }
981 void
AG_WidgetBlitSurfaceGL(void * obj,int name,float w,float h)982 AG_WidgetBlitSurfaceGL(void *obj, int name, float w, float h)
983 {
984 AG_Widget *wid = obj;
985 wid->drvOps->blitSurfaceFromGL(wid->drv, wid, name, w, h);
986 }
987 void
AG_WidgetBlitSurfaceFlippedGL(void * obj,int name,float w,float h)988 AG_WidgetBlitSurfaceFlippedGL(void *obj, int name, float w, float h)
989 {
990 AG_Widget *wid = obj;
991 wid->drvOps->blitSurfaceFlippedGL(wid->drv, wid, name, w, h);
992 }
993
994 /*
995 * Release/backup and regenerate the GL resources associated with a widget
996 * and its descendents.
997 *
998 * If some textures exist without a corresponding surface, allocate a
999 * software surface and copy their contents to be later restored. These
1000 * routines are necessary for dealing with GL context loss.
1001 */
1002 void
AG_WidgetFreeResourcesGL(void * obj)1003 AG_WidgetFreeResourcesGL(void *obj)
1004 {
1005 AG_Widget *wid = obj, *cwid;
1006
1007 if (wid->drvOps->backupSurfaces != NULL) {
1008 wid->drvOps->backupSurfaces(wid->drv, wid);
1009 }
1010 OBJECT_FOREACH_CHILD(cwid, wid, ag_widget)
1011 AG_WidgetFreeResourcesGL(cwid);
1012 }
1013 void
AG_WidgetRegenResourcesGL(void * obj)1014 AG_WidgetRegenResourcesGL(void *obj)
1015 {
1016 AG_Widget *wid = obj, *cwid;
1017
1018 if (wid->drvOps->restoreSurfaces != NULL) {
1019 wid->drvOps->restoreSurfaces(wid->drv, wid);
1020 }
1021 OBJECT_FOREACH_CHILD(cwid, wid, ag_widget)
1022 AG_WidgetRegenResourcesGL(cwid);
1023 }
1024 #endif /* HAVE_OPENGL */
1025
1026 /* Acquire widget focus */
1027 static __inline__ void
FocusWidget(AG_Widget * w)1028 FocusWidget(AG_Widget *w)
1029 {
1030 w->flags |= AG_WIDGET_FOCUSED;
1031 if (w->window != NULL) {
1032 AG_PostEvent(w->window, w, "widget-gainfocus", NULL);
1033 w->window->nFocused++;
1034 w->window->dirty = 1;
1035 } else {
1036 Verbose("%s: Gained focus, but no parent window\n",
1037 OBJECT(w)->name);
1038 }
1039 }
1040
1041 /* Give up widget focus */
1042 static __inline__ void
UnfocusWidget(AG_Widget * w)1043 UnfocusWidget(AG_Widget *w)
1044 {
1045 w->flags &= ~(AG_WIDGET_FOCUSED);
1046 if (w->window != NULL) {
1047 AG_PostEvent(w->window, w, "widget-lostfocus", NULL);
1048 w->window->nFocused--;
1049 w->window->dirty = 1;
1050 }
1051 }
1052
1053 /* Remove focus from a widget and its children. */
1054 void
AG_WidgetUnfocus(void * p)1055 AG_WidgetUnfocus(void *p)
1056 {
1057 AG_Widget *wid = p, *cwid;
1058
1059 AG_ObjectLock(wid);
1060 if (wid->focusFwd != NULL) {
1061 AG_ObjectLock(wid->focusFwd);
1062 if (wid->focusFwd->flags & AG_WIDGET_FOCUSED) {
1063 UnfocusWidget(wid->focusFwd);
1064 }
1065 AG_ObjectUnlock(wid->focusFwd);
1066 }
1067 if (wid->flags & AG_WIDGET_FOCUSED) {
1068 UnfocusWidget(wid);
1069 }
1070 OBJECT_FOREACH_CHILD(cwid, wid, ag_widget) {
1071 AG_WidgetUnfocus(cwid);
1072 }
1073 AG_ObjectUnlock(wid);
1074 }
1075
1076 /* Move the focus over a widget (and its parents). */
1077 int
AG_WidgetFocus(void * obj)1078 AG_WidgetFocus(void *obj)
1079 {
1080 AG_Widget *wid = obj, *wParent = wid;
1081 AG_Window *win = wid->window;
1082
1083 AG_LockVFS(wid);
1084 AG_ObjectLock(wid);
1085
1086 if (AG_WidgetIsFocused(wid))
1087 goto out;
1088
1089 if (!(wid->flags & AG_WIDGET_FOCUSABLE)) {
1090 if (wid->focusFwd != NULL &&
1091 !(wid->focusFwd->flags & AG_WIDGET_FOCUSED)) {
1092 AG_ObjectLock(wid->focusFwd);
1093 FocusWidget(wid->focusFwd);
1094 AG_ObjectUnlock(wid->focusFwd);
1095 goto out;
1096 }
1097 goto fail;
1098 }
1099
1100 /* Remove any existing focus. XXX inefficient */
1101 if (win != NULL && win->nFocused > 0)
1102 AG_WidgetUnfocus(win);
1103
1104 /*
1105 * Set the focus flag on the widget and its parents, up
1106 * to the parent window.
1107 */
1108 do {
1109 if (AG_OfClass(wParent, "AG_Widget:AG_Window:*")) {
1110 AG_WindowFocus(AGWINDOW(wParent));
1111 break;
1112 }
1113 AG_ObjectLock(wParent);
1114 if ((wParent->flags & AG_WIDGET_FOCUSED) == 0) {
1115 if (wParent->focusFwd != NULL &&
1116 !(wParent->focusFwd->flags & AG_WIDGET_FOCUSED)) {
1117 FocusWidget(wParent->focusFwd);
1118 }
1119 FocusWidget(wParent);
1120 }
1121 AG_ObjectUnlock(wParent);
1122 } while ((wParent = OBJECT(wParent)->parent) != NULL);
1123 out:
1124 AG_ObjectUnlock(wid);
1125 AG_UnlockVFS(wid);
1126 return (1);
1127 fail:
1128 AG_ObjectUnlock(wid);
1129 AG_UnlockVFS(wid);
1130 return (0);
1131 }
1132
1133 #ifdef HAVE_OPENGL
1134
1135 static void
DrawPrologueGL_Reshape(AG_Widget * wid)1136 DrawPrologueGL_Reshape(AG_Widget *wid)
1137 {
1138 glMatrixMode(GL_PROJECTION); glPushMatrix();
1139 glMatrixMode(GL_MODELVIEW); glPushMatrix();
1140
1141 AG_PostEvent(NULL, wid, "widget-reshape", NULL);
1142 wid->flags &= ~(AG_WIDGET_GL_RESHAPE);
1143
1144 glGetFloatv(GL_PROJECTION, wid->gl.mProjection);
1145 glGetFloatv(GL_MODELVIEW, wid->gl.mModelview);
1146
1147 glMatrixMode(GL_PROJECTION); glPopMatrix();
1148 glMatrixMode(GL_MODELVIEW); glPopMatrix();
1149 }
1150
1151 static void
DrawPrologueGL(AG_Widget * wid)1152 DrawPrologueGL(AG_Widget *wid)
1153 {
1154 Uint hView;
1155
1156 AG_PostEvent(NULL, wid, "widget-underlay", NULL);
1157
1158 glPushAttrib(GL_TRANSFORM_BIT | GL_VIEWPORT_BIT | GL_TEXTURE_BIT);
1159
1160 if (wid->flags & AG_WIDGET_GL_RESHAPE)
1161 DrawPrologueGL_Reshape(wid);
1162
1163 hView = AGDRIVER_SINGLE(wid->drv) ? AGDRIVER_SW(wid->drv)->h :
1164 HEIGHT(wid->window);
1165 glViewport(wid->rView.x1, (hView - wid->rView.y2),
1166 WIDTH(wid), HEIGHT(wid));
1167
1168 glMatrixMode(GL_TEXTURE);
1169 glPushMatrix();
1170 glLoadIdentity();
1171
1172 glMatrixMode(GL_PROJECTION);
1173 glPushMatrix();
1174 glLoadMatrixf(wid->gl.mProjection);
1175
1176 glMatrixMode(GL_MODELVIEW);
1177 glPushMatrix();
1178 glLoadMatrixf(wid->gl.mModelview);
1179
1180 glDisable(GL_CLIP_PLANE0);
1181 glDisable(GL_CLIP_PLANE1);
1182 glDisable(GL_CLIP_PLANE2);
1183 glDisable(GL_CLIP_PLANE3);
1184 }
1185
1186 static void
DrawEpilogueGL(AG_Widget * wid)1187 DrawEpilogueGL(AG_Widget *wid)
1188 {
1189 glMatrixMode(GL_MODELVIEW); glPopMatrix();
1190 glMatrixMode(GL_PROJECTION); glPopMatrix();
1191 glMatrixMode(GL_TEXTURE); glPopMatrix();
1192
1193 glPopAttrib(); /* GL_TRANSFORM_BIT | GL_VIEWPORT_BIT */
1194
1195 AG_PostEvent(NULL, wid, "widget-overlay", NULL);
1196 }
1197 #endif /* HAVE_OPENGL */
1198
1199 /*
1200 * Render a widget to the display.
1201 * Must be invoked from GUI rendering context.
1202 */
1203 void
AG_WidgetDraw(void * p)1204 AG_WidgetDraw(void *p)
1205 {
1206 AG_Widget *wid = p;
1207
1208 AG_ObjectLock(wid);
1209
1210 if (!(wid->flags & AG_WIDGET_VISIBLE) ||
1211 (wid->flags & AG_WIDGET_UNDERSIZE) ||
1212 WIDGET_OPS(wid)->draw == NULL)
1213 goto out;
1214
1215 if (wid->flags & AG_WIDGET_DISABLED) { wid->cState = AG_DISABLED_STATE; }
1216 else if (wid->flags & AG_WIDGET_MOUSEOVER) { wid->cState = AG_HOVER_STATE; }
1217 else if (wid->flags & AG_WIDGET_FOCUSED) { wid->cState = AG_FOCUSED_STATE; }
1218 else { wid->cState = AG_DEFAULT_STATE; }
1219
1220 if (wid->flags & AG_WIDGET_USE_TEXT) {
1221 AG_PushTextState();
1222 AG_TextFont(wid->font);
1223 AG_TextColor(wid->pal.c[wid->cState][AG_TEXT_COLOR]);
1224 }
1225 #ifdef HAVE_OPENGL
1226 if (wid->flags & AG_WIDGET_USE_OPENGL)
1227 DrawPrologueGL(wid);
1228 #endif
1229
1230 WIDGET_OPS(wid)->draw(wid);
1231
1232 #ifdef HAVE_OPENGL
1233 if (wid->flags & AG_WIDGET_USE_OPENGL)
1234 DrawEpilogueGL(wid);
1235 #endif
1236 if (wid->flags & AG_WIDGET_USE_TEXT)
1237 AG_PopTextState();
1238 out:
1239 AG_ObjectUnlock(wid);
1240 }
1241
1242 static void
SizeRequest(void * p,AG_SizeReq * r)1243 SizeRequest(void *p, AG_SizeReq *r)
1244 {
1245 r->w = 0;
1246 r->h = 0;
1247 }
1248
1249 static int
SizeAllocate(void * p,const AG_SizeAlloc * a)1250 SizeAllocate(void *p, const AG_SizeAlloc *a)
1251 {
1252 return (0);
1253 }
1254
1255 void
AG_WidgetSizeReq(void * obj,AG_SizeReq * r)1256 AG_WidgetSizeReq(void *obj, AG_SizeReq *r)
1257 {
1258 AG_Widget *w = obj;
1259
1260 r->w = 0;
1261 r->h = 0;
1262
1263 AG_ObjectLock(w);
1264 if (w->flags & AG_WIDGET_USE_TEXT) {
1265 AG_PushTextState();
1266 AG_TextFont(w->font);
1267 }
1268 if (WIDGET_OPS(w)->size_request != NULL) {
1269 WIDGET_OPS(w)->size_request(w, r);
1270 }
1271 if (w->flags & AG_WIDGET_USE_TEXT) {
1272 AG_PopTextState();
1273 }
1274 AG_ObjectUnlock(w);
1275 }
1276
1277 void
AG_WidgetSizeAlloc(void * obj,AG_SizeAlloc * a)1278 AG_WidgetSizeAlloc(void *obj, AG_SizeAlloc *a)
1279 {
1280 AG_Widget *w = obj;
1281
1282 AG_ObjectLock(w);
1283
1284 if (w->flags & AG_WIDGET_USE_TEXT) {
1285 AG_PushTextState();
1286 AG_TextFont(w->font);
1287 }
1288 if (a->w <= 0 || a->h <= 0) {
1289 a->w = 0;
1290 a->h = 0;
1291 w->flags |= AG_WIDGET_UNDERSIZE;
1292 } else {
1293 w->flags &= ~(AG_WIDGET_UNDERSIZE);
1294 }
1295 w->x = a->x;
1296 w->y = a->y;
1297 w->w = a->w;
1298 w->h = a->h;
1299 if (WIDGET_OPS(w)->size_allocate != NULL) {
1300 if (WIDGET_OPS(w)->size_allocate(w, a) == -1) {
1301 w->flags |= AG_WIDGET_UNDERSIZE;
1302 } else {
1303 w->flags &= ~(AG_WIDGET_UNDERSIZE);
1304 }
1305 }
1306 if (w->flags & AG_WIDGET_USE_TEXT) {
1307 AG_PopTextState();
1308 }
1309 #ifdef HAVE_OPENGL
1310 w->flags |= AG_WIDGET_GL_RESHAPE;
1311 #endif
1312 AG_ObjectUnlock(w);
1313 }
1314
1315 /*
1316 * Test whether view coordinates x,y lie in widget's sensitivity rectangle
1317 * (intersected against those of all parent widgets).
1318 */
1319 int
AG_WidgetSensitive(void * obj,int x,int y)1320 AG_WidgetSensitive(void *obj, int x, int y)
1321 {
1322 AG_Widget *wt = WIDGET(obj);
1323 AG_Widget *wtParent = wt;
1324 AG_Rect2 rx = wt->rSens;
1325
1326 while ((wtParent = OBJECT(wtParent)->parent) != NULL) {
1327 if (AG_OfClass(wtParent, "AG_Widget:AG_Window:*")) {
1328 break;
1329 }
1330 rx = AG_RectIntersect2(&rx, &wtParent->rSens);
1331 }
1332 return AG_RectInside2(&rx, x,y);
1333 }
1334
1335 /*
1336 * Search for a focused widget inside a window. Return value is only valid
1337 * as long as the Driver VFS is locked.
1338 */
1339 AG_Widget *
AG_WidgetFindFocused(void * p)1340 AG_WidgetFindFocused(void *p)
1341 {
1342 AG_Widget *wid = p;
1343 AG_Widget *cwid, *fwid;
1344
1345 AG_LockVFS(wid);
1346 AG_ObjectLock(wid);
1347
1348 if (!AG_OfClass(wid, "AG_Widget:AG_Window:*")) {
1349 if ((wid->flags & AG_WIDGET_FOCUSED) == 0 ||
1350 (wid->flags & AG_WIDGET_VISIBLE) == 0 ||
1351 (wid->flags & AG_WIDGET_DISABLED)) {
1352 goto fail;
1353 }
1354 }
1355 /* Search for a better match. */
1356 OBJECT_FOREACH_CHILD(cwid, wid, ag_widget) {
1357 if ((fwid = AG_WidgetFindFocused(cwid)) != NULL) {
1358 AG_ObjectUnlock(wid);
1359 AG_UnlockVFS(wid);
1360 return (fwid);
1361 }
1362 }
1363
1364 AG_ObjectUnlock(wid);
1365 AG_UnlockVFS(wid);
1366 return (wid);
1367 fail:
1368 AG_ObjectUnlock(wid);
1369 AG_UnlockVFS(wid);
1370 return (NULL);
1371 }
1372
1373 /* Compute the absolute view coordinates of a widget and its descendents. */
1374 void
AG_WidgetUpdateCoords(void * obj,int x,int y)1375 AG_WidgetUpdateCoords(void *obj, int x, int y)
1376 {
1377 AG_Widget *wid = obj, *chld;
1378 AG_Rect2 rPrev;
1379
1380 AG_LockVFS(wid);
1381 AG_ObjectLock(wid);
1382 wid->flags &= ~(AG_WIDGET_UPDATE_WINDOW);
1383
1384 if (wid->drv != NULL && AGDRIVER_MULTIPLE(wid->drv) &&
1385 AG_OfClass(wid, "AG_Widget:AG_Window:*")) {
1386 /* Multiple-window drivers use window coordinate systems */
1387 x = 0;
1388 y = 0;
1389 }
1390
1391 rPrev = wid->rView;
1392 wid->rView.x1 = x;
1393 wid->rView.y1 = y;
1394 wid->rView.w = wid->w;
1395 wid->rView.h = wid->h;
1396 wid->rView.x2 = x + wid->w;
1397 wid->rView.y2 = y + wid->h;
1398
1399 wid->rSens.x1 = x;
1400 wid->rSens.y1 = y;
1401 wid->rSens.w = wid->w;
1402 wid->rSens.h = wid->h;
1403 wid->rSens.x2 = x + wid->w;
1404 wid->rSens.y2 = y + wid->h;
1405
1406 if (AG_RectCompare2(&wid->rView, &rPrev) != 0) {
1407 AG_PostEvent(NULL, wid, "widget-moved", NULL);
1408 #ifdef HAVE_OPENGL
1409 wid->flags |= AG_WIDGET_GL_RESHAPE;
1410 #endif
1411 }
1412 OBJECT_FOREACH_CHILD(chld, wid, ag_widget) {
1413 AG_WidgetUpdateCoords(chld,
1414 wid->rView.x1 + chld->x,
1415 wid->rView.y1 + chld->y);
1416 }
1417
1418 AG_ObjectUnlock(wid);
1419 AG_UnlockVFS(wid);
1420 }
1421
1422 /* Parse a generic size specification. */
1423 enum ag_widget_sizespec
AG_WidgetParseSizeSpec(const char * input,int * w)1424 AG_WidgetParseSizeSpec(const char *input, int *w)
1425 {
1426 char spec[1024], *p;
1427 size_t len;
1428
1429 Strlcpy(spec, input, sizeof(spec));
1430 len = strlen(spec);
1431 if (len == 0) { goto syntax; }
1432 p = &spec[len-1];
1433
1434 switch (*p) {
1435 case '-':
1436 *w = 0;
1437 return (AG_WIDGET_FILL);
1438 case '%':
1439 *p = '\0';
1440 *w = (int)strtol(spec, NULL, 10);
1441 return (AG_WIDGET_PERCENT);
1442 case '>':
1443 if (spec[0] != '<') { goto syntax; }
1444 *p = '\0';
1445 AG_TextSize(&spec[1], w, NULL);
1446 return (AG_WIDGET_STRINGLEN);
1447 case 'x':
1448 if (p > &spec[0] && p[-1] != 'p') { goto syntax; }
1449 p[-1] = '\0';
1450 *w = (int)strtol(spec, NULL, 10);
1451 return (AG_WIDGET_PIXELS);
1452 }
1453 syntax:
1454 Verbose("Warning: Bad SizeSpec: \"%s\"\n", input);
1455 *w = 0;
1456 return (AG_WIDGET_BAD_SPEC);
1457 }
1458
1459 int
AG_WidgetScrollDelta(Uint32 * t1)1460 AG_WidgetScrollDelta(Uint32 *t1)
1461 {
1462 Uint32 t2 = AG_GetTicks();
1463 int delta;
1464
1465 if (*t1 != 0 && ((delta = (t2 - *t1))) < 250) {
1466 return (((250-delta)<<3)>>9);
1467 }
1468 *t1 = AG_GetTicks();
1469 return (1);
1470 }
1471
1472 /* Show a widget */
1473 void
AG_WidgetShow(void * obj)1474 AG_WidgetShow(void *obj)
1475 {
1476 AG_Widget *wid = obj;
1477
1478 AG_ObjectLock(wid);
1479 wid->flags &= ~(AG_WIDGET_HIDE);
1480 AG_PostEvent(NULL, wid, "widget-shown", NULL);
1481 AG_WindowUpdate(wid->window);
1482 AG_ObjectUnlock(wid);
1483 }
1484
1485 /* Hide a widget */
1486 void
AG_WidgetHide(void * obj)1487 AG_WidgetHide(void *obj)
1488 {
1489 AG_Widget *wid = obj;
1490
1491 AG_ObjectLock(wid);
1492 wid->flags |= AG_WIDGET_HIDE;
1493 AG_PostEvent(NULL, wid, "widget-hidden", NULL);
1494 AG_WindowUpdate(wid->window);
1495 AG_ObjectUnlock(wid);
1496 }
1497
1498 /* Make a widget and all of its children visible. */
1499 void
AG_WidgetShowAll(void * p)1500 AG_WidgetShowAll(void *p)
1501 {
1502 AG_Widget *wid = p;
1503 AG_Widget *chld;
1504
1505 AG_LockVFS(wid);
1506 AG_ObjectLock(wid);
1507
1508 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
1509 AG_WidgetShowAll(chld);
1510
1511 AG_PostEvent(NULL, wid, "widget-shown", NULL);
1512
1513 AG_ObjectUnlock(wid);
1514 AG_UnlockVFS(wid);
1515 }
1516
1517 /* Make a widget and all of its children invisible. */
1518 void
AG_WidgetHideAll(void * p)1519 AG_WidgetHideAll(void *p)
1520 {
1521 AG_Widget *wid = p;
1522 AG_Widget *chld;
1523
1524 AG_LockVFS(wid);
1525 AG_ObjectLock(wid);
1526
1527 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
1528 AG_WidgetHideAll(chld);
1529
1530 AG_PostEvent(NULL, wid, "widget-hidden", NULL);
1531
1532 AG_ObjectUnlock(wid);
1533 AG_UnlockVFS(wid);
1534 }
1535
1536 static void *
FindAtPoint(AG_Widget * parent,const char * type,int x,int y)1537 FindAtPoint(AG_Widget *parent, const char *type, int x, int y)
1538 {
1539 AG_Widget *chld;
1540 void *p;
1541
1542 OBJECT_FOREACH_CHILD(chld, parent, ag_widget) {
1543 if ((p = FindAtPoint(chld, type, x, y)) != NULL)
1544 return (p);
1545 }
1546 if ((parent->flags & AG_WIDGET_VISIBLE) &&
1547 AG_OfClass(parent, type) &&
1548 AG_WidgetArea(parent, x, y)) {
1549 return (parent);
1550 }
1551 return (NULL);
1552 }
1553
1554 /* Search for widgets of the specified class enclosing the given point. */
1555 void *
AG_WidgetFindPoint(const char * type,int x,int y)1556 AG_WidgetFindPoint(const char *type, int x, int y)
1557 {
1558 AG_Driver *drv;
1559 AG_Window *win;
1560 void *p;
1561
1562 AG_LockVFS(&agDrivers);
1563 OBJECT_FOREACH_CHILD(drv, &agDrivers, ag_driver) {
1564 AG_FOREACH_WINDOW_REVERSE(win, drv) {
1565 if ((p = FindAtPoint(WIDGET(win), type, x, y)) != NULL) {
1566 AG_UnlockVFS(&agDrivers);
1567 return (p);
1568 }
1569 }
1570 }
1571 AG_UnlockVFS(&agDrivers);
1572 return (NULL);
1573 }
1574
1575 static void *
FindRectOverlap(AG_Widget * parent,const char * type,int x,int y,int w,int h)1576 FindRectOverlap(AG_Widget *parent, const char *type, int x, int y, int w, int h)
1577 {
1578 AG_Widget *chld;
1579 void *p;
1580
1581 OBJECT_FOREACH_CHILD(chld, parent, ag_widget) {
1582 if ((p = FindRectOverlap(chld, type, x,y,w,h)) != NULL)
1583 return (p);
1584 }
1585 if (AG_OfClass(parent, type) &&
1586 !(x+w < parent->rView.x1 || x > parent->rView.x2 ||
1587 y+w < parent->rView.y1 || y > parent->rView.y2)) {
1588 return (parent);
1589 }
1590 return (NULL);
1591 }
1592
1593 /*
1594 * Search for widgets of the specified class enclosing the given rectangle.
1595 * Result is only accurate as long as the Driver VFS is locked.
1596 */
1597 void *
AG_WidgetFindRect(const char * type,int x,int y,int w,int h)1598 AG_WidgetFindRect(const char *type, int x, int y, int w, int h)
1599 {
1600 AG_Driver *drv;
1601 AG_Window *win;
1602 void *p;
1603
1604 AG_LockVFS(&agDrivers);
1605 OBJECT_FOREACH_CHILD(drv, &agDrivers, ag_driver) {
1606 AG_FOREACH_WINDOW_REVERSE(win, drv) {
1607 if ((p = FindRectOverlap(WIDGET(win), type, x,y,w,h)) != NULL) {
1608 AG_UnlockVFS(&agDrivers);
1609 return (p);
1610 }
1611 }
1612 }
1613 AG_UnlockVFS(&agDrivers);
1614 return (NULL);
1615 }
1616
1617 /* Generic inherited draw() routine. */
1618 void
AG_WidgetInheritDraw(void * obj)1619 AG_WidgetInheritDraw(void *obj)
1620 {
1621 WIDGET_SUPER_OPS(obj)->draw(obj);
1622 }
1623
1624 /* Generic inherited size_request() routine. */
1625 void
AG_WidgetInheritSizeRequest(void * obj,AG_SizeReq * r)1626 AG_WidgetInheritSizeRequest(void *obj, AG_SizeReq *r)
1627 {
1628 WIDGET_SUPER_OPS(obj)->size_request(obj, r);
1629 }
1630
1631 /* Generic inherited size_allocate() routine. */
1632 int
AG_WidgetInheritSizeAllocate(void * obj,const AG_SizeAlloc * a)1633 AG_WidgetInheritSizeAllocate(void *obj, const AG_SizeAlloc *a)
1634 {
1635 return WIDGET_SUPER_OPS(obj)->size_allocate(obj, a);
1636 }
1637
1638 /* Render a widget to an AG_Surface(3). */
1639 AG_Surface *
AG_WidgetSurface(void * obj)1640 AG_WidgetSurface(void *obj)
1641 {
1642 AG_Widget *wid = obj;
1643 AG_Surface *su;
1644 int rv;
1645
1646 AG_LockVFS(wid);
1647 rv = wid->drvOps->renderToSurface(wid->drv, wid, &su);
1648 AG_UnlockVFS(wid);
1649 return (rv == 0) ? su : NULL;
1650 }
1651
1652 /* Map a new AG_Surface(3) with a widget; return the new surface handle. */
1653 int
AG_WidgetMapSurface(void * obj,AG_Surface * su)1654 AG_WidgetMapSurface(void *obj, AG_Surface *su)
1655 {
1656 AG_Widget *wid = obj;
1657 int i, s = -1;
1658
1659 if (su == NULL)
1660 return (-1);
1661
1662 AG_ObjectLock(wid);
1663 for (i = 0; i < wid->nsurfaces; i++) {
1664 if (wid->surfaces[i] == NULL) {
1665 s = i;
1666 break;
1667 }
1668 }
1669 if (i == wid->nsurfaces) {
1670 wid->surfaces = Realloc(wid->surfaces,
1671 (wid->nsurfaces+1)*sizeof(AG_Surface *));
1672 wid->surfaceFlags = Realloc(wid->surfaceFlags,
1673 (wid->nsurfaces+1)*sizeof(Uint));
1674 wid->textures = Realloc(wid->textures,
1675 (wid->nsurfaces+1)*sizeof(Uint));
1676 wid->texcoords = Realloc(wid->texcoords,
1677 (wid->nsurfaces+1)*sizeof(AG_TexCoord));
1678 s = wid->nsurfaces++;
1679 }
1680 wid->surfaces[s] = su;
1681 wid->surfaceFlags[s] = 0;
1682 wid->textures[s] = 0;
1683 AG_ObjectUnlock(wid);
1684 return (s);
1685 }
1686
1687 /* Replace the contents of a mapped surface. */
1688 void
AG_WidgetReplaceSurface(void * obj,int s,AG_Surface * su)1689 AG_WidgetReplaceSurface(void *obj, int s, AG_Surface *su)
1690 {
1691 AG_Widget *wid = (AG_Widget *)obj;
1692
1693 AG_ObjectLock(wid);
1694 #ifdef AG_DEBUG
1695 if (s < 0 || s >= wid->nsurfaces)
1696 AG_FatalError("Invalid surface handle");
1697 #endif
1698 if (wid->surfaces[s] != NULL) {
1699 if (!WSURFACE_NODUP(wid,s))
1700 AG_SurfaceFree(wid->surfaces[s]);
1701 }
1702 wid->surfaces[s] = su;
1703 wid->surfaceFlags[s] &= ~(AG_WIDGET_SURFACE_NODUP);
1704
1705 /*
1706 * Queue the previous texture for deletion and set the texture handle
1707 * to 0 so the texture will be regenerated at the next blit.
1708 */
1709 if (wid->textures[s] != 0 &&
1710 wid->drv != NULL &&
1711 wid->drvOps->deleteTexture != NULL) {
1712 wid->drvOps->deleteTexture(wid->drv, wid->textures[s]);
1713 wid->textures[s] = 0;
1714 }
1715 AG_ObjectUnlock(wid);
1716 }
1717
1718 /*
1719 * Rebuild the effective style parameters of a widget and its descendants
1720 * based on its style attributes. Any required fonts are loaded. This
1721 * should be called whenever style attributes are changed.
1722 */
1723 static void
CompileStyleRecursive(AG_Widget * wid,const char * parentFace,double parentPtSize,Uint parentFlags,AG_WidgetPalette parentPalette)1724 CompileStyleRecursive(AG_Widget *wid, const char *parentFace,
1725 double parentPtSize, Uint parentFlags,
1726 AG_WidgetPalette parentPalette)
1727 {
1728 AG_StyleSheet *css = &agDefaultCSS;
1729 char face[256];
1730 double ptSize;
1731 Uint flags = parentFlags;
1732 AG_Widget *chld;
1733 AG_Variable *V;
1734 int i, j;
1735 AG_Object *po;
1736 char *cssData;
1737 double v;
1738 char *ep;
1739
1740 /* Select the effective style sheet for this widget. */
1741 for (po = OBJECT(wid);
1742 po->parent != NULL && AG_OfClass(po->parent, "AG_Widget:*");
1743 po = po->parent) {
1744 if (WIDGET(po)->css != NULL) {
1745 css = WIDGET(po)->css;
1746 break;
1747 }
1748 }
1749
1750 /* Set the font attributes. */
1751 if ((V = AG_GetVariableLocked(wid, "font-family")) != NULL) {
1752 Strlcpy(face, V->data.s, sizeof(face));
1753 AG_UnlockVariable(V);
1754 } else if (AG_LookupStyleSheet(css, wid, "font-family", &cssData)) {
1755 Strlcpy(face, cssData, sizeof(face));
1756 } else {
1757 Strlcpy(face, parentFace, sizeof(face));
1758 }
1759 if ((V = AG_GetVariableLocked(wid, "font-size")) != NULL) {
1760 v = strtod(V->data.s, &ep);
1761 ptSize = (*ep == '%') ? parentPtSize*(v/100.0) : v;
1762 AG_UnlockVariable(V);
1763 } else if (AG_LookupStyleSheet(css, wid, "font-size", &cssData)) {
1764 v = strtod(cssData, &ep);
1765 ptSize = (*ep == '%') ? parentPtSize*(v/100.0) : v;
1766 } else {
1767 ptSize = parentPtSize;
1768 }
1769 if ((V = AG_GetVariableLocked(wid, "font-weight")) != NULL) {
1770 if (AG_Strcasecmp(V->data.s, "bold") == 0) {
1771 flags |= AG_FONT_BOLD;
1772 } else if (AG_Strcasecmp(V->data.s, "normal") == 0) {
1773 flags &= ~(AG_FONT_BOLD);
1774 }
1775 AG_UnlockVariable(V);
1776 } else if (AG_LookupStyleSheet(css, wid, "font-weight", &cssData)) {
1777 if (AG_Strcasecmp(cssData, "bold") == 0) {
1778 flags |= AG_FONT_BOLD;
1779 } else if (AG_Strcasecmp(cssData, "normal") == 0) {
1780 flags &= ~(AG_FONT_BOLD);
1781 }
1782 }
1783 if ((V = AG_GetVariableLocked(wid, "font-style")) != NULL) {
1784 if (AG_Strcasecmp(V->data.s, "italic") == 0) {
1785 flags |= AG_FONT_ITALIC;
1786 } else if (AG_Strcasecmp(V->data.s, "normal") == 0) {
1787 flags &= ~(AG_FONT_ITALIC);
1788 }
1789 AG_UnlockVariable(V);
1790 }
1791
1792 /* Set the color attributes. */
1793 for (i = 0; i < AG_WIDGET_NSTATES; i++) {
1794 for (j = 0; j < AG_WIDGET_NCOLORS; j++) {
1795 AG_Color *parentColor = &parentPalette.c[i][j];
1796 char vName[AG_VARIABLE_NAME_MAX];
1797
1798 Strlcpy(vName, agWidgetColorNames[j], sizeof(vName));
1799 Strlcat(vName, agWidgetStateNames[i], sizeof(vName));
1800 if ((V = AG_GetVariableLocked(wid, vName)) != NULL) {
1801 wid->pal.c[i][j] = AG_ColorFromString(V->data.s,
1802 parentColor);
1803 AG_UnlockVariable(V);
1804 } else if (AG_LookupStyleSheet(css, wid, vName, &cssData)) {
1805 wid->pal.c[i][j] = AG_ColorFromString(cssData,
1806 parentColor);
1807 } else {
1808 Strlcpy(vName, agWidgetColorNames[j], sizeof(vName));
1809 if (AG_LookupStyleSheet(css, wid, vName, &cssData)) {
1810 wid->pal.c[i][j] = AG_ColorFromString(cssData,
1811 parentColor);
1812 } else {
1813 wid->pal.c[i][j] = *parentColor;
1814 }
1815 }
1816 }
1817 }
1818
1819 if (wid->flags & AG_WIDGET_USE_TEXT) {
1820 char *pFace = face, *tok;
1821 AG_Font *fontNew = NULL;
1822
1823 while ((tok = AG_Strsep(&pFace, ",")) != NULL) {
1824 fontNew = AG_FetchFont(face, (int)ptSize, (int)flags);
1825 if (fontNew != NULL)
1826 break;
1827 }
1828 if (fontNew == NULL) {
1829 fontNew = AG_FetchFont(NULL, (int)ptSize, (int)flags);
1830 }
1831 if (fontNew != NULL && wid->font != fontNew) {
1832 if (wid->font != NULL) {
1833 AG_UnusedFont(wid->font);
1834 }
1835 wid->font = fontNew;
1836 AG_PushTextState();
1837 AG_TextFont(wid->font);
1838 AG_PostEvent(NULL, wid, "font-changed", NULL);
1839 AG_PopTextState();
1840 AG_Redraw(wid);
1841 }
1842 }
1843
1844
1845 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
1846 CompileStyleRecursive(chld, face, ptSize, flags, wid->pal);
1847 }
1848 void
AG_WidgetCompileStyle(void * obj)1849 AG_WidgetCompileStyle(void *obj)
1850 {
1851 AG_Widget *wid = obj;
1852 AG_Widget *parent;
1853
1854 AG_LockVFS(wid);
1855 AG_MutexLock(&agTextLock);
1856
1857 if ((parent = OBJECT(wid)->parent) != NULL &&
1858 AG_OfClass(parent, "AG_Widget:*") &&
1859 parent->font != NULL) {
1860 CompileStyleRecursive(wid,
1861 OBJECT(parent->font)->name,
1862 parent->font->spec.size,
1863 parent->font->flags,
1864 parent->pal);
1865 } else {
1866
1867 CompileStyleRecursive(wid,
1868 OBJECT(agDefaultFont)->name,
1869 agDefaultFont->spec.size,
1870 agDefaultFont->flags,
1871 agDefaultPalette);
1872 }
1873 AG_MutexUnlock(&agTextLock);
1874 AG_UnlockVFS(wid);
1875 }
1876
1877 /*
1878 * Clear the style parameters of a widget and its descendants. Called
1879 * on detach. The widget's VFS must be locked.
1880 */
1881 void
AG_WidgetFreeStyle(void * obj)1882 AG_WidgetFreeStyle(void *obj)
1883 {
1884 AG_Widget *wid = obj;
1885 AG_Widget *chld;
1886
1887 if (wid->font != NULL) {
1888 AG_UnusedFont(wid->font);
1889 wid->font = NULL;
1890 }
1891 OBJECT_FOREACH_CHILD(chld, wid, ag_widget)
1892 AG_WidgetFreeStyle(chld);
1893 }
1894
1895 /* Copy all style properties from one widget to another. */
1896 void
AG_WidgetCopyStyle(void * objDst,void * objSrc)1897 AG_WidgetCopyStyle(void *objDst, void *objSrc)
1898 {
1899 AG_Widget *widSrc = objSrc;
1900 AG_Widget *widDst = objDst;
1901 AG_Variable *V;
1902 const char **s;
1903
1904 AG_ObjectLock(widSrc);
1905 AG_ObjectLock(widDst);
1906 for (s = &agWidgetPropNames[0]; *s != NULL; s++) {
1907 if ((V = AG_GetVariableLocked(widSrc, *s)) != NULL) {
1908 AG_SetString(widDst, *s, V->data.s);
1909 AG_UnlockVariable(V);
1910 }
1911
1912 }
1913 AG_ObjectUnlock(widDst);
1914 AG_ObjectUnlock(widSrc);
1915
1916 AG_WidgetCompileStyle(widDst);
1917 AG_Redraw(widDst);
1918 }
1919
1920 /*
1921 * Set the default font parameters for a widget.
1922 * If a NULL argument is provided, the parameter is inherited from parent.
1923 */
1924 void
AG_SetFont(void * obj,const AG_Font * font)1925 AG_SetFont(void *obj, const AG_Font *font)
1926 {
1927 AG_Widget *wid = obj;
1928
1929 AG_SetString(wid, "font-family", OBJECT(font)->name);
1930 AG_SetString(wid, "font-size", AG_Printf("%.2fpts", font->spec.size));
1931 AG_SetString(wid, "font-weight", (font->flags & AG_FONT_BOLD) ? "bold" : "normal");
1932 AG_SetString(wid, "font-style", (font->flags & AG_FONT_ITALIC) ? "italic" : "normal");
1933 AG_WidgetCompileStyle(wid);
1934 AG_Redraw(wid);
1935 }
1936 void
AG_SetStyle(void * obj,const char * which,const char * value)1937 AG_SetStyle(void *obj, const char *which, const char *value)
1938 {
1939 AG_Widget *wid = obj;
1940
1941 if (value != NULL) {
1942 AG_SetString(wid, which, value);
1943 } else {
1944 AG_Unset(wid, which); /* inherit */
1945 }
1946 AG_WidgetCompileStyle(wid);
1947 AG_Redraw(wid);
1948 }
1949
1950 AG_WidgetClass agWidgetClass = {
1951 {
1952 "Agar(Widget)",
1953 sizeof(AG_Widget),
1954 { 0,0 },
1955 Init,
1956 NULL, /* free */
1957 Destroy,
1958 NULL, /* load */
1959 NULL, /* save */
1960 NULL /* edit */
1961 },
1962 NULL, /* draw */
1963 SizeRequest,
1964 SizeAllocate
1965 };
1966