6 #include <string.h> /* for memmove(), strcmp(), strlen(), etc. */
9 #include "SDL_gfxPrimitives.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
) {
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
) {
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
) {
100 void clear_edit(struct widget_edit_t
*edit
) {
104 xuni_memory_free(edit
->prev
);
108 edit
->datawidget
= 0;
115 void clear_active(struct xuni_t
*xuni
, struct widget_p_t
*wp
, int keep
) {
117 revert_widget(xuni
, wp
->widget
);
119 widget_event(xuni
, wp
->widget
, WIDGET_EVENT_RESCALE
);
122 deactivate_widget(wp
->widget
, xuni
);
127 SDL_EnableUNICODE(0);
130 void clear_tab(struct widget_tab_t
*tab
, panel_type_t 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;
142 = xuni_memory_resize((char *)edit
->data
->text
,
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;
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;
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
182 \return True if a repaint is needed, that is, if any widget had a sel
185 int clear_widget_sel(struct widget_t
*widget
) {
189 if(!widget
) return 0;
191 if(widget
->sel
) repaint
= 1;
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
;
203 struct widget_t
*set_widget_sel_rec(struct widget_sel_t
*sel
, int xp
, int yp
,
204 struct widget_t
*widget
, int *repaint
) {
207 struct widget_t
*w
= 0, *last
= 0;
210 if(widget
&& !strcmp(widget
->name
, "data")) {
211 /*printf("item \"%s\": ", widget->p.label->text);*/
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",
224 widget
->pos
->real
.x
+ widget
->pos
->real
.w
,
225 widget
->pos
->real
.y
+ widget
->pos
->real
.h
);
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
);
238 if(!last
&& widget
->compose
->widget
[x
]->visibility
239 & WIDGET_VISIBILITY_INDEPENDENT
) {
244 if(!set_widget_sel_compose(sel
, xp
, yp
, w
)) break;
249 if(x
!= (size_t)-1) x
--;
251 while(x
!= (size_t)-1) {
252 *repaint
= clear_widget_sel(widget
->compose
->widget
[x
])
259 if(!last
&& widget
->visibility
& WIDGET_VISIBILITY_SELABLE
) {
261 if(!widget
->sel
) *repaint
= 1;
265 if(widget
->sel
) *repaint
= 1;
269 else *repaint
= clear_widget_sel(widget
) || *repaint
;
274 static int set_widget_sel_compose(struct widget_sel_t
*sel
, int xp
, int yp
,
275 struct widget_t
*widget
) {
280 /*if(!(widget->visibility & WIDGET_VISIBILITY_INDEPENDENT)) {
281 widget->base->sel = 1;
284 if(!(widget
->base
->visibility
& WIDGET_VISIBILITY_NOT_COMPOSE
)) {
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
) {
305 if(!widget
) return 0;
307 if(sel
->p
.widget
&& !sel
->clickin
&& !sel
->wasin
) clear_sel(sel
);
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
) {
318 sel
->clickin
= click
;
327 int set_widget_repaint(struct widget_sel_t
*sel
, int xp
, int yp
, int click
,
328 struct widget_t
*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
) {
345 /* the mouse button was clicked or released inside the widget */
346 if(sel
->clickin
!= click
) {
347 sel
->clickin
= click
;
351 if(v
) sel
->p
.widget
->repaint
= 1;
357 struct load_resource_widget_t
{
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
[] = {
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
) {
424 unit
= find_resource_text(list
, "unit");
425 angle
= get_expression_value(first_resource_text(list
), 0);
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",
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
;
444 if(!strcmp(name
, "sans")) which
= THEME_FONT_SANS
;
445 else if(!strcmp(name
, "mono")) which
= THEME_FONT_MONO
;
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
) {
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
);
471 xuni_memory_increment(ref
->p
.font
);
472 widget
->p
.font
= ref
->p
.font
;
476 printf("*** Error: <ref> not supported for widgets of type %s\n",
477 get_widget_type_name(xuni
, type
));
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
}
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
;
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
}
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
;
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;
552 init_box(widget
, BOX_STYLE_NORMAL
, 0);
555 init_button(widget
, new_sub_label(widget
,
556 find_font_name(list
),
557 find_resource_text(list
, "text")));
559 case WIDGET_CHECKBOX
:
560 init_checkbox(widget
, xuni
,
561 find_font_name(list
),
562 find_resource_text(list
, "text"), 0);
564 case WIDGET_COMBOBOX
:
565 init_combobox(xuni
, widget
, find_font_name(list
));
568 init_font(widget
, find_resource_text(list
, "path"));
571 init_image(widget
, find_resource_text(list
, "path"),
572 get_angle(first_resource_tag(list
, "angle")),
573 IMAGE_LOAD_FREE
, IMAGE_RESCALE_LAZY
);
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));
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
,
586 (Uint8
)get_expression_value(find_resource_text(list
,
588 (Uint8
)get_expression_value(find_resource_text(list
,
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")));
597 init_panel_from_resource(xuni
, widget
, list
);
598 /*init_panel(widget);*/
600 case WIDGET_TEXTAREA
:
601 init_textarea(widget
, xuni
);
604 init_textbox(widget
, xuni
, find_resource_text(list
, "text"),
605 find_font_name(list
));
609 get_expression_value(find_resource_text(list
, "box-width"),
611 get_expression_value(find_resource_text(list
, "box-height"),
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
));
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
}
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 */
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
) {
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")) {
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
));
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
) {
674 SDLKey key
= (size_t)-1;
675 SDLMod mod
= KMOD_NONE
, m
= KMOD_NONE
;
680 for(end
= data
; isalnum(*end
); end
++);
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
);
695 log_message(ERROR_TYPE_RESOURCE
, 0, __FILE__
, __LINE__
,
696 "Multiple key specified: \"%s\"\n", data
);
703 for(data
= end
; isspace(*data
); 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
[] = {
713 {"control", KMOD_CTRL
},
716 {"lcontrol", KMOD_LCTRL
},
717 {"lctrl", KMOD_LCTRL
},
718 {"lshift", KMOD_LSHIFT
},
720 {"nothing", KMOD_NONE
},
722 {"rcontrol", KMOD_RCTRL
},
723 {"rctrl", KMOD_RCTRL
},
724 {"rshift", KMOD_RSHIFT
},
725 {"shift", KMOD_SHIFT
}
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);*/
739 static SDLKey
get_key_identifier_from_string(const char *name
) {
740 struct string_index_t data
[] = {
756 if(!name
) return ACCELERATOR_ERROR
;
758 if(*name
&& !name
[1]) {
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
;
773 /*! Creates a widget based on the information in the resource tree node
776 Also sets the current theme to the first theme that is found in the
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
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
;
797 widget
= init_resource_widget_ref(xuni
, list
);
803 type
= get_widget_type(find_resource_text(list
, "type"));
804 if(type
== WIDGET_NONE
) return;
807 *base
= allocate_widget(find_resource_text(list
, "name"), 0);
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
);
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
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
,
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
) {
885 if(strcmp(match
, (*widget
)->name
) == 0) {
886 *widget
= (*widget
)->base
;
893 int compare_widget_name(struct widget_t
*widget
, ...) {
897 va_start(arg
, widget
);
899 r
= compare_widget_name_nonrec_reverse(&widget
, arg
);
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
));
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
) {
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
);
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
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
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
,
994 struct widget_t
*widget
= find_widget(base
, name
);
996 if(!use
->p
.panel
->nameid
) {
997 use
->p
.panel
->nameid
= allocate_nameid();
1001 init_wid_widget(use
->p
.panel
->nameid
, widget
, id
, name
);
1004 log_message(ERROR_TYPE_RESOURCE
, 0, __FILE__
, __LINE__
,
1005 "Could not find widget \"%s\" [wid %lu]", name
,
1010 void init_wid_array(struct widget_t
*base
, struct widget_t
*use
,
1011 struct wid_init_t
*init
, size_t n
) {
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 : '-');*/
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;
1068 if(xuni
->gui
->edit
.data
) {
1069 reposition_label_data(xuni
, xuni
->gui
->edit
.data
,
1070 &xuni
->gui
->edit
, xuni
->gui
->edit
.pos
);
1076 /*printf("data: \"%s\"\n", xuni->gui->edit.data);*/
1081 int widget_can_be_active(struct widget_t
*widget
) {
1082 if(!widget
) return 0;
1084 if(widget
->type
== WIDGET_TEXTBOX
) return 1;
1089 void update_widget(struct xuni_t
*xuni
, struct widget_t
*widget
) {
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
) {
1105 if(widget
->type
== WIDGET_TEXTBOX
) {
1109 if(SDL_EnableUNICODE(-1) != v
) SDL_EnableUNICODE(v
);
1112 void activate_widget(struct widget_t
*widget
, struct xuni_t
*xuni
,
1115 struct widget_edit_t
*edit
= &xuni
->gui
->edit
;
1121 if(widget
->type
== WIDGET_TEXTBOX
) {
1122 struct label_t
*label
= widget
->compose
->widget
[WID_TEXTBOX_LABEL
]
1124 size_t len
= label
->text
? strlen(label
->text
) : 0;
1126 if(edit
->data
) deactivate_widget(xuni
->gui
->active
.widget
, xuni
);
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;
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 */
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
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
) {
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
) {
1194 if(widget
->type
== WIDGET_TEXTBOX
) {
1195 /* makes comboboxes have a history of what was typed in them */
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
) {
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
]
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
);