Small bugfixes.
[xuni.git] / src / gui.c
blobbce22da1bb48840e2ff367d3f932de4977e15b26
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 /*! Clears the sel member of the widget \a widget and all of its child
175 widgets. Returns true if a repaint will be required; i.e., if any widget
176 that had its sel member set to false was originally true. (If this was the
177 case, a widget will have changed from selected to not selected, and a
178 repaint would be necessary.)
180 \param widget The widget at the top of the tree of widgets to clear the
181 sel members of.
182 \return True if a repaint is needed, that is, if any widget had a sel
183 member set to true.
185 int clear_widget_sel(struct widget_t *widget) {
186 size_t x;
187 int repaint = 0;
189 if(!widget) return 0;
191 if(widget->sel) repaint = 1;
192 widget->sel = 0;
194 if(!widget->compose) return 0;
196 for(x = 0; x < widget->compose->widgets; x ++) {
197 repaint = clear_widget_sel(widget->compose->widget[x]) || repaint;
200 return repaint;
203 struct widget_t *set_widget_sel_rec(struct widget_sel_t *sel, int xp, int yp,
204 struct widget_t *widget, int *repaint) {
206 size_t x;
207 struct widget_t *w = 0, *last = 0;
209 #if 0
210 if(widget && !strcmp(widget->name, "data")) {
211 /*printf("item \"%s\": ", widget->p.label->text);*/
212 printf("data: ");
214 printf("in_sdl_rect()? %s",
215 in_sdl_rect(xp, yp, &widget->pos->real) ? "yes" : "no");
216 printf(" pos_in_rect()? %s\n",
217 pos_in_rect(xp, yp, widget->pos) ? "yes" : "no");
219 if(!in_sdl_rect(xp, yp, &widget->pos->real)) {
220 printf("cursor: (%i,%i) widget: (%i,%i) to (%i,%i)\n",
221 xp, yp,
222 widget->pos->real.x,
223 widget->pos->real.y,
224 widget->pos->real.x + widget->pos->real.w,
225 widget->pos->real.y + widget->pos->real.h);
228 #endif
230 if(widget && widget->visibility & WIDGET_VISIBILITY_VISIBLE
231 && in_sdl_rect(xp, yp, &widget->pos->real)) {
233 if(widget->compose) {
234 for(x = widget->compose->widgets - 1; x != (size_t)-1; x --) {
235 w = set_widget_sel_rec(sel, xp, yp,
236 widget->compose->widget[x], repaint);
237 if(w) {
238 if(!last && widget->compose->widget[x]->visibility
239 & WIDGET_VISIBILITY_INDEPENDENT) {
241 last = w;
244 if(!set_widget_sel_compose(sel, xp, yp, w)) break;
245 else last = 0;
249 if(x != (size_t)-1) x --;
251 while(x != (size_t)-1) {
252 *repaint = clear_widget_sel(widget->compose->widget[x])
253 || *repaint;
255 x --;
259 if(!last && widget->visibility & WIDGET_VISIBILITY_SELABLE) {
260 last = widget;
261 if(!widget->sel) *repaint = 1;
262 widget->sel = 1;
264 else {
265 if(widget->sel) *repaint = 1;
266 widget->sel = 0;
269 else *repaint = clear_widget_sel(widget) || *repaint;
271 return last;
274 static int set_widget_sel_compose(struct widget_sel_t *sel, int xp, int yp,
275 struct widget_t *widget) {
277 /*widget->sel = 1;*/
279 if(widget->base) {
280 /*if(!(widget->visibility & WIDGET_VISIBILITY_INDEPENDENT)) {
281 widget->base->sel = 1;
284 if(!(widget->base->visibility & WIDGET_VISIBILITY_NOT_COMPOSE)) {
285 return 1;
289 return 0;
292 int set_widget_sel_repaint(struct widget_sel_t *sel, int xp, int yp,
293 int click, struct widget_t *widget) {
295 return set_widget_repaint(sel, xp, yp, click, widget)
296 | set_widget_sel(sel, xp, yp, click, widget);
299 int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click,
300 struct widget_t *widget) {
302 struct widget_t *w;
303 int repaint = 0;
305 if(!widget) return 0;
307 if(sel->p.widget && !sel->clickin && !sel->wasin) clear_sel(sel);
309 if(!sel->clickin) {
310 /*clear_widget_sel(widget);*/
311 w = set_widget_sel_rec(sel, xp, yp, widget, &repaint);
313 /*if(repaint && w == sel->p.widget) puts("inline change");*/
315 if(w && w != sel->p.widget) {
316 sel->p.widget = w;
317 sel->wasin = 1;
318 sel->clickin = click;
320 return 1;
324 return repaint;
327 int set_widget_repaint(struct widget_sel_t *sel, int xp, int yp, int click,
328 struct widget_t *widget) {
330 int r, v = 0;
332 if(sel->p.widget) {
333 v = set_widget_sel(sel, xp, yp, click, widget);
335 r = pos_in_rect(xp, yp, sel->p.widget->pos);
337 /* !!! set v=1 only if the widget _changes_ upon loss of mouse focus or whatever */
339 /* the mouse focus in the widget was toggled */
340 if(sel->wasin != r) {
341 sel->wasin = r;
342 v = 1;
345 /* the mouse button was clicked or released inside the widget */
346 if(sel->clickin != click) {
347 sel->clickin = click;
348 v = 1;
351 if(v) sel->p.widget->repaint = 1;
354 return v;
357 struct load_resource_widget_t {
358 struct xuni_t *xuni;
359 struct widget_t **widget;
362 static void load_resource_widgets_callback(void *vdata,
363 struct resource_data_t *data);
365 static void load_resource_widgets_callback(void *vdata,
366 struct resource_data_t *data) {
368 struct load_resource_widget_t *load_data = vdata;
370 add_resource_widget(load_data->xuni, load_data->widget, data);
373 void load_resource_widgets(struct xuni_t *xuni, struct widget_t **widget,
374 struct resource_data_t *data) {
376 struct load_resource_widget_t callback_data;
377 callback_data.xuni = xuni;
378 callback_data.widget = widget;
380 search_resource_tag(data, "widget", 1,
381 load_resource_widgets_callback, &callback_data);
384 /* !!! use wtype instead */
385 static enum widget_type_t get_widget_type(const char *find) {
386 struct string_index_t data[] = {
387 {"box", WIDGET_BOX},
388 {"button", WIDGET_BUTTON},
389 {"checkbox", WIDGET_CHECKBOX},
390 {"combobox", WIDGET_COMBOBOX},
391 {"font", WIDGET_FONT},
392 {"image", WIDGET_IMAGE},
393 {"image tile", WIDGET_IMAGE_TILE},
394 {"label", WIDGET_LABEL},
395 {"listbox", WIDGET_LISTBOX},
396 {"panel", WIDGET_PANEL},
397 {"textarea", WIDGET_TEXTAREA},
398 {"textbox", WIDGET_TEXTBOX},
399 {"theme", WIDGET_THEME}
401 size_t type = string_to_index(data, sizeof(data) / sizeof(*data), find);
403 if(type == (size_t)-1) {
404 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
405 "Invalid widget type: \"%s\"", find);
408 return (enum widget_type_t)type;
411 /*! Returns the value of the angle stored in \a list. \a list should contain
412 the text representing the angle as well as an optional tag "unit", which
413 should be either "degrees" or "radians" and specifies the unit type of the
414 original angle. Note that the returned value is always in radians, no
415 matter what the original angle's unit type was.
417 \param list The resource tag to get the angle from.
418 \return The angle, in radians, that was represented in \a list.
420 static double get_angle(struct resource_list_t *list) {
421 double angle;
422 const char *unit;
424 unit = find_resource_text(list, "unit");
425 angle = get_expression_value(first_resource_text(list), 0);
427 if(unit) {
428 if(!strcmp(unit, "degrees")) angle = degrees_to_radians(angle);
429 else if(strcmp(unit, "radians")) {
430 printf("*** Unrecognized unit type \"%s\" for \"angle\"\n",
431 unit);
435 return angle;
438 /* this could handle all theme widgets */
439 static size_t find_font_name(struct resource_list_t *list) {
440 const char *name = find_resource_text(list, "font");
441 enum wid_theme_t which = THEME_FONT_SANS;
443 if(name) {
444 if(!strcmp(name, "sans")) which = THEME_FONT_SANS;
445 else if(!strcmp(name, "mono")) which = THEME_FONT_MONO;
446 else {
447 printf("*** Unknown font name \"%s\"\n", name);
451 return (size_t)which;
454 static int init_resource_widget_ref(struct xuni_t *xuni,
455 struct widget_t *widget, enum widget_type_t type,
456 struct resource_list_t *list) {
458 const char *refname;
459 struct widget_t *ref;
461 refname = find_resource_text(list, "ref");
462 if(!refname) return 0;
464 ref = find_widget(xuni->gui->widget, refname);
465 if(!ref) return 0;
467 widget->type = type;
469 switch(type) {
470 case WIDGET_FONT:
471 xuni_memory_increment(ref->p.font);
472 widget->p.font = ref->p.font;
474 break;
475 default:
476 printf("*** Error: <ref> not supported for widgets of type %s\n",
477 get_widget_type_name(xuni, type));
478 break;
481 return 1;
484 static enum label_type_t get_label_alignment(const char *name) {
485 struct string_index_t data[] = {
486 {"center", LABEL_ALIGN_CENTRE},
487 {"centre", LABEL_ALIGN_CENTRE},
488 {"left", LABEL_ALIGN_LEFT},
489 {"right", LABEL_ALIGN_RIGHT}
491 size_t index;
493 if(!name) return LABEL_ALIGN_LEFT;
495 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
497 if(index == (size_t)-1) {
498 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
499 "Invalid label alignment type: \"%s\"", name);
500 return LABEL_ALIGN_LEFT;
503 return index;
506 static enum scrollbar_use_t get_scrollbar_use(const char *name) {
507 struct string_index_t data[] = {
508 {"both", SCROLLBAR_USE_BOTH},
509 {"horizontal", SCROLLBAR_USE_HORIZONTAL},
510 {"none", SCROLLBAR_USE_NONE},
511 {"vertical", SCROLLBAR_USE_VERTICAL}
513 size_t index;
515 if(!name) return SCROLLBAR_USE_VERTICAL;
517 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
519 if(index == (size_t)-1) {
520 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
521 "Invalid scrollbar use: \"%s\"", name);
522 return SCROLLBAR_USE_VERTICAL;
525 return index;
528 /*! Calls the right initialization function for a widget of type \a type.
529 Passes the data found in the resource structure \a list on to the
530 initialization functions.
532 Note that it would be difficult to replace this switch with an array of
533 function pointers or some such structure, because the initialization
534 functions all take different parameters.
536 \param xuni A pointer to the main xuni structure.
537 \param widget The widget to initialize. This should have been allocated
538 by allocate_widget(), and its position should have been initialized
539 by init_widget_pos(), prior to calling this function. However, this
540 widget will be a generic widget, without its type set.
541 \param type The type of the widget \a widget.
542 \param list The resource file structure from which to obtain the data to
543 initialize the widget \a widget with.
545 static void init_resource_widget(struct xuni_t *xuni, struct widget_t *widget,
546 enum widget_type_t type, struct resource_list_t *list) {
548 if(init_resource_widget_ref(xuni, widget, type, list)) return;
550 switch(type) {
551 case WIDGET_BOX:
552 init_box(widget, BOX_STYLE_NORMAL, 0);
553 break;
554 case WIDGET_BUTTON:
555 init_button(widget, new_sub_label(widget,
556 find_font_name(list),
557 find_resource_text(list, "text")));
558 break;
559 case WIDGET_CHECKBOX:
560 init_checkbox(widget, xuni,
561 find_font_name(list),
562 find_resource_text(list, "text"), 0);
563 break;
564 case WIDGET_COMBOBOX:
565 init_combobox(xuni, widget, find_font_name(list));
566 break;
567 case WIDGET_FONT:
568 init_font(widget, find_resource_text(list, "path"));
569 break;
570 case WIDGET_IMAGE:
571 init_image(widget, find_resource_text(list, "path"),
572 get_angle(first_resource_tag(list, "angle")),
573 IMAGE_LOAD_FREE, IMAGE_RESCALE_LAZY);
574 break;
575 case WIDGET_IMAGE_TILE:
576 init_image_tile(widget, find_resource_text(list, "path"),
577 (int)get_expression_value(find_resource_text(list, "xinc"), 0),
578 (int)get_expression_value(find_resource_text(list, "yinc"), 0));
579 break;
580 case WIDGET_LABEL:
581 init_label(widget, find_font_name(list),
582 find_resource_text(list, "text"),
583 get_label_alignment(find_resource_text(list, "align")),
584 (Uint8)get_expression_value(find_resource_text(list,
585 "red"), 255),
586 (Uint8)get_expression_value(find_resource_text(list,
587 "green"), 255),
588 (Uint8)get_expression_value(find_resource_text(list,
589 "blue"), 255));
590 break;
591 case WIDGET_LISTBOX:
592 init_listbox(widget, xuni,
593 get_scrollbar_use(find_resource_text(list, "scrollbar-allow")),
594 get_scrollbar_use(find_resource_text(list, "scrollbar-force")));
595 break;
596 case WIDGET_PANEL:
597 init_panel_from_resource(xuni, widget, list);
598 /*init_panel(widget);*/
599 break;
600 case WIDGET_TEXTAREA:
601 init_textarea(widget, xuni);
602 break;
603 case WIDGET_TEXTBOX:
604 init_textbox(widget, xuni, find_resource_text(list, "text"),
605 find_font_name(list));
606 break;
607 case WIDGET_THEME:
608 init_theme(widget,
609 get_expression_value(find_resource_text(list, "box-width"),
610 100 / 60.0),
611 get_expression_value(find_resource_text(list, "box-height"),
612 100 / 60.0));
613 break;
614 default:
615 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
616 "Widgets of type %s cannot be instantiated in resource files",
617 get_widget_type_name(xuni, type));
618 break;
622 static enum pos_pack_t get_pos_pack(const char *name) {
623 struct string_index_t data[] = {
624 {"bottom", POS_PACK_BOTTOM},
625 {"none", POS_PACK_NONE},
626 {"top", POS_PACK_TOP}
628 size_t index;
630 if(!name) return POS_PACK_NONE;
632 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
634 if(index == (size_t)-1) {
635 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
636 "Invalid position pack type: \"%s\"", name);
637 return POS_PACK_NONE; /* a sensible default */
640 return index;
643 static void process_widget_accelerators(struct xuni_t *xuni,
644 struct widget_t *panel, struct widget_t *widget,
645 struct resource_list_t *list) {
647 size_t x;
649 for(x = 0; x < list->n; x ++) {
650 if(list->data[x]->type != RESOURCE_TAG
651 || strcmp(list->data[x]->data.tag->name, "accelerator")) {
653 continue;
656 if(panel->type != WIDGET_PANEL) {
657 printf("Attempted to add an accelerator to a \"%s\"\n",
658 get_widget_type_name(xuni, panel->type));
660 else {
661 widget_accelerator_from_string(xuni, panel, widget,
662 first_resource_text(list->data[x]->data.tag->list));
667 enum { ACCELERATOR_ERROR = 0xf000 };
669 static void widget_accelerator_from_string(struct xuni_t *xuni,
670 struct widget_t *panel, struct widget_t *widget, const char *data) {
672 const char *end;
673 char c;
674 SDLKey key = (size_t)-1;
675 SDLMod mod = KMOD_NONE, m = KMOD_NONE;
677 if(!data) return;
679 do {
680 for(end = data; isalnum(*end); end ++);
682 c = *end;
683 *(char *)end = 0;
684 /*printf("accelerator: \"%s\"\n", data);*/
685 m = get_key_mod_from_string(data);
686 if(m == ACCELERATOR_ERROR) {
687 if(key != ACCELERATOR_ERROR) {
688 key = get_key_identifier_from_string(data);
689 if(key == ACCELERATOR_ERROR) {
690 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
691 "Unknown key/mod name \"%s\"\n", data);
694 else {
695 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
696 "Multiple key specified: \"%s\"\n", data);
699 else mod = m;
701 *(char *)end = c;
703 for(data = end; isspace(*data); data ++);
704 end = data;
705 } while(*data);
707 add_widget_accelerator(xuni, panel, widget, key, mod);
710 static SDLMod get_key_mod_from_string(const char *name) {
711 struct string_index_t data[] = {
712 {"alt", KMOD_ALT},
713 {"control", KMOD_CTRL},
714 {"ctrl", KMOD_CTRL},
715 {"lalt", KMOD_LALT},
716 {"lcontrol", KMOD_LCTRL},
717 {"lctrl", KMOD_LCTRL},
718 {"lshift", KMOD_LSHIFT},
719 {"none", KMOD_NONE},
720 {"nothing", KMOD_NONE},
721 {"ralt", KMOD_RALT},
722 {"rcontrol", KMOD_RCTRL},
723 {"rctrl", KMOD_RCTRL},
724 {"rshift", KMOD_RSHIFT},
725 {"shift", KMOD_SHIFT}
727 size_t index;
729 if(!name) return ACCELERATOR_ERROR;
731 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
733 if(index == (size_t)-1) return ACCELERATOR_ERROR;
734 /*printf("Found mod accelerator: %x\n", (unsigned)index);*/
736 return index;
739 static SDLKey get_key_identifier_from_string(const char *name) {
740 struct string_index_t data[] = {
741 {"f1", SDLK_F1},
742 {"f10", SDLK_F10},
743 {"f11", SDLK_F11},
744 {"f12", SDLK_F12},
745 {"f2", SDLK_F2},
746 {"f3", SDLK_F3},
747 {"f4", SDLK_F4},
748 {"f5", SDLK_F5},
749 {"f6", SDLK_F6},
750 {"f7", SDLK_F7},
751 {"f8", SDLK_F8},
752 {"f9", SDLK_F9}
754 size_t index;
756 if(!name) return ACCELERATOR_ERROR;
758 if(*name && !name[1]) {
759 /* ... */
762 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
764 if(index == (size_t)-1) {
765 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
766 "Unknown key name \"%s\"", name);
767 return ACCELERATOR_ERROR;
770 return index;
773 /*! Creates a widget based on the information in the resource tree node
774 \a data.
776 Also sets the current theme to the first theme that is found in the
777 resource tree.
779 \param xuni The xuni_t structure, to pass on to other functions and set
780 the current theme in and so on.
781 \param base The widget to add the new widget to as a compose widget. If
782 \a base is NULL, it means that the root widget is being created, in
783 which case \a base is set to point to the new widget.
784 \param data The resource tree node representing a {widget}, from which to
785 create a widget.
787 static void add_resource_widget(struct xuni_t *xuni, struct widget_t **base,
788 struct resource_data_t *data) {
790 struct widget_t *widget;
791 struct resource_list_t *list;
792 enum widget_type_t type;
794 list = data->data.tag->list;
796 #if 0
797 widget = init_resource_widget_ref(xuni, list);
798 if(widget) {
799 type = widget->type;
801 else {
802 #endif
803 type = get_widget_type(find_resource_text(list, "type"));
804 if(type == WIDGET_NONE) return;
806 if(!*base) {
807 *base = allocate_widget(find_resource_text(list, "name"), 0);
808 widget = *base;
810 else {
811 if(!(*base)->compose) (*base)->compose = allocate_panel(*base);
813 add_allocate_widget(*base, find_resource_text(list, "name"));
815 widget = last_compose_widget(*base);
818 init_widget_pos(widget,
819 get_expression_value(find_resource_text(list, "xpos"), 0),
820 get_expression_value(find_resource_text(list, "ypos"), 0),
821 get_expression_value(find_resource_text(list, "width"), 1),
822 get_expression_value(find_resource_text(list, "height"), 1),
823 get_pos_pack(find_resource_text(list, "pack")));
825 if(!get_expression_value(find_resource_text(list, "enabled"), 1)) {
826 widget->visibility &= ~WIDGET_VISIBILITY_CLICKABLE;
828 if(!get_expression_value(find_resource_text(list, "selable"), 1)) {
829 widget->visibility &= ~WIDGET_VISIBILITY_SELABLE;
831 if(!get_expression_value(find_resource_text(list, "visible"), 1)) {
832 widget->visibility &= ~WIDGET_VISIBILITY_VISIBLE;
835 process_widget_accelerators(xuni, *base, widget, list);
837 init_resource_widget(xuni, widget, type, list);
839 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
841 if(widget->type == WIDGET_THEME) {
842 if(!xuni->theme->current) {
843 xuni->theme->current = widget;
846 /* recurse (indirectly) */
847 load_resource_widgets(xuni, &widget, data);
849 set_theme_nameid(widget);
850 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
852 else if(widget->type == WIDGET_PANEL) {
853 /* recurse (indirectly) */
854 load_resource_widgets(xuni, &widget, data);
858 #if 0
859 /* This code returns a widget based on a list of names to dereference. It
860 works, but using WIDs is much more efficient, and this is not used at the
861 moment.
863 static int compare_widget_name_nonrec(struct widget_t *widget, va_list arg) {
864 const char *match = va_arg(arg, const char *);
866 if(!widget || !match) return !match;
868 return strcmp(match, widget->name) == 0
869 && compare_widget_name_nonrec(widget->base, arg);
872 static int compare_widget_name_nonrec_reverse(struct widget_t **widget,
873 va_list arg) {
875 const char *match = va_arg(arg, const char *);
877 if(!*widget || !match) return !match;
879 if(!compare_widget_name_nonrec_reverse(widget, arg)
880 || !(*widget)->name) {
882 return 0;
885 if(strcmp(match, (*widget)->name) == 0) {
886 *widget = (*widget)->base;
887 return 1;
890 return 0;
893 int compare_widget_name(struct widget_t *widget, ...) {
894 int r;
896 va_list arg;
897 va_start(arg, widget);
899 r = compare_widget_name_nonrec_reverse(&widget, arg);
901 va_end(arg);
903 return r;
905 #endif
907 /*! Allocates enough space for a struct widget_nameid_t.
908 \return A pointer to the newly allocated nameid structure.
910 struct nameid_t *allocate_nameid(void) {
911 struct nameid_t *nameid = xuni_memory_allocate(sizeof(*nameid));
913 nameid->widget = 0;
914 nameid->widgets = 0;
916 return nameid;
919 /*! Adds the widget \a widget to \a nameid.
921 \param nameid The array of nameids to add the new wid to.
922 \param widget The widget to add to \a nameid.
923 \param id The wid to use for this widget.
924 \param name The path to the widget \a widget relative to the panel widget
925 that \a nameid is a part of.
927 void init_wid_widget(struct nameid_t *nameid, struct widget_t *widget,
928 size_t id, const char *name) {
930 size_t x;
932 if(nameid->widgets <= id) {
933 nameid->widget = xuni_memory_resize(nameid->widget,
934 (id + 1) * sizeof(*nameid->widget));
936 /* clear any new nameids allocated between nameid->widgets and id */
937 for(x = nameid->widgets; x <= id; x ++) {
938 nameid->widget[x].id = 0;
939 nameid->widget[x].name = 0;
940 nameid->widget[x].widget = 0;
943 nameid->widgets = id + 1;
946 if(nameid->widget[id].id || nameid->widget[id].name
947 || nameid->widget[id].widget) {
949 log_message(ERROR_TYPE_WARNING, 0, __FILE__, __LINE__,
950 "Duplicate wid: old: \"%s\" [%lu], new: \"%s\" [%lu]",
951 nameid->widget[id].name, (unsigned long)nameid->widget[id].id,
952 name, (unsigned long)id);
954 else {
955 nameid->widget[id].id = id;
956 if(widget) widget->id = id;
957 nameid->widget[id].name = name;
958 nameid->widget[id].widget = widget;
962 /*! Just like init_wid(), but allows the widget referred to be \a name to not
963 exist. (init_wid() would display an error in that case.)
965 Mainly useful for widgets that do not have to be loaded. Prime examples
966 of this are the various widget images of themes, like the checkbox image.
968 \param base The base of the widget, from which to use find_widget() to
969 find the widget represented by the widget path \a name.
970 \param nameid The nameid array to add the new nameid to.
971 \param id The wid of the new widget -- usually a client-defined enum
972 value.
973 \param name The path to the widget, starting from \a base.
975 void init_wid_possible_zero(struct widget_t *base, struct nameid_t *nameid,
976 size_t id, const char *name) {
978 init_wid_widget(nameid, find_widget(base, name), id, name);
981 /*! Adds a widget to the nameid array of \a use. The widget is located using
982 the name in \a name with the base \a base.
984 \param base The base of the widget, from which to use find_widget() to
985 find the widget represented by the widget path \a name.
986 \param use The panel widget to add the new nameid to.
987 \param id The wid of the new widget -- usually a client-defined enum
988 value.
989 \param name The path to the widget, starting from \a base.
991 void init_wid(struct widget_t *base, struct widget_t *use, size_t id,
992 const char *name) {
994 struct widget_t *widget = find_widget(base, name);
996 if(!use->p.panel->nameid) {
997 use->p.panel->nameid = allocate_nameid();
1000 if(widget) {
1001 init_wid_widget(use->p.panel->nameid, widget, id, name);
1003 else {
1004 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
1005 "Could not find widget \"%s\" [wid %lu]", name,
1006 (unsigned long)id);
1010 void init_wid_array(struct widget_t *base, struct widget_t *use,
1011 struct wid_init_t *init, size_t n) {
1013 size_t x;
1015 for(x = 0; x < n; x ++) {
1016 init_wid(base, use, init[x].id, init[x].name);
1020 int widget_process_character(struct xuni_t *xuni, SDL_keysym *sym) {
1021 struct widget_t *widget = xuni->gui->active.widget;
1023 if(!widget) return 0;
1025 /*printf("widget_process_character(): widget=%p, key=%i [%c]\n",
1026 (void *)widget, sym->unicode,
1027 isprint(sym->unicode) ? sym->unicode : '-');*/
1029 /* !!! */
1030 if(widget->type == WIDGET_TEXTBOX) {
1031 widget = widget->compose->widget[WID_TEXTBOX_LABEL];
1034 if(widget->type == WIDGET_LABEL) {
1035 if(isprint(sym->unicode)) {
1036 edit_add_char(&xuni->gui->edit, sym->unicode);
1038 else if(sym->unicode == '\b' && xuni->gui->edit.pos) {
1039 xuni->gui->edit.pos --;
1040 edit_del_char(&xuni->gui->edit);
1042 else if(sym->unicode == '\r') {
1043 clear_active(xuni, &xuni->gui->active, 1);
1045 else if(sym->sym == SDLK_DELETE
1046 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1048 edit_del_char(&xuni->gui->edit);
1050 else if(sym->sym == SDLK_LEFT && xuni->gui->edit.pos) {
1051 xuni->gui->edit.pos --;
1053 else if(sym->sym == SDLK_RIGHT
1054 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1056 xuni->gui->edit.pos ++;
1058 else if(sym->sym == SDLK_END) {
1059 xuni->gui->edit.pos = xuni->gui->edit.len;
1061 else if(sym->sym == SDLK_HOME) {
1062 xuni->gui->edit.pos = 0;
1064 else {
1065 return 0;
1068 if(xuni->gui->edit.data) {
1069 reposition_label_data(xuni, xuni->gui->edit.data,
1070 &xuni->gui->edit, xuni->gui->edit.pos);
1073 return 1;
1076 /*printf("data: \"%s\"\n", xuni->gui->edit.data);*/
1078 return 0;
1081 int widget_can_be_active(struct widget_t *widget) {
1082 if(!widget) return 0;
1084 if(widget->type == WIDGET_TEXTBOX) return 1;
1086 return 0;
1089 void update_widget(struct xuni_t *xuni, struct widget_t *widget) {
1090 if(!widget) return;
1092 if(widget->type == WIDGET_TEXTBOX) {
1093 /*gui->sel.p.widget->base->compose
1094 ->widget[WID_TEXTBOX_LABEL]->p.label->text = gui->edit.data;*/
1096 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
1100 void enable_unicode(const struct widget_t *widget) {
1101 int v = 0;
1103 if(!widget) return;
1105 if(widget->type == WIDGET_TEXTBOX) {
1106 v = 1;
1109 if(SDL_EnableUNICODE(-1) != v) SDL_EnableUNICODE(v);
1112 void activate_widget(struct widget_t *widget, struct xuni_t *xuni,
1113 int xp, int yp) {
1115 struct widget_edit_t *edit = &xuni->gui->edit;
1116 struct widget_t *w;
1117 int x;
1119 if(!widget) return;
1121 if(widget->type == WIDGET_TEXTBOX) {
1122 struct label_t *label = widget->compose->widget[WID_TEXTBOX_LABEL]
1123 ->p.label;
1124 size_t len = label->text ? strlen(label->text) : 0;
1126 if(edit->data) deactivate_widget(xuni->gui->active.widget, xuni);
1128 edit->data = label;
1129 edit->datawidget = widget->compose->widget[WID_TEXTBOX_LABEL];
1131 edit->prev = xuni_memory_allocate(sizeof(*edit->prev));
1132 *edit->prev = *label;
1134 edit->data->text = xuni_memory_duplicate_string_len(label->text, len);
1136 edit->len = edit->alloc = label->text ? len : 0;
1138 if(label->text) {
1139 w = widget->compose->widget[WID_TEXTBOX_LABEL];
1141 x = xp - w->pos->real.x;
1142 if(w->pos->clip) x += w->pos->clip->xclip;
1144 edit->pos = width_to_label_pos(xuni, x,
1145 get_theme_widget(xuni, label->font),
1146 (char *)label->text); /* !!! const */
1148 else {
1149 edit->pos = 0;
1152 /* Reposition the textbox so that the cursor is inside the visible
1153 portion of the textbox. If a half-invisible character is selected,
1154 for example, this will scroll the textbox slightly so that the
1155 cursor is visible.
1157 This would be unnecessary if textbox selection was based on the
1158 label instead of the box of the textbox.
1160 widget_event(xuni, widget, WIDGET_EVENT_REPOSITION);
1164 void revert_widget(struct xuni_t *xuni, struct widget_t *widget) {
1165 if(!widget) return;
1167 if(widget->type == WIDGET_TEXTBOX) {
1168 struct widget_t *label = widget->compose->widget[WID_TEXTBOX_LABEL];
1170 /*printf("revert_widget(): edit: %p \"%s\"\n", label->p.label->text,
1171 label->p.label->text);
1172 printf("revert_widget(): prev: %p \"%s\"\n",
1173 xuni->gui->edit.prev->text, xuni->gui->edit.prev->text);*/
1175 xuni_memory_free((char *)label->p.label->text); /* !!! const */
1176 /*SDL_FreeSurface(label->p.label->label);*/
1178 if(xuni->gui->edit.prev) {
1179 label->p.label->text = xuni->gui->edit.prev->text;
1181 xuni_memory_free(xuni->gui->edit.prev);
1183 else label->p.label->text = xuni_memory_duplicate_string_len("", 0);
1185 widget_event(xuni, label, WIDGET_EVENT_RESCALE);
1187 init_edit(&xuni->gui->edit);
1191 void deactivate_widget(struct widget_t *widget, struct xuni_t *xuni) {
1192 if(!widget) return;
1194 if(widget->type == WIDGET_TEXTBOX) {
1195 /* makes comboboxes have a history of what was typed in them */
1196 /*if(widget->base
1197 && widget->base->type == WIDGET_COMBOBOX) {
1199 struct widget_t *label = widget->compose
1200 ->widget[WID_TEXTBOX_LABEL];
1202 add_combobox_item(xuni, widget->base, label->p.label->font,
1203 label->p.label->text);
1204 widget_event(xuni, widget->base->compose->widget
1205 [WID_COMBOBOX_DROPDOWN], WIDGET_EVENT_RESCALE);
1208 call_deactivate_func(xuni, widget);
1210 xuni_memory_free((char *)xuni->gui->edit.prev->text); /* !!! const */
1211 /*free_surface(xuni->gui->edit.prev->label);*/
1212 xuni_memory_free(xuni->gui->edit.prev);
1214 init_edit(&xuni->gui->edit);
1218 void perform_widget_click(struct xuni_t *xuni, struct widget_t *widget) {
1219 if(!widget) return;
1221 if(widget->base) {
1222 if(widget->type == WIDGET_CHECKBOX) {
1223 toggle_checkbox(widget);
1225 else if(widget->base->type == WIDGET_SCROLLBAR) {
1226 if(widget->base->compose->widget[WID_SCROLLBAR_UP]
1227 == widget) {
1229 move_scrollbar(xuni, widget->base, -10);
1231 else if(widget->base->compose->widget
1232 [WID_SCROLLBAR_DOWN] == widget) {
1234 move_scrollbar(xuni, widget->base, 10);
1237 /*reposition_widget(xuni, widget->base);*/
1238 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
1239 WIDGET_EVENT_REPOSITION);
1241 else if(widget->base->type == WIDGET_COMBOBOX) {
1242 if(widget->base->compose->widget[WID_COMBOBOX_BUTTON] == widget) {
1243 widget->base->compose->widget[WID_COMBOBOX_DROPDOWN]
1244 ->visibility ^= WIDGET_VISIBILITY_VISIBLE;
1250 void free_gui(struct xuni_t *xuni) {
1251 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_FREE);