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 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
,
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
,
66 void init_theme_structure(struct theme_t
*theme
) {
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
) {
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
) {
107 void clear_edit(struct widget_edit_t
*edit
) {
111 xuni_memory_free(edit
->prev
);
115 edit
->datawidget
= 0;
122 void clear_active(struct xuni_t
*xuni
, struct widget_p_t
*wp
, int keep
) {
124 revert_widget(xuni
, wp
->widget
);
126 widget_event(xuni
, wp
->widget
, WIDGET_EVENT_RESCALE
);
129 deactivate_widget(wp
->widget
, xuni
);
134 SDL_EnableUNICODE(0);
137 void clear_tab(struct widget_tab_t
*tab
, panel_type_t 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;
149 = xuni_memory_resize((char *)edit
->data
->text
,
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;
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;
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
189 \return True if a repaint is needed, that is, if any widget had a sel
192 int clear_widget_sel(struct widget_t
*widget
) {
196 if(!widget
) return 0;
198 if(widget
->sel
) repaint
= 1;
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
;
210 struct widget_t
*set_widget_sel_rec(struct widget_sel_t
*sel
, int xp
, int yp
,
211 struct widget_t
*widget
, int *repaint
) {
214 struct widget_t
*w
= 0, *last
= 0;
217 if(widget
&& !strcmp(widget
->name
, "data")) {
218 /*printf("item \"%s\": ", widget->p.label->text);*/
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",
231 widget
->pos
->real
.x
+ widget
->pos
->real
.w
,
232 widget
->pos
->real
.y
+ widget
->pos
->real
.h
);
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
);
245 if(!last
&& widget
->compose
->widget
[x
]->visibility
246 & WIDGET_VISIBILITY_INDEPENDENT
) {
251 if(!set_widget_sel_compose(sel
, xp
, yp
, w
)) break;
256 if(x
!= (size_t)-1) x
--;
258 while(x
!= (size_t)-1) {
259 *repaint
= clear_widget_sel(widget
->compose
->widget
[x
])
266 if(!last
&& widget
->visibility
& WIDGET_VISIBILITY_SELABLE
) {
268 if(!widget
->sel
) *repaint
= 1;
272 if(widget
->sel
) *repaint
= 1;
276 else *repaint
= clear_widget_sel(widget
) || *repaint
;
281 static int set_widget_sel_compose(struct widget_sel_t
*sel
, int xp
, int yp
,
282 struct widget_t
*widget
) {
287 /*if(!(widget->visibility & WIDGET_VISIBILITY_INDEPENDENT)) {
288 widget->base->sel = 1;
291 if(!(widget
->base
->visibility
& WIDGET_VISIBILITY_NOT_COMPOSE
)) {
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
) {
312 if(!widget
) return 0;
314 if(sel
->p
.widget
&& !sel
->clickin
&& !sel
->wasin
) clear_sel(sel
);
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
) {
325 sel
->clickin
= click
;
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
) {
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
)) {
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
) {
357 /* the mouse button was clicked or released inside the widget */
358 if(sel
->clickin
!= click
) {
359 sel
->clickin
= click
;
363 if(v
) sel
->p
.widget
->repaint
= 1;
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
) {
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
;
395 amount
= yp
- sel
->scrollbar
.yp
;
396 max
= bar
->pos
->real
.h
- seek
->pos
->real
.h
;
399 pos
= (double)amount
/ max
* 100.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
);
411 widget_event(xuni
, scroll
->base
, WIDGET_EVENT_REPOSITION
);
421 struct load_resource_widget_t
{
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
,
453 if(!wtype_search_name(xuni
, find
, &pos
)) {
454 log_message(ERROR_TYPE_RESOURCE
, 0, __FILE__
, __LINE__
,
455 "Invalid widget type: \"%s\"", find
);
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
) {
476 unit
= find_resource_text(list
, "unit");
477 angle
= get_expression_value(first_resource_text(list
), 0);
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",
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
;
496 if(!strcmp(name
, "sans")) which
= THEME_FONT_SANS
;
497 else if(!strcmp(name
, "mono")) which
= THEME_FONT_MONO
;
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
) {
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
);
523 xuni_memory_increment(ref
->p
.font
);
524 widget
->p
.font
= ref
->p
.font
;
528 printf("*** Error: <ref> not supported for widgets of type %s\n",
529 get_widget_type_name(xuni
, type
));
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
}
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
;
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
}
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
;
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;
604 init_box(widget
, BOX_STYLE_NORMAL
, 0);
607 init_button(widget
, new_sub_label(widget
,
608 find_font_name(list
),
609 find_resource_text(list
, "text")));
611 case WIDGET_CHECKBOX
:
612 init_checkbox(widget
, xuni
,
613 find_font_name(list
),
614 find_resource_text(list
, "text"), 0);
616 case WIDGET_COMBOBOX
:
617 init_combobox(xuni
, widget
, find_font_name(list
));
620 init_font(widget
, find_resource_text(list
, "path"));
623 init_image(widget
, find_resource_text(list
, "path"),
624 get_angle(first_resource_tag(list
, "angle")),
625 IMAGE_LOAD_FREE
, IMAGE_RESCALE_LAZY
);
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));
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
,
638 (Uint8
)get_expression_value(find_resource_text(list
,
640 (Uint8
)get_expression_value(find_resource_text(list
,
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")));
649 init_panel_from_resource(xuni
, widget
, list
);
650 /*init_panel(widget);*/
652 case WIDGET_TEXTAREA
:
653 init_textarea(widget
, xuni
);
656 init_textbox(widget
, xuni
, find_resource_text(list
, "text"),
657 find_font_name(list
));
661 get_expression_value(find_resource_text(list
, "box-width"),
663 get_expression_value(find_resource_text(list
, "box-height"),
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
));
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
}
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 */
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
) {
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")) {
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
));
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
) {
726 SDLKey key
= (size_t)-1;
727 SDLMod mod
= KMOD_NONE
, m
= KMOD_NONE
;
732 for(end
= data
; isalnum(*end
); end
++);
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
);
747 log_message(ERROR_TYPE_RESOURCE
, 0, __FILE__
, __LINE__
,
748 "Multiple key specified: \"%s\"\n", data
);
755 for(data
= end
; isspace(*data
); 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
[] = {
765 {"control", KMOD_CTRL
},
768 {"lcontrol", KMOD_LCTRL
},
769 {"lctrl", KMOD_LCTRL
},
770 {"lshift", KMOD_LSHIFT
},
772 {"nothing", KMOD_NONE
},
774 {"rcontrol", KMOD_RCTRL
},
775 {"rctrl", KMOD_RCTRL
},
776 {"rshift", KMOD_RSHIFT
},
777 {"shift", KMOD_SHIFT
}
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);*/
791 static SDLKey
get_key_identifier_from_string(const char *name
) {
792 struct string_index_t data
[] = {
808 if(!name
) return ACCELERATOR_ERROR
;
810 if(*name
&& !name
[1]) {
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
;
825 /*! Creates a widget based on the information in the resource tree node
828 Also sets the current theme to the first theme that is found in the
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
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
;
849 widget
= init_resource_widget_ref(xuni
, list
);
855 type
= get_widget_type(xuni
, find_resource_text(list
, "type"));
856 if(type
== WIDGET_NONE
) return;
859 *base
= allocate_widget(find_resource_text(list
, "name"), 0);
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
);
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
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
,
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
) {
937 if(strcmp(match
, (*widget
)->name
) == 0) {
938 *widget
= (*widget
)->base
;
945 int compare_widget_name(struct widget_t
*widget
, ...) {
949 va_start(arg
, widget
);
951 r
= compare_widget_name_nonrec_reverse(&widget
, arg
);
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
));
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
) {
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
);
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
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
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
,
1046 struct widget_t
*widget
= find_widget(base
, name
);
1048 if(!use
->p
.panel
->nameid
) {
1049 use
->p
.panel
->nameid
= allocate_nameid();
1053 init_wid_widget(use
->p
.panel
->nameid
, widget
, id
, name
);
1056 log_message(ERROR_TYPE_RESOURCE
, 0, __FILE__
, __LINE__
,
1057 "Could not find widget \"%s\" [wid %lu]", name
,
1062 void init_wid_array(struct widget_t
*base
, struct widget_t
*use
,
1063 struct wid_init_t
*init
, size_t n
) {
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
) {
1076 size_t x
, pos
= startpos
;
1078 while((dir
< 0) ? pos
: (pos
< edit
->len
)) {
1082 space
= (isspace(edit
->data
->text
[x
]) ? 1 : 0);
1083 if(space
!= flip
) break;
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 : '-');*/
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
);
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
) {
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
);
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);
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);
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;
1178 if(xuni
->gui
->edit
.data
) {
1179 reposition_label_data(xuni
, xuni
->gui
->edit
.data
,
1180 &xuni
->gui
->edit
, xuni
->gui
->edit
.pos
);
1186 /*printf("data: \"%s\"\n", xuni->gui->edit.data);*/
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;
1206 void update_widget(struct xuni_t
*xuni
, struct widget_t
*widget
) {
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
) {
1222 if(widget
->type
== WIDGET_TEXTBOX
) {
1226 if(SDL_EnableUNICODE(-1) != v
) SDL_EnableUNICODE(v
);
1229 void activate_widget(struct widget_t
*widget
, struct xuni_t
*xuni
,
1232 struct widget_edit_t
*edit
= &xuni
->gui
->edit
;
1238 if(widget
->type
== WIDGET_TEXTBOX
) {
1239 struct label_t
*label
= widget
->compose
->widget
[WID_TEXTBOX_LABEL
]
1241 size_t len
= label
->text
? strlen(label
->text
) : 0;
1243 if(edit
->data
) deactivate_widget(xuni
->gui
->active
.widget
, xuni
);
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);
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 */
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
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
) {
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
) {
1311 if(widget
->type
== WIDGET_TEXTBOX
) {
1312 /* makes comboboxes have a history of what was typed in them */
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
) {
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
]
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
);