=== Overview ===
[xuni.git] / src / gui.c
blob3b4484e4c778fcc211e4fc8071ce43342fa1a127
1 /*! \file gui.c
3 */
5 #include <stdlib.h>
6 #include <string.h> /* for memmove(), strcmp(), strlen(), etc. */
7 #include <ctype.h>
9 #include "SDL_gfxPrimitives.h"
11 #include "error.h"
12 #include "loop.h"
13 #include "graphics.h"
14 #include "gui.h"
15 #include "memory.h"
16 #include "utility.h"
18 #include "resource/calcfunc.h"
20 #include "widget/widgets.h"
22 #include "widget/box.h"
23 #include "widget/button.h"
24 #include "widget/checkbox.h"
25 #include "widget/dump.h"
26 #include "widget/font.h"
27 #include "widget/image.h"
28 #include "widget/image_tile.h"
29 #include "widget/label.h"
30 #include "widget/listbox.h"
31 #include "widget/panel.h"
32 #include "widget/scrollbar.h"
33 #include "widget/textbox.h"
34 #include "widget/theme.h"
36 static int set_widget_sel_compose(struct widget_sel_t *sel, int xp, int yp,
37 struct widget_t *widget);
38 static enum widget_type_t get_widget_type(const char *find);
39 static double get_angle(struct resource_list_t *list);
40 static size_t find_font_name(struct resource_list_t *list);
41 static int init_resource_widget_ref(struct xuni_t *xuni,
42 struct widget_t *widget, enum widget_type_t type,
43 struct resource_list_t *list);
44 static enum label_type_t get_label_alignment(const char *name);
45 static enum scrollbar_use_t get_scrollbar_use(const char *name);
46 static void init_resource_widget(struct xuni_t *xuni, struct widget_t *widget,
47 enum widget_type_t type, struct resource_list_t *list);
48 static enum pos_pack_t get_pos_pack(const char *name);
49 static void process_widget_accelerators(struct xuni_t *xuni,
50 struct widget_t *panel, struct widget_t *widget,
51 struct resource_list_t *list);
52 static void widget_accelerator_from_string(struct xuni_t *xuni,
53 struct widget_t *panel, struct widget_t *widget, const char *data);
54 static SDLMod get_key_mod_from_string(const char *name);
55 static SDLKey get_key_identifier_from_string(const char *name);
56 static void add_resource_widget(struct xuni_t *xuni, struct widget_t **base,
57 struct resource_data_t *data);
59 void init_theme_structure(struct theme_t *theme) {
60 theme->current = 0;
61 theme->cursors.normal = 0;
62 theme->cursors.text = 0;
63 theme->cursors.which = CURSOR_NORMAL;
66 /*! Initializes the gui member of the structure \a xuni.
67 \param xuni The xuni structure to initialize the gui member of.
69 void init_gui(struct xuni_t *xuni) {
70 /*gui->panel = allocate_panel(0);*/
71 xuni->gui->widget = 0;
73 init_edit(&xuni->gui->edit);
74 clear_gui(xuni, (size_t)-1, -1);
77 void init_edit(struct widget_edit_t *edit) {
78 edit->data = 0;
79 edit->prev = 0;
80 edit->datawidget = 0;
81 edit->alloc = 0;
82 edit->len = 0;
83 edit->pos = 0;
86 void clear_gui(struct xuni_t *xuni, panel_type_t panel, int keep) {
87 clear_widget_sel(xuni->gui->widget);
88 clear_sel(&xuni->gui->sel);
89 clear_active(xuni, &xuni->gui->active, keep); /* before clear_edit() */
90 clear_edit(&xuni->gui->edit);
91 clear_tab(&xuni->gui->tab, panel);
94 void clear_sel(struct widget_sel_t *sel) {
95 sel->wasin = 0;
96 sel->clickin = 0;
97 sel->p.widget = 0;
100 void clear_edit(struct widget_edit_t *edit) {
101 edit->data = 0;
103 if(edit->prev) {
104 xuni_memory_free(edit->prev);
105 edit->prev = 0;
108 edit->datawidget = 0;
110 edit->alloc = 0;
111 edit->len = 0;
112 edit->pos = 0;
115 void clear_active(struct xuni_t *xuni, struct widget_p_t *wp, int keep) {
116 if(!keep) {
117 revert_widget(xuni, wp->widget);
119 widget_event(xuni, wp->widget, WIDGET_EVENT_RESCALE);
121 else if(keep > 0) {
122 deactivate_widget(wp->widget, xuni);
125 wp->widget = 0;
127 SDL_EnableUNICODE(0);
130 void clear_tab(struct widget_tab_t *tab, panel_type_t panel) {
131 tab->panel = panel;
132 tab->sel = (size_t)-1;
135 /* !!! lots of const -> non-const in this function */
136 void edit_add_char(struct widget_edit_t *edit, char c) {
137 if(edit->len + 1 >= edit->alloc) {
138 if(!edit->alloc) edit->alloc = 2;
139 else edit->alloc *= 2;
141 edit->data->text
142 = xuni_memory_resize((char *)edit->data->text,
143 edit->alloc);
146 if(edit->pos != edit->len) {
147 memmove((char *)edit->data->text + edit->pos + 1,
148 (char *)edit->data->text + edit->pos,
149 edit->len - edit->pos + 1);
152 ((char *)edit->data->text)[edit->pos] = c;
154 if(edit->pos == edit->len) {
155 ((char *)edit->data->text)[edit->pos + 1] = 0;
158 edit->pos ++;
159 edit->len ++;
162 /* !!! lots of const -> non-const in this function */
163 void edit_del_char(struct widget_edit_t *edit) {
164 if(edit->pos + 1 != edit->len) {
165 memmove((char *)edit->data->text + edit->pos,
166 (char *)edit->data->text + edit->pos + 1,
167 edit->len - edit->pos + 1 - 1);
169 else ((char *)edit->data->text)[edit->pos] = 0;
171 edit->len --;
174 int clear_widget_sel(struct widget_t *widget) {
175 size_t x;
176 int repaint = 0;
178 if(!widget) return 0;
180 if(widget->sel) repaint = 1;
181 widget->sel = 0;
183 if(!widget->compose) return 0;
185 for(x = 0; x < widget->compose->widgets; x ++) {
186 repaint = clear_widget_sel(widget->compose->widget[x]) || repaint;
189 return repaint;
192 struct widget_t *set_widget_sel_rec(struct widget_sel_t *sel, int xp, int yp,
193 struct widget_t *widget, int *repaint) {
195 size_t x;
196 struct widget_t *w = 0, *last = 0;
198 #if 0
199 if(widget && !strcmp(widget->name, "data")) {
200 /*printf("item \"%s\": ", widget->p.label->text);*/
201 printf("data: ");
203 printf("in_sdl_rect()? %s",
204 in_sdl_rect(xp, yp, &widget->pos->real) ? "yes" : "no");
205 printf(" pos_in_rect()? %s\n",
206 pos_in_rect(xp, yp, widget->pos) ? "yes" : "no");
208 if(!in_sdl_rect(xp, yp, &widget->pos->real)) {
209 printf("cursor: (%i,%i) widget: (%i,%i) to (%i,%i)\n",
210 xp, yp,
211 widget->pos->real.x,
212 widget->pos->real.y,
213 widget->pos->real.x + widget->pos->real.w,
214 widget->pos->real.y + widget->pos->real.h);
217 #endif
219 if(widget && widget->visibility & WIDGET_VISIBILITY_VISIBLE
220 && in_sdl_rect(xp, yp, &widget->pos->real)) {
222 if(widget->compose) {
223 for(x = widget->compose->widgets - 1; x != (size_t)-1; x --) {
224 w = set_widget_sel_rec(sel, xp, yp,
225 widget->compose->widget[x], repaint);
226 if(w) {
227 if(!last && widget->compose->widget[x]->visibility
228 & WIDGET_VISIBILITY_INDEPENDENT) {
230 last = w;
233 if(!set_widget_sel_compose(sel, xp, yp, w)) break;
234 else last = 0;
238 if(x != (size_t)-1) x --;
240 while(x != (size_t)-1) {
241 *repaint = clear_widget_sel(widget->compose->widget[x])
242 || *repaint;
244 x --;
248 if(!last && widget->visibility & WIDGET_VISIBILITY_SELABLE) {
249 last = widget;
250 if(!widget->sel) *repaint = 1;
251 widget->sel = 1;
253 else {
254 if(widget->sel) *repaint = 1;
255 widget->sel = 0;
258 else *repaint = clear_widget_sel(widget) || *repaint;
260 return last;
263 static int set_widget_sel_compose(struct widget_sel_t *sel, int xp, int yp,
264 struct widget_t *widget) {
266 /*widget->sel = 1;*/
268 if(widget->base) {
269 /*if(!(widget->visibility & WIDGET_VISIBILITY_INDEPENDENT)) {
270 widget->base->sel = 1;
273 if(!(widget->base->visibility & WIDGET_VISIBILITY_NOT_COMPOSE)) {
274 return 1;
278 return 0;
281 int set_widget_sel_repaint(struct widget_sel_t *sel, int xp, int yp,
282 int click, struct widget_t *widget) {
284 return set_widget_repaint(sel, xp, yp, click, widget)
285 | set_widget_sel(sel, xp, yp, click, widget);
288 int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click,
289 struct widget_t *widget) {
291 struct widget_t *w;
292 int repaint = 0;
294 if(!widget) return 0;
296 if(sel->p.widget && !sel->clickin && !sel->wasin) clear_sel(sel);
298 if(!sel->clickin) {
299 /*clear_widget_sel(widget);*/
300 w = set_widget_sel_rec(sel, xp, yp, widget, &repaint);
302 /*if(repaint && w == sel->p.widget) puts("inline change");*/
304 if(w && w != sel->p.widget) {
305 sel->p.widget = w;
306 sel->wasin = 1;
307 sel->clickin = click;
309 return 1;
313 return repaint;
316 int set_widget_repaint(struct widget_sel_t *sel, int xp, int yp, int click,
317 struct widget_t *widget) {
319 int r, v = 0;
321 if(sel->p.widget) {
322 v = set_widget_sel(sel, xp, yp, click, widget);
324 r = pos_in_rect(xp, yp, sel->p.widget->pos);
326 /* !!! set v=1 only if the widget _changes_ upon loss of mouse focus or whatever */
328 /* the mouse focus in the widget was toggled */
329 if(sel->wasin != r) {
330 sel->wasin = r;
331 v = 1;
334 /* the mouse button was clicked or released inside the widget */
335 if(sel->clickin != click) {
336 sel->clickin = click;
337 v = 1;
340 if(v) sel->p.widget->repaint = 1;
343 return v;
346 struct load_resource_widget_t {
347 struct xuni_t *xuni;
348 struct widget_t **widget;
351 static void load_resource_widgets_callback(void *vdata,
352 struct resource_data_t *data);
354 static void load_resource_widgets_callback(void *vdata,
355 struct resource_data_t *data) {
357 struct load_resource_widget_t *load_data = vdata;
359 add_resource_widget(load_data->xuni, load_data->widget, data);
362 void load_resource_widgets(struct xuni_t *xuni, struct widget_t **widget,
363 struct resource_data_t *data) {
365 struct load_resource_widget_t callback_data;
366 callback_data.xuni = xuni;
367 callback_data.widget = widget;
369 search_resource_tag(data, "widget", 1,
370 load_resource_widgets_callback, &callback_data);
373 /* !!! use wtype instead */
374 static enum widget_type_t get_widget_type(const char *find) {
375 struct string_index_t data[] = {
376 {"box", WIDGET_BOX},
377 {"button", WIDGET_BUTTON},
378 {"checkbox", WIDGET_CHECKBOX},
379 {"combobox", WIDGET_COMBOBOX},
380 {"font", WIDGET_FONT},
381 {"image", WIDGET_IMAGE},
382 {"image tile", WIDGET_IMAGE_TILE},
383 {"label", WIDGET_LABEL},
384 {"listbox", WIDGET_LISTBOX},
385 {"panel", WIDGET_PANEL},
386 {"textarea", WIDGET_TEXTAREA},
387 {"textbox", WIDGET_TEXTBOX},
388 {"theme", WIDGET_THEME}
390 size_t type = string_to_index(data, sizeof(data) / sizeof(*data), find);
392 if(type == (size_t)-1) {
393 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
394 "Invalid widget type: \"%s\"", find);
397 return (enum widget_type_t)type;
400 static double get_angle(struct resource_list_t *list) {
401 double angle;
402 struct resource_list_t *anglep;
403 const char *unit;
405 anglep = first_resource_tag(list, "angle");
406 unit = find_resource_text(anglep, "unit");
407 angle = get_expression_value(first_resource_text(anglep), 0);
409 if(unit) {
410 if(!strcmp(unit, "degrees")) angle = degrees_to_radians(angle);
411 else if(strcmp(unit, "radians")) {
412 printf("*** Unrecognized unit type \"%s\" for \"angle\"\n",
413 unit);
417 return angle;
420 /* this could handle all theme widgets */
421 static size_t find_font_name(struct resource_list_t *list) {
422 const char *name = find_resource_text(list, "font");
423 enum wid_theme_t which = THEME_FONT_SANS;
425 if(name) {
426 if(!strcmp(name, "sans")) which = THEME_FONT_SANS;
427 else if(!strcmp(name, "mono")) which = THEME_FONT_MONO;
428 else {
429 printf("*** Unknown font name \"%s\"\n", name);
433 return (size_t)which;
436 static int init_resource_widget_ref(struct xuni_t *xuni,
437 struct widget_t *widget, enum widget_type_t type,
438 struct resource_list_t *list) {
440 const char *refname;
441 struct widget_t *ref;
443 refname = find_resource_text(list, "ref");
444 if(!refname) return 0;
446 ref = find_widget(xuni->gui->widget, refname);
447 if(!ref) return 0;
449 widget->type = type;
451 switch(type) {
452 case WIDGET_FONT:
453 xuni_memory_increment(ref->p.font);
454 widget->p.font = ref->p.font;
456 break;
457 default:
458 printf("*** Error: <ref> not supported for widgets of type %s\n",
459 get_widget_type_name(xuni, type));
460 break;
463 return 1;
466 static enum label_type_t get_label_alignment(const char *name) {
467 struct string_index_t data[] = {
468 {"center", LABEL_ALIGN_CENTRE},
469 {"centre", LABEL_ALIGN_CENTRE},
470 {"left", LABEL_ALIGN_LEFT},
471 {"right", LABEL_ALIGN_RIGHT}
473 size_t index;
475 if(!name) return LABEL_ALIGN_LEFT;
477 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
479 if(index == (size_t)-1) {
480 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
481 "Invalid label alignment type: \"%s\"", name);
482 return LABEL_ALIGN_LEFT;
485 return index;
488 static enum scrollbar_use_t get_scrollbar_use(const char *name) {
489 struct string_index_t data[] = {
490 {"both", SCROLLBAR_USE_BOTH},
491 {"horizontal", SCROLLBAR_USE_HORIZONTAL},
492 {"none", SCROLLBAR_USE_NONE},
493 {"vertical", SCROLLBAR_USE_VERTICAL}
495 size_t index;
497 if(!name) return SCROLLBAR_USE_VERTICAL;
499 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
501 if(index == (size_t)-1) {
502 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
503 "Invalid scrollbar use: \"%s\"", name);
504 return SCROLLBAR_USE_VERTICAL;
507 return index;
510 static void init_resource_widget(struct xuni_t *xuni, struct widget_t *widget,
511 enum widget_type_t type, struct resource_list_t *list) {
513 if(init_resource_widget_ref(xuni, widget, type, list)) return;
515 switch(type) {
516 case WIDGET_BOX:
517 init_box(widget, BOX_STYLE_NORMAL, 0);
518 break;
519 case WIDGET_BUTTON:
520 init_button(widget, new_sub_label(widget,
521 find_font_name(list),
522 find_resource_text(list, "text")));
523 break;
524 case WIDGET_CHECKBOX:
525 init_checkbox(widget, xuni,
526 find_font_name(list),
527 find_resource_text(list, "text"), 0);
528 break;
529 case WIDGET_COMBOBOX:
530 init_combobox(xuni, widget, find_font_name(list));
531 break;
532 case WIDGET_FONT:
533 init_font(widget, find_resource_text(list, "path"));
534 break;
535 case WIDGET_IMAGE:
536 init_image(widget, find_resource_text(list, "path"), get_angle(list));
537 break;
538 case WIDGET_IMAGE_TILE:
539 init_image_tile(widget, find_resource_text(list, "path"),
540 (int)get_expression_value(find_resource_text(list, "xinc"), 0),
541 (int)get_expression_value(find_resource_text(list, "yinc"), 0));
542 break;
543 case WIDGET_LABEL:
544 init_label(widget, find_font_name(list),
545 find_resource_text(list, "text"),
546 get_label_alignment(find_resource_text(list, "align")),
547 (Uint8)get_expression_value(find_resource_text(list,
548 "red"), 255),
549 (Uint8)get_expression_value(find_resource_text(list,
550 "green"), 255),
551 (Uint8)get_expression_value(find_resource_text(list,
552 "blue"), 255));
553 break;
554 case WIDGET_LISTBOX:
555 init_listbox(widget, xuni,
556 get_scrollbar_use(find_resource_text(list, "scrollbar-allow")),
557 get_scrollbar_use(find_resource_text(list, "scrollbar-force")));
558 break;
559 case WIDGET_PANEL:
560 init_panel_from_resource(xuni, widget, list);
561 /*init_panel(widget);*/
562 break;
563 case WIDGET_TEXTAREA:
564 init_textarea(widget, xuni);
565 break;
566 case WIDGET_TEXTBOX:
567 init_textbox(widget, xuni, find_resource_text(list, "text"),
568 find_font_name(list));
569 break;
570 case WIDGET_THEME:
571 init_theme(widget,
572 get_expression_value(find_resource_text(list, "box-width"),
573 100 / 60.0),
574 get_expression_value(find_resource_text(list, "box-height"),
575 100 / 60.0));
576 break;
577 default:
578 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
579 "Widgets of type %s cannot be instantiated in resource files",
580 get_widget_type_name(xuni, type));
581 break;
585 static enum pos_pack_t get_pos_pack(const char *name) {
586 struct string_index_t data[] = {
587 {"bottom", POS_PACK_BOTTOM},
588 {"none", POS_PACK_NONE},
589 {"top", POS_PACK_TOP}
591 size_t index;
593 if(!name) return POS_PACK_NONE;
595 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
597 if(index == (size_t)-1) {
598 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
599 "Invalid position pack type: \"%s\"", name);
600 return POS_PACK_NONE; /* a sensible default */
603 return index;
606 static void process_widget_accelerators(struct xuni_t *xuni,
607 struct widget_t *panel, struct widget_t *widget,
608 struct resource_list_t *list) {
610 size_t x;
612 for(x = 0; x < list->n; x ++) {
613 if(list->data[x]->type != RESOURCE_TAG
614 || strcmp(list->data[x]->data.tag->name, "accelerator")) {
616 continue;
619 if(panel->type != WIDGET_PANEL) {
620 printf("Attempted to add an accelerator to a \"%s\"\n",
621 get_widget_type_name(xuni, panel->type));
623 else {
624 widget_accelerator_from_string(xuni, panel, widget,
625 first_resource_text(list->data[x]->data.tag->list));
630 enum { ACCELERATOR_ERROR = 0xf000 };
632 static void widget_accelerator_from_string(struct xuni_t *xuni,
633 struct widget_t *panel, struct widget_t *widget, const char *data) {
635 const char *end;
636 char c;
637 SDLKey key = (size_t)-1;
638 SDLMod mod = KMOD_NONE, m = KMOD_NONE;
640 if(!data) return;
642 do {
643 for(end = data; isalnum(*end); end ++);
645 c = *end;
646 *(char *)end = 0;
647 /*printf("accelerator: \"%s\"\n", data);*/
648 m = get_key_mod_from_string(data);
649 if(m == ACCELERATOR_ERROR) {
650 if(key != ACCELERATOR_ERROR) {
651 key = get_key_identifier_from_string(data);
652 if(key == ACCELERATOR_ERROR) {
653 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
654 "Unknown key/mod name \"%s\"\n", data);
657 else {
658 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
659 "Multiple key specified: \"%s\"\n", data);
662 else mod = m;
664 *(char *)end = c;
666 for(data = end; isspace(*data); data ++);
667 end = data;
668 } while(*data);
670 add_widget_accelerator(xuni, panel, widget, key, mod);
673 static SDLMod get_key_mod_from_string(const char *name) {
674 struct string_index_t data[] = {
675 {"alt", KMOD_ALT},
676 {"control", KMOD_CTRL},
677 {"ctrl", KMOD_CTRL},
678 {"lalt", KMOD_LALT},
679 {"lcontrol", KMOD_LCTRL},
680 {"lctrl", KMOD_LCTRL},
681 {"lshift", KMOD_LSHIFT},
682 {"none", KMOD_NONE},
683 {"nothing", KMOD_NONE},
684 {"ralt", KMOD_RALT},
685 {"rcontrol", KMOD_RCTRL},
686 {"rctrl", KMOD_RCTRL},
687 {"rshift", KMOD_RSHIFT},
688 {"shift", KMOD_SHIFT}
690 size_t index;
692 if(!name) return ACCELERATOR_ERROR;
694 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
696 if(index == (size_t)-1) return ACCELERATOR_ERROR;
697 /*printf("Found mod accelerator: %x\n", (unsigned)index);*/
699 return index;
702 static SDLKey get_key_identifier_from_string(const char *name) {
703 struct string_index_t data[] = {
704 {"f1", SDLK_F1},
705 {"f10", SDLK_F10},
706 {"f11", SDLK_F11},
707 {"f12", SDLK_F12},
708 {"f2", SDLK_F2},
709 {"f3", SDLK_F3},
710 {"f4", SDLK_F4},
711 {"f5", SDLK_F5},
712 {"f6", SDLK_F6},
713 {"f7", SDLK_F7},
714 {"f8", SDLK_F8},
715 {"f9", SDLK_F9}
717 size_t index;
719 if(!name) return ACCELERATOR_ERROR;
721 if(*name && !name[1]) {
722 /* ... */
725 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
727 if(index == (size_t)-1) {
728 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
729 "Unknown key name \"%s\"", name);
730 return ACCELERATOR_ERROR;
733 return index;
736 /*! Creates a widget based on the information in the resource tree node
737 \a data.
739 Also sets the current theme to the first theme that is found in the
740 resource tree.
742 \param xuni The xuni_t structure, to pass on to other functions and set
743 the current theme in and so on.
744 \param base The widget to add the new widget to as a compose widget. If
745 \a base is NULL, it means that the root widget is being created, in
746 which case \a base is set to point to the new widget.
747 \param data The resource tree node representing a <widget>, from which to
748 create a widget.
750 static void add_resource_widget(struct xuni_t *xuni, struct widget_t **base,
751 struct resource_data_t *data) {
753 struct widget_t *widget;
754 struct resource_list_t *list;
755 enum widget_type_t type;
757 list = data->data.tag->list;
759 #if 0
760 widget = init_resource_widget_ref(xuni, list);
761 if(widget) {
762 type = widget->type;
764 else {
765 #endif
766 type = get_widget_type(find_resource_text(list, "type"));
767 if(type == WIDGET_NONE) return;
769 if(!*base) {
770 *base = allocate_widget(find_resource_text(list, "name"), 0);
771 widget = *base;
773 else {
774 if(!(*base)->compose) (*base)->compose = allocate_panel(*base);
776 add_allocate_widget(*base, find_resource_text(list, "name"));
778 widget = last_compose_widget(*base);
781 init_widget_pos(widget,
782 get_expression_value(find_resource_text(list, "xpos"), 0),
783 get_expression_value(find_resource_text(list, "ypos"), 0),
784 get_expression_value(find_resource_text(list, "width"), 1),
785 get_expression_value(find_resource_text(list, "height"), 1),
786 get_pos_pack(find_resource_text(list, "pack")));
788 if(!get_expression_value(find_resource_text(list, "enabled"), 1)) {
789 widget->visibility &= ~WIDGET_VISIBILITY_CLICKABLE;
791 if(!get_expression_value(find_resource_text(list, "selable"), 1)) {
792 widget->visibility &= ~WIDGET_VISIBILITY_SELABLE;
794 if(!get_expression_value(find_resource_text(list, "visible"), 1)) {
795 widget->visibility &= ~WIDGET_VISIBILITY_VISIBLE;
798 process_widget_accelerators(xuni, *base, widget, list);
800 init_resource_widget(xuni, widget, type, list);
802 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
804 if(widget->type == WIDGET_THEME) {
805 if(!xuni->theme->current) {
806 xuni->theme->current = widget;
809 /* recurse (indirectly) */
810 load_resource_widgets(xuni, &widget, data);
812 set_theme_nameid(widget);
813 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
815 else if(widget->type == WIDGET_PANEL) {
816 /* recurse (indirectly) */
817 load_resource_widgets(xuni, &widget, data);
821 #if 0
822 /* This code returns a widget based on a list of names to dereference. It
823 works, but using WIDs is much more efficient, and this is not used at the
824 moment.
826 static int compare_widget_name_nonrec(struct widget_t *widget, va_list arg) {
827 const char *match = va_arg(arg, const char *);
829 if(!widget || !match) return !match;
831 return strcmp(match, widget->name) == 0
832 && compare_widget_name_nonrec(widget->base, arg);
835 static int compare_widget_name_nonrec_reverse(struct widget_t **widget,
836 va_list arg) {
838 const char *match = va_arg(arg, const char *);
840 if(!*widget || !match) return !match;
842 if(!compare_widget_name_nonrec_reverse(widget, arg)
843 || !(*widget)->name) {
845 return 0;
848 if(strcmp(match, (*widget)->name) == 0) {
849 *widget = (*widget)->base;
850 return 1;
853 return 0;
856 int compare_widget_name(struct widget_t *widget, ...) {
857 int r;
859 va_list arg;
860 va_start(arg, widget);
862 r = compare_widget_name_nonrec_reverse(&widget, arg);
864 va_end(arg);
866 return r;
868 #endif
870 /*! Allocates enough space for a struct widget_nameid_t.
871 \return A pointer to the newly allocated nameid structure.
873 struct nameid_t *allocate_nameid(void) {
874 struct nameid_t *nameid = xuni_memory_allocate(sizeof(*nameid));
876 nameid->widget = 0;
877 nameid->widgets = 0;
879 return nameid;
882 /*! Adds the widget \a widget to \a nameid.
884 \param nameid The array of nameids to add the new wid to.
885 \param widget The widget to add to \a nameid.
886 \param id The wid to use for this widget.
887 \param name The path to the widget \a widget relative to the panel widget
888 that \a nameid is a part of.
890 void init_wid_widget(struct nameid_t *nameid, struct widget_t *widget,
891 size_t id, const char *name) {
893 size_t x;
895 if(nameid->widgets <= id) {
896 nameid->widget = xuni_memory_resize(nameid->widget,
897 (id + 1) * sizeof(*nameid->widget));
899 /* clear any new nameids allocated between nameid->widgets and id */
900 for(x = nameid->widgets; x <= id; x ++) {
901 nameid->widget[x].id = 0;
902 nameid->widget[x].name = 0;
903 nameid->widget[x].widget = 0;
906 nameid->widgets = id + 1;
909 if(nameid->widget[id].id || nameid->widget[id].name
910 || nameid->widget[id].widget) {
912 log_message(ERROR_TYPE_WARNING, 0, __FILE__, __LINE__,
913 "Duplicate wid: old: \"%s\" [%lu], new: \"%s\" [%lu]",
914 nameid->widget[id].name, (unsigned long)nameid->widget[id].id,
915 name, (unsigned long)id);
917 else {
918 nameid->widget[id].id = id;
919 if(widget) widget->id = id;
920 nameid->widget[id].name = name;
921 nameid->widget[id].widget = widget;
925 /*! Just like init_wid(), but allows the widget referred to be \a name to not
926 exist. (init_wid() would display an error in that case.)
928 Mainly useful for widgets that do not have to be loaded. Prime examples
929 of this are the various widget images of themes, like the checkbox image.
931 \param base The base of the widget, from which to use find_widget() to
932 find the widget represented by the widget path \a name.
933 \param nameid The nameid array to add the new nameid to.
934 \param id The wid of the new widget -- usually a client-defined enum
935 value.
936 \param name The path to the widget, starting from \a base.
938 void init_wid_possible_zero(struct widget_t *base, struct nameid_t *nameid,
939 size_t id, const char *name) {
941 init_wid_widget(nameid, find_widget(base, name), id, name);
944 /*! Adds a widget to the nameid array of \a use. The widget is located using
945 the name in \a name with the base \a base.
947 \param base The base of the widget, from which to use find_widget() to
948 find the widget represented by the widget path \a name.
949 \param use The panel widget to add the new nameid to.
950 \param id The wid of the new widget -- usually a client-defined enum
951 value.
952 \param name The path to the widget, starting from \a base.
954 void init_wid(struct widget_t *base, struct widget_t *use, size_t id,
955 const char *name) {
957 struct widget_t *widget = find_widget(base, name);
959 if(!use->p.panel->nameid) {
960 use->p.panel->nameid = allocate_nameid();
963 if(widget) {
964 init_wid_widget(use->p.panel->nameid, widget, id, name);
966 else {
967 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
968 "Could not find widget \"%s\" [wid %lu]", name,
969 (unsigned long)id);
973 void init_wid_array(struct widget_t *base, struct widget_t *use,
974 struct wid_init_t *init, size_t n) {
976 size_t x;
978 for(x = 0; x < n; x ++) {
979 init_wid(base, use, init[x].id, init[x].name);
983 int widget_process_character(struct xuni_t *xuni, SDL_keysym *sym) {
984 struct widget_t *widget = xuni->gui->active.widget;
986 if(!widget) return 0;
988 /*printf("widget_process_character(): widget=%p, key=%i [%c]\n",
989 (void *)widget, sym->unicode,
990 isprint(sym->unicode) ? sym->unicode : '-');*/
992 /* !!! */
993 if(widget->type == WIDGET_TEXTBOX) {
994 widget = widget->compose->widget[WID_TEXTBOX_LABEL];
997 if(widget->type == WIDGET_LABEL) {
998 if(isprint(sym->unicode)) {
999 edit_add_char(&xuni->gui->edit, sym->unicode);
1001 else if(sym->unicode == '\b' && xuni->gui->edit.pos) {
1002 xuni->gui->edit.pos --;
1003 edit_del_char(&xuni->gui->edit);
1005 else if(sym->unicode == '\r') {
1006 clear_active(xuni, &xuni->gui->active, 1);
1008 else if(sym->sym == SDLK_DELETE
1009 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1011 edit_del_char(&xuni->gui->edit);
1013 else if(sym->sym == SDLK_LEFT && xuni->gui->edit.pos) {
1014 xuni->gui->edit.pos --;
1016 else if(sym->sym == SDLK_RIGHT
1017 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1019 xuni->gui->edit.pos ++;
1021 else if(sym->sym == SDLK_END) {
1022 xuni->gui->edit.pos = xuni->gui->edit.len;
1024 else if(sym->sym == SDLK_HOME) {
1025 xuni->gui->edit.pos = 0;
1027 else {
1028 return 0;
1031 if(xuni->gui->edit.data) {
1032 reposition_label_data(xuni, xuni->gui->edit.data,
1033 &xuni->gui->edit, xuni->gui->edit.pos);
1036 return 1;
1039 /*printf("data: \"%s\"\n", xuni->gui->edit.data);*/
1041 return 0;
1044 int widget_can_be_active(struct widget_t *widget) {
1045 if(!widget) return 0;
1047 if(widget->type == WIDGET_TEXTBOX) return 1;
1049 return 0;
1052 void update_widget(struct xuni_t *xuni, struct widget_t *widget) {
1053 if(!widget) return;
1055 if(widget->type == WIDGET_TEXTBOX) {
1056 /*gui->sel.p.widget->base->compose
1057 ->widget[WID_TEXTBOX_LABEL]->p.label->text = gui->edit.data;*/
1059 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
1063 void enable_unicode(const struct widget_t *widget) {
1064 int v = 0;
1066 if(!widget) return;
1068 if(widget->type == WIDGET_TEXTBOX) {
1069 v = 1;
1072 if(SDL_EnableUNICODE(-1) != v) SDL_EnableUNICODE(v);
1075 void activate_widget(struct widget_t *widget, struct xuni_t *xuni,
1076 int xp, int yp) {
1078 struct widget_edit_t *edit = &xuni->gui->edit;
1079 struct widget_t *w;
1080 int x;
1082 if(!widget) return;
1084 if(widget->type == WIDGET_TEXTBOX) {
1085 struct label_t *label = widget->compose->widget[WID_TEXTBOX_LABEL]
1086 ->p.label;
1087 size_t len = label->text ? strlen(label->text) : 0;
1089 if(edit->data) deactivate_widget(xuni->gui->active.widget, xuni);
1091 edit->data = label;
1092 edit->datawidget = widget->compose->widget[WID_TEXTBOX_LABEL];
1094 edit->prev = xuni_memory_allocate(sizeof(*edit->prev));
1095 *edit->prev = *label;
1097 edit->data->text = xuni_memory_duplicate_string_len(label->text, len);
1099 edit->len = edit->alloc = label->text ? len : 0;
1101 if(label->text) {
1102 w = widget->compose->widget[WID_TEXTBOX_LABEL];
1104 x = xp - w->pos->real.x;
1105 if(w->pos->clip) x += w->pos->clip->xclip;
1107 edit->pos = width_to_label_pos(x,
1108 get_theme_widget(xuni, label->font),
1109 (char *)label->text); /* !!! const */
1111 else {
1112 edit->pos = 0;
1117 void revert_widget(struct xuni_t *xuni, struct widget_t *widget) {
1118 if(!widget) return;
1120 if(widget->type == WIDGET_TEXTBOX) {
1121 struct widget_t *label = widget->compose->widget[WID_TEXTBOX_LABEL];
1123 /*printf("revert_widget(): edit: %p \"%s\"\n", label->p.label->text,
1124 label->p.label->text);
1125 printf("revert_widget(): prev: %p \"%s\"\n",
1126 xuni->gui->edit.prev->text, xuni->gui->edit.prev->text);*/
1128 xuni_memory_free((char *)label->p.label->text); /* !!! const */
1129 /*SDL_FreeSurface(label->p.label->label);*/
1131 if(xuni->gui->edit.prev) {
1132 label->p.label->text = xuni->gui->edit.prev->text;
1134 xuni_memory_free(xuni->gui->edit.prev);
1136 else label->p.label->text = xuni_memory_duplicate_string_len("", 0);
1138 widget_event(xuni, label, WIDGET_EVENT_RESCALE);
1140 init_edit(&xuni->gui->edit);
1144 void deactivate_widget(struct widget_t *widget, struct xuni_t *xuni) {
1145 if(!widget) return;
1147 if(widget->type == WIDGET_TEXTBOX) {
1148 /* makes comboboxes have a history of what was typed in them */
1149 /*if(widget->base
1150 && widget->base->type == WIDGET_COMBOBOX) {
1152 struct widget_t *label = widget->compose
1153 ->widget[WID_TEXTBOX_LABEL];
1155 add_combobox_item(xuni, widget->base, label->p.label->font,
1156 label->p.label->text);
1157 widget_event(xuni, widget->base->compose->widget
1158 [WID_COMBOBOX_DROPDOWN], WIDGET_EVENT_RESCALE);
1161 call_deactivate_func(xuni, widget);
1163 xuni_memory_free((char *)xuni->gui->edit.prev->text); /* !!! const */
1164 /*free_surface(xuni->gui->edit.prev->label);*/
1165 xuni_memory_free(xuni->gui->edit.prev);
1167 init_edit(&xuni->gui->edit);
1171 void perform_widget_click(struct xuni_t *xuni, struct widget_t *widget) {
1172 if(!widget) return;
1174 if(widget->base) {
1175 if(widget->type == WIDGET_CHECKBOX) {
1176 toggle_checkbox(widget);
1178 else if(widget->base->type == WIDGET_SCROLLBAR) {
1179 if(widget->base->compose->widget[WID_SCROLLBAR_UP]
1180 == widget) {
1182 move_scrollbar(xuni, widget->base, -10);
1184 else if(widget->base->compose->widget
1185 [WID_SCROLLBAR_DOWN] == widget) {
1187 move_scrollbar(xuni, widget->base, 10);
1190 /*reposition_widget(xuni, widget->base);*/
1191 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
1192 WIDGET_EVENT_REPOSITION);
1194 else if(widget->base->type == WIDGET_COMBOBOX) {
1195 if(widget->base->compose->widget[WID_COMBOBOX_BUTTON] == widget) {
1196 widget->base->compose->widget[WID_COMBOBOX_DROPDOWN]
1197 ->visibility ^= WIDGET_VISIBILITY_VISIBLE;
1203 void free_gui(struct xuni_t *xuni) {
1204 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_FREE);