Minor syntactical changes for readability.
[xuni.git] / src / gui.c
blob78a28e1d767610bbb0e7cc210e116f2da53958bc
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 int set_scrollbar_sel(struct xuni_t *xuni, struct widget_sel_t *sel,
39 int xp, int yp, int click);
40 static int compare_widget_type(void *data, size_t n, void *find);
41 static enum widget_type_t get_widget_type(struct xuni_t *xuni,
42 const char *find);
43 static double get_angle(struct resource_list_t *list);
44 static size_t find_font_name(struct resource_list_t *list);
45 static int init_resource_widget_ref(struct xuni_t *xuni,
46 struct widget_t *widget, enum widget_type_t type,
47 struct resource_list_t *list);
48 static enum label_type_t get_label_alignment(const char *name);
49 static enum scrollbar_use_t get_scrollbar_use(const char *name);
50 static void init_resource_widget(struct xuni_t *xuni, struct widget_t *widget,
51 enum widget_type_t type, struct resource_list_t *list);
52 static enum pos_pack_t get_pos_pack(const char *name);
53 static void process_widget_accelerators(struct xuni_t *xuni,
54 struct widget_t *panel, struct widget_t *widget,
55 struct resource_list_t *list);
56 static void widget_accelerator_from_string(struct xuni_t *xuni,
57 struct widget_t *panel, struct widget_t *widget, const char *data);
58 static SDLMod get_key_mod_from_string(const char *name);
59 static SDLKey get_key_identifier_from_string(const char *name);
60 static void add_resource_widget(struct xuni_t *xuni, struct widget_t **base,
61 struct resource_data_t *data);
63 static size_t move_cursor_word(struct widget_edit_t *edit, size_t startpos,
64 int dir, int flip);
66 void init_theme_structure(struct theme_t *theme) {
67 theme->current = 0;
68 theme->cursors.normal = 0;
69 theme->cursors.text = 0;
70 theme->cursors.which = CURSOR_NORMAL;
73 /*! Initializes the gui member of the structure \a xuni.
74 \param xuni The xuni structure to initialize the gui member of.
76 void init_gui(struct xuni_t *xuni) {
77 /*gui->panel = allocate_panel(0);*/
78 xuni->gui->widget = 0;
80 init_edit(&xuni->gui->edit);
81 clear_gui(xuni, (size_t)-1, -1);
84 void init_edit(struct widget_edit_t *edit) {
85 edit->data = 0;
86 edit->prev = 0;
87 edit->datawidget = 0;
88 edit->alloc = 0;
89 edit->len = 0;
90 edit->pos = 0;
93 void clear_gui(struct xuni_t *xuni, panel_type_t panel, int keep) {
94 clear_widget_sel(xuni->gui->widget);
95 clear_sel(&xuni->gui->sel);
96 clear_active(xuni, &xuni->gui->active, keep); /* before clear_edit() */
97 clear_edit(&xuni->gui->edit);
98 clear_tab(&xuni->gui->tab, panel);
101 void clear_sel(struct widget_sel_t *sel) {
102 sel->wasin = 0;
103 sel->clickin = 0;
104 sel->p.widget = 0;
107 void clear_edit(struct widget_edit_t *edit) {
108 edit->data = 0;
110 if(edit->prev) {
111 xuni_memory_free(edit->prev);
112 edit->prev = 0;
115 edit->datawidget = 0;
117 edit->alloc = 0;
118 edit->len = 0;
119 edit->pos = 0;
122 void clear_active(struct xuni_t *xuni, struct widget_p_t *wp, int keep) {
123 if(!keep) {
124 revert_widget(xuni, wp->widget);
126 widget_event(xuni, wp->widget, WIDGET_EVENT_RESCALE);
128 else if(keep > 0) {
129 deactivate_widget(wp->widget, xuni);
132 wp->widget = 0;
134 SDL_EnableUNICODE(0);
137 void clear_tab(struct widget_tab_t *tab, panel_type_t panel) {
138 tab->panel = panel;
139 tab->sel = (size_t)-1;
142 /* !!! lots of const -> non-const in this function */
143 void edit_add_char(struct widget_edit_t *edit, char c) {
144 if(edit->len + 1 >= edit->alloc) {
145 if(!edit->alloc) edit->alloc = 2;
146 else edit->alloc *= 2;
148 edit->data->text
149 = xuni_memory_resize((char *)edit->data->text,
150 edit->alloc);
153 if(edit->pos != edit->len) {
154 memmove((char *)edit->data->text + edit->pos + 1,
155 (char *)edit->data->text + edit->pos,
156 edit->len - edit->pos + 1);
159 ((char *)edit->data->text)[edit->pos] = c;
161 if(edit->pos == edit->len) {
162 ((char *)edit->data->text)[edit->pos + 1] = 0;
165 edit->pos ++;
166 edit->len ++;
169 /* !!! lots of const -> non-const in this function */
170 void edit_del_chars(struct widget_edit_t *edit, size_t n) {
171 if(edit->pos + n < edit->len) {
172 memmove((char *)edit->data->text + edit->pos,
173 (char *)edit->data->text + edit->pos + n,
174 edit->len - edit->pos + n - 1);
176 else ((char *)edit->data->text)[edit->pos] = 0;
178 edit->len -= n;
181 /*! Clears the sel member of the widget \a widget and all of its child
182 widgets. Returns true if a repaint will be required; i.e., if any widget
183 that had its sel member set to false was originally true. (If this was the
184 case, a widget will have changed from selected to not selected, and a
185 repaint would be necessary.)
187 \param widget The widget at the top of the tree of widgets to clear the
188 sel members of.
189 \return True if a repaint is needed, that is, if any widget had a sel
190 member set to true.
192 int clear_widget_sel(struct widget_t *widget) {
193 size_t x;
194 int repaint = 0;
196 if(!widget) return 0;
198 if(widget->sel) repaint = 1;
199 widget->sel = 0;
201 if(!widget->compose) return 0;
203 for(x = 0; x < widget->compose->widgets; x ++) {
204 repaint = clear_widget_sel(widget->compose->widget[x]) || repaint;
207 return repaint;
210 struct widget_t *set_widget_sel_rec(struct widget_sel_t *sel, int xp, int yp,
211 struct widget_t *widget, int *repaint) {
213 size_t x;
214 struct widget_t *w = 0, *last = 0;
216 #if 0
217 if(widget && !strcmp(widget->name, "data")) {
218 /*printf("item \"%s\": ", widget->p.label->text);*/
219 printf("data: ");
221 printf("in_sdl_rect()? %s",
222 in_sdl_rect(xp, yp, &widget->pos->real) ? "yes" : "no");
223 printf(" pos_in_rect()? %s\n",
224 pos_in_rect(xp, yp, widget->pos) ? "yes" : "no");
226 if(!in_sdl_rect(xp, yp, &widget->pos->real)) {
227 printf("cursor: (%i,%i) widget: (%i,%i) to (%i,%i)\n",
228 xp, yp,
229 widget->pos->real.x,
230 widget->pos->real.y,
231 widget->pos->real.x + widget->pos->real.w,
232 widget->pos->real.y + widget->pos->real.h);
235 #endif
237 if(widget && widget->visibility & WIDGET_VISIBILITY_VISIBLE
238 && in_sdl_rect(xp, yp, &widget->pos->real)) {
240 if(widget->compose) {
241 for(x = widget->compose->widgets - 1; x != (size_t)-1; x --) {
242 w = set_widget_sel_rec(sel, xp, yp,
243 widget->compose->widget[x], repaint);
244 if(w) {
245 if(!last && widget->compose->widget[x]->visibility
246 & WIDGET_VISIBILITY_INDEPENDENT) {
248 last = w;
251 if(!set_widget_sel_compose(sel, xp, yp, w)) break;
252 else last = 0;
256 if(x != (size_t)-1) x --;
258 while(x != (size_t)-1) {
259 *repaint = clear_widget_sel(widget->compose->widget[x])
260 || *repaint;
262 x --;
266 if(!last && widget->visibility & WIDGET_VISIBILITY_SELABLE) {
267 last = widget;
268 if(!widget->sel) *repaint = 1;
269 widget->sel = 1;
271 else {
272 if(widget->sel) *repaint = 1;
273 widget->sel = 0;
276 else *repaint = clear_widget_sel(widget) || *repaint;
278 return last;
281 static int set_widget_sel_compose(struct widget_sel_t *sel, int xp, int yp,
282 struct widget_t *widget) {
284 /*widget->sel = 1;*/
286 if(widget->base) {
287 /*if(!(widget->visibility & WIDGET_VISIBILITY_INDEPENDENT)) {
288 widget->base->sel = 1;
291 if(!(widget->base->visibility & WIDGET_VISIBILITY_NOT_COMPOSE)) {
292 return 1;
296 return 0;
299 int set_widget_sel_repaint(struct xuni_t *xuni, struct widget_sel_t *sel,
300 int xp, int yp, int click, struct widget_t *widget) {
302 return set_widget_repaint(xuni, sel, xp, yp, click, widget)
303 | set_widget_sel(sel, xp, yp, click, widget);
306 int set_widget_sel(struct widget_sel_t *sel, int xp, int yp, int click,
307 struct widget_t *widget) {
309 struct widget_t *w;
310 int repaint = 0;
312 if(!widget) return 0;
314 if(sel->p.widget && !sel->clickin && !sel->wasin) clear_sel(sel);
316 if(!sel->clickin) {
317 /*clear_widget_sel(widget);*/
318 w = set_widget_sel_rec(sel, xp, yp, widget, &repaint);
320 /*if(repaint && w == sel->p.widget) puts("inline change");*/
322 if(w && w != sel->p.widget) {
323 sel->p.widget = w;
324 sel->wasin = 1;
325 sel->clickin = click;
327 return 1;
331 return repaint;
334 int set_widget_repaint(struct xuni_t *xuni, struct widget_sel_t *sel,
335 int xp, int yp, int click, struct widget_t *widget) {
337 int r, v = 0;
339 if(sel->p.widget) {
340 v = set_widget_sel(sel, xp, yp, click, widget);
342 r = pos_in_rect(xp, yp, sel->p.widget->pos);
344 /* a scrollbar was scrolled via its seek box */
345 if(set_scrollbar_sel(xuni, sel, xp, yp, click)) {
346 v = 1;
349 /* !!! set v=1 only if the widget _changes_ upon loss of mouse focus or whatever */
351 /* the mouse focus in the widget was toggled */
352 if(sel->wasin != r) {
353 sel->wasin = r;
354 v = 1;
357 /* the mouse button was clicked or released inside the widget */
358 if(sel->clickin != click) {
359 sel->clickin = click;
360 v = 1;
363 if(v) sel->p.widget->repaint = 1;
366 return v;
369 static int set_scrollbar_sel(struct xuni_t *xuni, struct widget_sel_t *sel,
370 int xp, int yp, int click) {
372 struct widget_t *seek = sel->p.widget;
373 struct widget_t *scroll = seek->base;
374 struct widget_t *bar = scroll->compose->widget[WID_SCROLLBAR_SEEKBAR];
376 if(scroll && scroll->type == WIDGET_SCROLLBAR
377 && seek == scroll->compose->widget[WID_SCROLLBAR_SEEK]) {
379 if(!sel->clickin && click) {
380 sel->scrollbar.xp = xp;
381 sel->scrollbar.yp = yp;
382 sel->scrollbar.original = get_scrollbar_pos(scroll);
384 else if(sel->clickin) {
385 int amount, max;
386 double pos;
388 if(scroll->p.scrollbar->orientation
389 == SCROLLBAR_ORIENTATION_HORIZONTAL) {
391 amount = xp - sel->scrollbar.xp;
392 max = bar->pos->real.w - seek->pos->real.w;
394 else {
395 amount = yp - sel->scrollbar.yp;
396 max = bar->pos->real.h - seek->pos->real.h;
399 pos = (double)amount / max * 100.0;
401 if(max > 0) {
402 pos += sel->scrollbar.original;
404 if(pos < 0.0) pos = 0.0;
405 if(pos > 100.0) pos = 100.0;
407 set_scrollbar_pos(scroll, pos);
410 if(scroll->base) {
411 widget_event(xuni, scroll->base, WIDGET_EVENT_REPOSITION);
414 return 1;
418 return 0;
421 struct load_resource_widget_t {
422 struct xuni_t *xuni;
423 struct widget_t **widget;
426 static void load_resource_widgets_callback(void *vdata,
427 struct resource_data_t *data);
429 static void load_resource_widgets_callback(void *vdata,
430 struct resource_data_t *data) {
432 struct load_resource_widget_t *load_data = vdata;
434 add_resource_widget(load_data->xuni, load_data->widget, data);
437 void load_resource_widgets(struct xuni_t *xuni, struct widget_t **widget,
438 struct resource_data_t *data) {
440 struct load_resource_widget_t callback_data;
441 callback_data.xuni = xuni;
442 callback_data.widget = widget;
444 search_resource_tag(data, "widget", 1,
445 load_resource_widgets_callback, &callback_data);
448 static enum widget_type_t get_widget_type(struct xuni_t *xuni,
449 const char *find) {
451 size_t pos;
453 if(!wtype_search_name(xuni, find, &pos)) {
454 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
455 "Invalid widget type: \"%s\"", find);
457 return WIDGET_NONE;
460 return (enum widget_type_t)pos;
463 /*! Returns the value of the angle stored in \a list. \a list should contain
464 the text representing the angle as well as an optional tag "unit", which
465 should be either "degrees" or "radians" and specifies the unit type of the
466 original angle. Note that the returned value is always in radians, no
467 matter what the original angle's unit type was.
469 \param list The resource tag to get the angle from.
470 \return The angle, in radians, that was represented in \a list.
472 static double get_angle(struct resource_list_t *list) {
473 double angle;
474 const char *unit;
476 unit = find_resource_text(list, "unit");
477 angle = get_expression_value(first_resource_text(list), 0);
479 if(unit) {
480 if(!strcmp(unit, "degrees")) angle = degrees_to_radians(angle);
481 else if(strcmp(unit, "radians")) {
482 printf("*** Unrecognized unit type \"%s\" for \"angle\"\n",
483 unit);
487 return angle;
490 /* this could handle all theme widgets */
491 static size_t find_font_name(struct resource_list_t *list) {
492 const char *name = find_resource_text(list, "font");
493 enum wid_theme_t which = THEME_FONT_SANS;
495 if(name) {
496 if(!strcmp(name, "sans")) which = THEME_FONT_SANS;
497 else if(!strcmp(name, "mono")) which = THEME_FONT_MONO;
498 else {
499 printf("*** Unknown font name \"%s\"\n", name);
503 return (size_t)which;
506 static int init_resource_widget_ref(struct xuni_t *xuni,
507 struct widget_t *widget, enum widget_type_t type,
508 struct resource_list_t *list) {
510 const char *refname;
511 struct widget_t *ref;
513 refname = find_resource_text(list, "ref");
514 if(!refname) return 0;
516 ref = find_widget(xuni->gui->widget, refname);
517 if(!ref) return 0;
519 widget->type = type;
521 switch(type) {
522 case WIDGET_FONT:
523 xuni_memory_increment(ref->p.font);
524 widget->p.font = ref->p.font;
526 break;
527 default:
528 printf("*** Error: <ref> not supported for widgets of type %s\n",
529 get_widget_type_name(xuni, type));
530 break;
533 return 1;
536 static enum label_type_t get_label_alignment(const char *name) {
537 struct string_index_t data[] = {
538 {"center", LABEL_ALIGN_CENTRE},
539 {"centre", LABEL_ALIGN_CENTRE},
540 {"left", LABEL_ALIGN_LEFT},
541 {"right", LABEL_ALIGN_RIGHT}
543 size_t index;
545 if(!name) return LABEL_ALIGN_LEFT;
547 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
549 if(index == (size_t)-1) {
550 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
551 "Invalid label alignment type: \"%s\"", name);
552 return LABEL_ALIGN_LEFT;
555 return index;
558 static enum scrollbar_use_t get_scrollbar_use(const char *name) {
559 struct string_index_t data[] = {
560 {"both", SCROLLBAR_USE_BOTH},
561 {"horizontal", SCROLLBAR_USE_HORIZONTAL},
562 {"none", SCROLLBAR_USE_NONE},
563 {"vertical", SCROLLBAR_USE_VERTICAL}
565 size_t index;
567 if(!name) return SCROLLBAR_USE_VERTICAL;
569 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
571 if(index == (size_t)-1) {
572 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
573 "Invalid scrollbar use: \"%s\"", name);
574 return SCROLLBAR_USE_VERTICAL;
577 return index;
580 /*! Calls the right initialization function for a widget of type \a type.
581 Passes the data found in the resource structure \a list on to the
582 initialization functions.
584 Note that it would be difficult to replace this switch with an array of
585 function pointers or some such structure, because the initialization
586 functions all take different parameters.
588 \param xuni A pointer to the main xuni structure.
589 \param widget The widget to initialize. This should have been allocated
590 by allocate_widget(), and its position should have been initialized
591 by init_widget_pos(), prior to calling this function. However, this
592 widget will be a generic widget, without its type set.
593 \param type The type of the widget \a widget.
594 \param list The resource file structure from which to obtain the data to
595 initialize the widget \a widget with.
597 static void init_resource_widget(struct xuni_t *xuni, struct widget_t *widget,
598 enum widget_type_t type, struct resource_list_t *list) {
600 if(init_resource_widget_ref(xuni, widget, type, list)) return;
602 switch(type) {
603 case WIDGET_BOX:
604 init_box(widget, BOX_STYLE_NORMAL, 0);
605 break;
606 case WIDGET_BUTTON:
607 init_button(widget, new_sub_label(widget,
608 find_font_name(list),
609 find_resource_text(list, "text")));
610 break;
611 case WIDGET_CHECKBOX:
612 init_checkbox(widget, xuni,
613 find_font_name(list),
614 find_resource_text(list, "text"), 0);
615 break;
616 case WIDGET_COMBOBOX:
617 init_combobox(xuni, widget, find_font_name(list));
618 break;
619 case WIDGET_FONT:
620 init_font(widget, find_resource_text(list, "path"));
621 break;
622 case WIDGET_IMAGE:
623 init_image(widget, find_resource_text(list, "path"),
624 get_angle(first_resource_tag(list, "angle")),
625 IMAGE_LOAD_FREE, IMAGE_RESCALE_LAZY);
626 break;
627 case WIDGET_IMAGE_TILE:
628 init_image_tile(widget, find_resource_text(list, "path"),
629 (int)get_expression_value(find_resource_text(list, "xinc"), 0),
630 (int)get_expression_value(find_resource_text(list, "yinc"), 0));
631 break;
632 case WIDGET_LABEL:
633 init_label(widget, find_font_name(list),
634 find_resource_text(list, "text"),
635 get_label_alignment(find_resource_text(list, "align")),
636 (Uint8)get_expression_value(find_resource_text(list,
637 "red"), 255),
638 (Uint8)get_expression_value(find_resource_text(list,
639 "green"), 255),
640 (Uint8)get_expression_value(find_resource_text(list,
641 "blue"), 255));
642 break;
643 case WIDGET_LISTBOX:
644 init_listbox(widget, xuni,
645 get_scrollbar_use(find_resource_text(list, "scrollbar-allow")),
646 get_scrollbar_use(find_resource_text(list, "scrollbar-force")));
647 break;
648 case WIDGET_PANEL:
649 init_panel_from_resource(xuni, widget, list);
650 /*init_panel(widget);*/
651 break;
652 case WIDGET_TEXTAREA:
653 init_textarea(widget, xuni);
654 break;
655 case WIDGET_TEXTBOX:
656 init_textbox(widget, xuni, find_resource_text(list, "text"),
657 find_font_name(list));
658 break;
659 case WIDGET_THEME:
660 init_theme(widget,
661 get_expression_value(find_resource_text(list, "box-width"),
662 100 / 60.0),
663 get_expression_value(find_resource_text(list, "box-height"),
664 100 / 60.0));
665 break;
666 default:
667 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
668 "Widgets of type %s cannot be instantiated in resource files",
669 get_widget_type_name(xuni, type));
670 break;
674 static enum pos_pack_t get_pos_pack(const char *name) {
675 struct string_index_t data[] = {
676 {"bottom", POS_PACK_BOTTOM},
677 {"none", POS_PACK_NONE},
678 {"top", POS_PACK_TOP}
680 size_t index;
682 if(!name) return POS_PACK_NONE;
684 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
686 if(index == (size_t)-1) {
687 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
688 "Invalid position pack type: \"%s\"", name);
689 return POS_PACK_NONE; /* a sensible default */
692 return index;
695 static void process_widget_accelerators(struct xuni_t *xuni,
696 struct widget_t *panel, struct widget_t *widget,
697 struct resource_list_t *list) {
699 size_t x;
701 for(x = 0; x < list->n; x ++) {
702 if(list->data[x]->type != RESOURCE_TAG
703 || strcmp(list->data[x]->data.tag->name, "accelerator")) {
705 continue;
708 if(panel->type != WIDGET_PANEL) {
709 printf("Attempted to add an accelerator to a \"%s\"\n",
710 get_widget_type_name(xuni, panel->type));
712 else {
713 widget_accelerator_from_string(xuni, panel, widget,
714 first_resource_text(list->data[x]->data.tag->list));
719 enum { ACCELERATOR_ERROR = 0xf000 };
721 static void widget_accelerator_from_string(struct xuni_t *xuni,
722 struct widget_t *panel, struct widget_t *widget, const char *data) {
724 const char *end;
725 char c;
726 SDLKey key = (size_t)-1;
727 SDLMod mod = KMOD_NONE, m = KMOD_NONE;
729 if(!data) return;
731 do {
732 for(end = data; isalnum(*end); end ++);
734 c = *end;
735 *(char *)end = 0;
736 /*printf("accelerator: \"%s\"\n", data);*/
737 m = get_key_mod_from_string(data);
738 if(m == ACCELERATOR_ERROR) {
739 if(key != ACCELERATOR_ERROR) {
740 key = get_key_identifier_from_string(data);
741 if(key == ACCELERATOR_ERROR) {
742 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
743 "Unknown key/mod name \"%s\"\n", data);
746 else {
747 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
748 "Multiple key specified: \"%s\"\n", data);
751 else mod = m;
753 *(char *)end = c;
755 for(data = end; isspace(*data); data ++);
756 end = data;
757 } while(*data);
759 add_widget_accelerator(xuni, panel, widget, key, mod);
762 static SDLMod get_key_mod_from_string(const char *name) {
763 struct string_index_t data[] = {
764 {"alt", KMOD_ALT},
765 {"control", KMOD_CTRL},
766 {"ctrl", KMOD_CTRL},
767 {"lalt", KMOD_LALT},
768 {"lcontrol", KMOD_LCTRL},
769 {"lctrl", KMOD_LCTRL},
770 {"lshift", KMOD_LSHIFT},
771 {"none", KMOD_NONE},
772 {"nothing", KMOD_NONE},
773 {"ralt", KMOD_RALT},
774 {"rcontrol", KMOD_RCTRL},
775 {"rctrl", KMOD_RCTRL},
776 {"rshift", KMOD_RSHIFT},
777 {"shift", KMOD_SHIFT}
779 size_t index;
781 if(!name) return ACCELERATOR_ERROR;
783 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
785 if(index == (size_t)-1) return ACCELERATOR_ERROR;
786 /*printf("Found mod accelerator: %x\n", (unsigned)index);*/
788 return index;
791 static SDLKey get_key_identifier_from_string(const char *name) {
792 struct string_index_t data[] = {
793 {"f1", SDLK_F1},
794 {"f10", SDLK_F10},
795 {"f11", SDLK_F11},
796 {"f12", SDLK_F12},
797 {"f2", SDLK_F2},
798 {"f3", SDLK_F3},
799 {"f4", SDLK_F4},
800 {"f5", SDLK_F5},
801 {"f6", SDLK_F6},
802 {"f7", SDLK_F7},
803 {"f8", SDLK_F8},
804 {"f9", SDLK_F9}
806 size_t index;
808 if(!name) return ACCELERATOR_ERROR;
810 if(*name && !name[1]) {
811 /* ... */
814 index = string_to_index(data, sizeof(data) / sizeof(*data), name);
816 if(index == (size_t)-1) {
817 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
818 "Unknown key name \"%s\"", name);
819 return ACCELERATOR_ERROR;
822 return index;
825 /*! Creates a widget based on the information in the resource tree node
826 \a data.
828 Also sets the current theme to the first theme that is found in the
829 resource tree.
831 \param xuni The xuni_t structure, to pass on to other functions and set
832 the current theme in and so on.
833 \param base The widget to add the new widget to as a compose widget. If
834 \a base is NULL, it means that the root widget is being created, in
835 which case \a base is set to point to the new widget.
836 \param data The resource tree node representing a {widget}, from which to
837 create a widget.
839 static void add_resource_widget(struct xuni_t *xuni, struct widget_t **base,
840 struct resource_data_t *data) {
842 struct widget_t *widget;
843 struct resource_list_t *list;
844 enum widget_type_t type;
846 list = data->data.tag->list;
848 #if 0
849 widget = init_resource_widget_ref(xuni, list);
850 if(widget) {
851 type = widget->type;
853 else {
854 #endif
855 type = get_widget_type(xuni, find_resource_text(list, "type"));
856 if(type == WIDGET_NONE) return;
858 if(!*base) {
859 *base = allocate_widget(find_resource_text(list, "name"), 0);
860 widget = *base;
862 else {
863 if(!(*base)->compose) (*base)->compose = allocate_panel(*base);
865 add_allocate_widget(*base, find_resource_text(list, "name"));
867 widget = last_compose_widget(*base);
870 init_widget_pos(widget,
871 get_expression_value(find_resource_text(list, "xpos"), 0),
872 get_expression_value(find_resource_text(list, "ypos"), 0),
873 get_expression_value(find_resource_text(list, "width"), 1),
874 get_expression_value(find_resource_text(list, "height"), 1),
875 get_pos_pack(find_resource_text(list, "pack")));
877 if(!get_expression_value(find_resource_text(list, "enabled"), 1)) {
878 widget->visibility &= ~WIDGET_VISIBILITY_CLICKABLE;
880 if(!get_expression_value(find_resource_text(list, "selable"), 1)) {
881 widget->visibility &= ~WIDGET_VISIBILITY_SELABLE;
883 if(!get_expression_value(find_resource_text(list, "visible"), 1)) {
884 widget->visibility &= ~WIDGET_VISIBILITY_VISIBLE;
887 process_widget_accelerators(xuni, *base, widget, list);
889 init_resource_widget(xuni, widget, type, list);
891 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
893 if(widget->type == WIDGET_THEME) {
894 if(!xuni->theme->current) {
895 xuni->theme->current = widget;
898 /* recurse (indirectly) */
899 load_resource_widgets(xuni, &widget, data);
901 set_theme_nameid(widget);
902 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
904 else if(widget->type == WIDGET_PANEL) {
905 /* recurse (indirectly) */
906 load_resource_widgets(xuni, &widget, data);
910 #if 0
911 /* This code returns a widget based on a list of names to dereference. It
912 works, but using WIDs is much more efficient, and this is not used at the
913 moment.
915 static int compare_widget_name_nonrec(struct widget_t *widget, va_list arg) {
916 const char *match = va_arg(arg, const char *);
918 if(!widget || !match) return !match;
920 return strcmp(match, widget->name) == 0
921 && compare_widget_name_nonrec(widget->base, arg);
924 static int compare_widget_name_nonrec_reverse(struct widget_t **widget,
925 va_list arg) {
927 const char *match = va_arg(arg, const char *);
929 if(!*widget || !match) return !match;
931 if(!compare_widget_name_nonrec_reverse(widget, arg)
932 || !(*widget)->name) {
934 return 0;
937 if(strcmp(match, (*widget)->name) == 0) {
938 *widget = (*widget)->base;
939 return 1;
942 return 0;
945 int compare_widget_name(struct widget_t *widget, ...) {
946 int r;
948 va_list arg;
949 va_start(arg, widget);
951 r = compare_widget_name_nonrec_reverse(&widget, arg);
953 va_end(arg);
955 return r;
957 #endif
959 /*! Allocates enough space for a struct widget_nameid_t.
960 \return A pointer to the newly allocated nameid structure.
962 struct nameid_t *allocate_nameid(void) {
963 struct nameid_t *nameid = xuni_memory_allocate(sizeof(*nameid));
965 nameid->widget = 0;
966 nameid->widgets = 0;
968 return nameid;
971 /*! Adds the widget \a widget to \a nameid.
973 \param nameid The array of nameids to add the new wid to.
974 \param widget The widget to add to \a nameid.
975 \param id The wid to use for this widget.
976 \param name The path to the widget \a widget relative to the panel widget
977 that \a nameid is a part of.
979 void init_wid_widget(struct nameid_t *nameid, struct widget_t *widget,
980 size_t id, const char *name) {
982 size_t x;
984 if(nameid->widgets <= id) {
985 nameid->widget = xuni_memory_resize(nameid->widget,
986 (id + 1) * sizeof(*nameid->widget));
988 /* clear any new nameids allocated between nameid->widgets and id */
989 for(x = nameid->widgets; x <= id; x ++) {
990 nameid->widget[x].id = 0;
991 nameid->widget[x].name = 0;
992 nameid->widget[x].widget = 0;
995 nameid->widgets = id + 1;
998 if(nameid->widget[id].id || nameid->widget[id].name
999 || nameid->widget[id].widget) {
1001 log_message(ERROR_TYPE_WARNING, 0, __FILE__, __LINE__,
1002 "Duplicate wid: old: \"%s\" [%lu], new: \"%s\" [%lu]",
1003 nameid->widget[id].name, (unsigned long)nameid->widget[id].id,
1004 name, (unsigned long)id);
1006 else {
1007 nameid->widget[id].id = id;
1008 if(widget) widget->id = id;
1009 nameid->widget[id].name = name;
1010 nameid->widget[id].widget = widget;
1014 /*! Just like init_wid(), but allows the widget referred to be \a name to not
1015 exist. (init_wid() would display an error in that case.)
1017 Mainly useful for widgets that do not have to be loaded. Prime examples
1018 of this are the various widget images of themes, like the checkbox image.
1020 \param base The base of the widget, from which to use find_widget() to
1021 find the widget represented by the widget path \a name.
1022 \param nameid The nameid array to add the new nameid to.
1023 \param id The wid of the new widget -- usually a client-defined enum
1024 value.
1025 \param name The path to the widget, starting from \a base.
1027 void init_wid_possible_zero(struct widget_t *base, struct nameid_t *nameid,
1028 size_t id, const char *name) {
1030 init_wid_widget(nameid, find_widget(base, name), id, name);
1033 /*! Adds a widget to the nameid array of \a use. The widget is located using
1034 the name in \a name with the base \a base.
1036 \param base The base of the widget, from which to use find_widget() to
1037 find the widget represented by the widget path \a name.
1038 \param use The panel widget to add the new nameid to.
1039 \param id The wid of the new widget -- usually a client-defined enum
1040 value.
1041 \param name The path to the widget, starting from \a base.
1043 void init_wid(struct widget_t *base, struct widget_t *use, size_t id,
1044 const char *name) {
1046 struct widget_t *widget = find_widget(base, name);
1048 if(!use->p.panel->nameid) {
1049 use->p.panel->nameid = allocate_nameid();
1052 if(widget) {
1053 init_wid_widget(use->p.panel->nameid, widget, id, name);
1055 else {
1056 log_message(ERROR_TYPE_RESOURCE, 0, __FILE__, __LINE__,
1057 "Could not find widget \"%s\" [wid %lu]", name,
1058 (unsigned long)id);
1062 void init_wid_array(struct widget_t *base, struct widget_t *use,
1063 struct wid_init_t *init, size_t n) {
1065 size_t x;
1067 for(x = 0; x < n; x ++) {
1068 init_wid(base, use, init[x].id, init[x].name);
1072 static size_t move_cursor_word(struct widget_edit_t *edit, size_t startpos,
1073 int dir, int flip) {
1075 int space;
1076 size_t x, pos = startpos;
1078 while((dir < 0) ? pos : (pos < edit->len)) {
1079 x = pos;
1080 if(dir < 0) x --;
1082 space = (isspace(edit->data->text[x]) ? 1 : 0);
1083 if(space != flip) break;
1085 pos += dir;
1088 return pos;
1091 int widget_process_character(struct xuni_t *xuni, SDL_keysym *sym) {
1092 struct widget_t *widget = xuni->gui->active.widget;
1094 if(!widget) return 0;
1096 /*printf("widget_process_character(): widget=%p, key=%i [%c]\n",
1097 (void *)widget, sym->unicode,
1098 isprint(sym->unicode) ? sym->unicode : '-');*/
1100 /* !!! */
1101 if(widget->type == WIDGET_TEXTBOX) {
1102 widget = widget->compose->widget[WID_TEXTBOX_LABEL];
1105 if(widget->type == WIDGET_LABEL) {
1106 if(isprint(sym->unicode)) {
1107 edit_add_char(&xuni->gui->edit, sym->unicode);
1109 else if(sym->unicode == '\b' && xuni->gui->edit.pos) {
1110 if(sym->mod & KMOD_CTRL) {
1111 size_t end = xuni->gui->edit.pos;
1113 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1114 xuni->gui->edit.pos, -1, 1);
1115 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1116 xuni->gui->edit.pos, -1, 0);
1118 edit_del_chars(&xuni->gui->edit, end - xuni->gui->edit.pos);
1120 else {
1121 xuni->gui->edit.pos --;
1122 edit_del_chars(&xuni->gui->edit, 1);
1125 else if(sym->unicode == '\r') {
1126 clear_active(xuni, &xuni->gui->active, 1);
1128 else if(sym->sym == SDLK_DELETE
1129 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1131 if(sym->mod & KMOD_CTRL) {
1132 size_t end;
1134 end = move_cursor_word(&xuni->gui->edit,
1135 xuni->gui->edit.pos, 1, 0);
1136 end = move_cursor_word(&xuni->gui->edit, end, 1, 1);
1138 edit_del_chars(&xuni->gui->edit, end - xuni->gui->edit.pos);
1140 else {
1141 edit_del_chars(&xuni->gui->edit, 1);
1144 else if(sym->sym == SDLK_LEFT && xuni->gui->edit.pos) {
1145 if(sym->mod & KMOD_CTRL) {
1146 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1147 xuni->gui->edit.pos, -1, 1);
1148 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1149 xuni->gui->edit.pos, -1, 0);
1151 else {
1152 xuni->gui->edit.pos --;
1155 else if(sym->sym == SDLK_RIGHT
1156 && xuni->gui->edit.pos < xuni->gui->edit.len) {
1158 if(sym->mod & KMOD_CTRL) {
1159 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1160 xuni->gui->edit.pos, 1, 0);
1161 xuni->gui->edit.pos = move_cursor_word(&xuni->gui->edit,
1162 xuni->gui->edit.pos, 1, 1);
1164 else {
1165 xuni->gui->edit.pos ++;
1168 else if(sym->sym == SDLK_END) {
1169 xuni->gui->edit.pos = xuni->gui->edit.len;
1171 else if(sym->sym == SDLK_HOME) {
1172 xuni->gui->edit.pos = 0;
1174 else {
1175 return 0;
1178 if(xuni->gui->edit.data) {
1179 reposition_label_data(xuni, xuni->gui->edit.data,
1180 &xuni->gui->edit, xuni->gui->edit.pos);
1183 return 1;
1186 /*printf("data: \"%s\"\n", xuni->gui->edit.data);*/
1188 return 0;
1191 /*! Returns true if the widget \a widget can be activated. This should depend
1192 only on the type of \a widget -- for example, textboxes can be activated
1193 (after which they trap keyboard input), but buttons cannot.
1195 \param widget The widget to examine the type of.
1196 \return True if the widget \a widget can be activated.
1198 int widget_can_be_active(struct widget_t *widget) {
1199 if(!widget) return 0;
1201 if(widget->type == WIDGET_TEXTBOX) return 1;
1203 return 0;
1206 void update_widget(struct xuni_t *xuni, struct widget_t *widget) {
1207 if(!widget) return;
1209 if(widget->type == WIDGET_TEXTBOX) {
1210 /*gui->sel.p.widget->base->compose
1211 ->widget[WID_TEXTBOX_LABEL]->p.label->text = gui->edit.data;*/
1213 widget_event(xuni, widget, WIDGET_EVENT_RESCALE);
1217 void enable_unicode(const struct widget_t *widget) {
1218 int v = 0;
1220 if(!widget) return;
1222 if(widget->type == WIDGET_TEXTBOX) {
1223 v = 1;
1226 if(SDL_EnableUNICODE(-1) != v) SDL_EnableUNICODE(v);
1229 void activate_widget(struct widget_t *widget, struct xuni_t *xuni,
1230 int xp, int yp) {
1232 struct widget_edit_t *edit = &xuni->gui->edit;
1233 struct widget_t *w;
1234 int x;
1236 if(!widget) return;
1238 if(widget->type == WIDGET_TEXTBOX) {
1239 struct label_t *label = widget->compose->widget[WID_TEXTBOX_LABEL]
1240 ->p.label;
1241 size_t len = label->text ? strlen(label->text) : 0;
1243 if(edit->data) deactivate_widget(xuni->gui->active.widget, xuni);
1245 edit->data = label;
1246 edit->datawidget = widget->compose->widget[WID_TEXTBOX_LABEL];
1248 edit->prev = xuni_memory_allocate(sizeof(*edit->prev));
1249 *edit->prev = *label;
1251 edit->data->text = xuni_memory_duplicate_string_len(label->text, len);
1253 edit->len = edit->alloc = (label->text ? len : 0);
1255 if(label->text) {
1256 w = widget->compose->widget[WID_TEXTBOX_LABEL];
1258 x = xp - w->pos->real.x;
1259 if(w->pos->clip) x += w->pos->clip->xclip;
1261 edit->pos = width_to_label_pos(xuni, x,
1262 get_theme_widget(xuni, label->font),
1263 (char *)label->text); /* !!! const */
1265 else {
1266 edit->pos = 0;
1269 /* Reposition the textbox so that the cursor is inside the visible
1270 portion of the textbox. If a half-invisible character is selected,
1271 for example, this will scroll the textbox slightly so that the
1272 cursor is visible.
1274 This would be unnecessary if textbox selection was based on the
1275 label instead of the box of the textbox.
1277 widget_event(xuni, widget, WIDGET_EVENT_REPOSITION);
1281 void revert_widget(struct xuni_t *xuni, struct widget_t *widget) {
1282 if(!widget) return;
1284 if(widget->type == WIDGET_TEXTBOX) {
1285 struct widget_t *label = widget->compose->widget[WID_TEXTBOX_LABEL];
1287 /*printf("revert_widget(): edit: %p \"%s\"\n", label->p.label->text,
1288 label->p.label->text);
1289 printf("revert_widget(): prev: %p \"%s\"\n",
1290 xuni->gui->edit.prev->text, xuni->gui->edit.prev->text);*/
1292 xuni_memory_free((char *)label->p.label->text); /* !!! const */
1293 /*SDL_FreeSurface(label->p.label->label);*/
1295 if(xuni->gui->edit.prev) {
1296 label->p.label->text = xuni->gui->edit.prev->text;
1298 xuni_memory_free(xuni->gui->edit.prev);
1300 else label->p.label->text = xuni_memory_duplicate_string_len("", 0);
1302 widget_event(xuni, label, WIDGET_EVENT_RESCALE);
1304 init_edit(&xuni->gui->edit);
1308 void deactivate_widget(struct widget_t *widget, struct xuni_t *xuni) {
1309 if(!widget) return;
1311 if(widget->type == WIDGET_TEXTBOX) {
1312 /* makes comboboxes have a history of what was typed in them */
1313 /*if(widget->base
1314 && widget->base->type == WIDGET_COMBOBOX) {
1316 struct widget_t *label = widget->compose
1317 ->widget[WID_TEXTBOX_LABEL];
1319 add_combobox_item(xuni, widget->base, label->p.label->font,
1320 label->p.label->text);
1321 widget_event(xuni, widget->base->compose->widget
1322 [WID_COMBOBOX_DROPDOWN], WIDGET_EVENT_RESCALE);
1325 call_deactivate_func(xuni, widget);
1327 xuni_memory_free((char *)xuni->gui->edit.prev->text); /* !!! const */
1328 /*free_surface(xuni->gui->edit.prev->label);*/
1329 xuni_memory_free(xuni->gui->edit.prev);
1331 init_edit(&xuni->gui->edit);
1335 void perform_widget_click(struct xuni_t *xuni, struct widget_t *widget) {
1336 if(!widget) return;
1338 if(widget->base) {
1339 if(widget->type == WIDGET_CHECKBOX) {
1340 toggle_checkbox(widget);
1342 else if(widget->base->type == WIDGET_SCROLLBAR) {
1343 if(widget->base->compose->widget[WID_SCROLLBAR_UP]
1344 == widget) {
1346 move_scrollbar(xuni, widget->base, -10);
1348 else if(widget->base->compose->widget
1349 [WID_SCROLLBAR_DOWN] == widget) {
1351 move_scrollbar(xuni, widget->base, 10);
1354 /*reposition_widget(xuni, widget->base);*/
1355 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
1356 WIDGET_EVENT_REPOSITION);
1358 else if(widget->base->type == WIDGET_COMBOBOX) {
1359 if(widget->base->compose->widget[WID_COMBOBOX_BUTTON] == widget) {
1360 widget->base->compose->widget[WID_COMBOBOX_DROPDOWN]
1361 ->visibility ^= WIDGET_VISIBILITY_VISIBLE;
1367 /*! Frees the memory allocated for the gui_t structure (xuni_t::gui).
1368 \param xuni A pointer to the main xuni structure. Only the gui member of
1369 this structure is used.
1371 void free_gui(struct xuni_t *xuni) {
1372 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_FREE);