Created common signature for panel event handling functions.
[xuni.git] / src / loop.c
blob63ef6241aa0e8e20bc1266ba141b7405fedec6ce
1 /*! \file loop.c
3 */
5 #include <stdlib.h>
7 #include "SDL_framerate.h"
9 #include "graphics.h"
10 #include "gui.h"
11 #include "loop.h"
12 #include "memory.h"
13 #include "resource/resource.h"
15 #include "widget/dump.h"
16 #include "widget/widgets.h"
17 #include "widget/listbox.h"
19 static int call_perform_click_func(struct xuni_t *xuni,
20 struct panel_data_t *panel, struct widget_t *widget, panel_type_t *mode);
21 static void call_free_funcs(struct widget_t *widget, struct xuni_t *xuni);
23 static SDL_Cursor *sdl_cursor_from_xpm(const char *image[]);
24 static void set_widget_cursor(struct xuni_t *xuni);
26 static void set_loop_caption(panel_type_t mode, struct xuni_t *xuni);
27 static void setup_theme_cursor(struct xuni_t *xuni, struct widget_t *panel);
28 static int get_event(int frameupdate, SDL_Event *event);
29 static int process_event(SDL_Event *event, panel_type_t *mode,
30 struct xuni_t *xuni);
31 static void scroll_listbox(struct xuni_t *xuni, int dir);
32 static int set_loop_widget_sel(panel_type_t mode, int xp, int yp,
33 int click, struct xuni_t *xuni);
34 static int find_widget_accelerator(struct xuni_accelerator_t *accel,
35 SDL_keysym *key, struct xuni_t *xuni, panel_type_t *mode);
36 static int check_sdl_key_mod(SDLMod mod, SDLMod now);
37 static int event_mouse_button_up(int xp, int yp, panel_type_t *mode,
38 struct xuni_t *xuni);
39 static int perform_loop_click(struct xuni_t *xuni, panel_type_t *mode,
40 struct widget_t *widget);
41 static int event_key_down(struct xuni_t *xuni, SDL_keysym *keysym,
42 panel_type_t *mode, int *handled);
44 /*! Calls the registered initialization functions for \a widget and all of its
45 sub-widgets. Only operates on widgets of type panel.
47 \param xuni A pointer to the main xuni structure.
48 \param widget The widget to call the initialization function for, if it is
49 a panel widget.
50 \param settings A pointer to the resource tree.
52 void call_init_funcs(struct xuni_t *xuni, struct widget_t *widget,
53 struct resource_t *settings) {
55 size_t x;
57 if(!widget) return;
59 if(widget->type == WIDGET_PANEL
60 && widget->p.panel->event[PANEL_EVENT_INIT].handler) {
62 widget->p.panel->event[PANEL_EVENT_INIT].p.init.settings = settings;
63 widget->p.panel->event[PANEL_EVENT_INIT].p.init.panel = widget;
65 (*widget->p.panel->event[PANEL_EVENT_INIT].handler)
66 (xuni, widget->p.panel);
69 if(widget->compose) {
70 for(x = 0; x < widget->compose->widgets; x ++) {
71 call_init_funcs(xuni, widget->compose->widget[x], settings);
76 int panel_event_recursive(struct xuni_t *xuni, struct panel_event_t *data,
77 enum panel_event_type_t type, struct widget_t *widget) {
79 int r = 0;
80 size_t x;
82 if(!widget) return 0;
84 if(widget->type == WIDGET_PANEL && data->handler) {
85 (*data->handler)(xuni, widget->p.panel);
88 if(widget->compose) {
89 for(x = 0; x < widget->compose->widgets; x ++) {
90 r = panel_event_recursive(xuni, data, type,
91 widget->compose->widget[x]) || r;
95 return r;
98 static int call_perform_click_func(struct xuni_t *xuni,
99 struct panel_data_t *panel, struct widget_t *widget, panel_type_t *mode) {
101 perform_widget_click(xuni, widget);
103 /*print_widget_backtrace(xuni->gui->sel.p.widget);*/
105 /* !!! quick fix to disallow clicking on a panel. Without this, for
106 example, clicking on the options graphics panel triggers the
107 fullscreen checkbox, as
108 PANEL_OPTIONS_GRAPHICS == WID_GRAPHICS_FULLSCREEN */
109 if(widget && panel->event[PANEL_EVENT_CLICK].handler
110 && widget_nameid_access(xuni->gui->widget, *mode) != widget) {
112 panel->event[PANEL_EVENT_CLICK].p.click.mode = mode;
113 panel->event[PANEL_EVENT_CLICK].p.click.widget = widget;
115 return (*panel->event[PANEL_EVENT_CLICK].handler)(xuni, panel);
118 return 0;
121 void call_deactivate_func(struct xuni_t *xuni, struct widget_t *widget) {
122 struct widget_t *base = widget;
124 while(base && base->type != WIDGET_PANEL) base = base->base;
126 if(base && base->p.panel->event[PANEL_EVENT_DEACTIVATE].handler) {
127 base->p.panel->event[PANEL_EVENT_DEACTIVATE].p.deactivate.widget
128 = widget;
130 (*base->p.panel->event[PANEL_EVENT_DEACTIVATE].handler)
131 (xuni, base->p.panel);
135 static void call_free_funcs(struct widget_t *widget, struct xuni_t *xuni) {
136 size_t x;
138 if(!widget) return;
140 if(widget->type == WIDGET_PANEL) {
141 if(widget->p.panel->event[PANEL_EVENT_FREE].handler) {
142 (*widget->p.panel->event[PANEL_EVENT_FREE].handler)
143 (xuni, widget->p.panel);
146 /* this is handled by free_widget() */
147 /*xuni_memory_free(widget->p.panel->data);*/
150 if(widget->compose) {
151 for(x = 0; x < widget->compose->widgets; x ++) {
152 call_free_funcs(widget->compose->widget[x], xuni);
157 /*#define HIDE_COMBOBOX_POPUPS*/
159 int default_panel_sel(struct xuni_t *xuni, struct panel_data_t *data) {
160 struct panel_event_sel_t *sel = &data->event[PANEL_EVENT_SEL].p.sel;
162 return set_default_widget_sel(xuni, sel->mode, sel->xp, sel->yp,
163 sel->click, data->data);
166 /* !!! this function is obsolete */
167 int set_default_widget_sel(struct xuni_t *xuni, panel_type_t mode,
168 int xp, int yp, int click, void *vdata) {
170 #ifdef HIDE_COMBOBOX_POPUPS
171 struct widget_t *widget = xuni->gui->sel.p.widget;
172 #endif
173 int r;
175 r = set_widget_sel_repaint(&xuni->gui->sel, xp, yp, click,
176 widget_nameid_access(xuni->gui->widget, mode));
178 /*if(xuni->gui->sel.p.widget) {
179 printf("%s\n", xuni->gui->sel.p.widget->name);
182 #ifdef HIDE_COMBOBOX_POPUPS
183 /* Hide combobox popups that have become unselected. */
184 if(widget && widget != xuni->gui->sel.p.widget) {
185 struct widget_t *w = xuni->gui->sel.p.widget, *t;
187 /* See if any comboboxes that were previously selected still are. */
188 while(w) {
189 if(w->visibility & WIDGET_VISIBILITY_VISIBLE
190 && w->type == WIDGET_COMBOBOX) {
192 for(t = widget; t; t = t->base) {
193 if(t == w) break;
196 if(t) break;
199 w = w->base;
202 /* If no comboboxes were selected and remain selected, hide the popups
203 for all comboboxes that were selected.
205 if(!w) {
206 do {
207 if(widget->type == WIDGET_COMBOBOX) {
208 widget->compose->widget[WID_COMBOBOX_DROPDOWN]->visibility
209 &= ~WIDGET_VISIBILITY_VISIBLE;
212 widget = widget->base;
213 } while(widget);
216 #endif
218 return r;
221 void set_panel_callbacks(struct widget_t *widget, void *vdata,
222 int frameupdate,
223 panel_event_func_t init_func,
224 panel_event_func_t start_func,
225 panel_event_func_t event_func,
226 panel_event_func_t set_widget_sel_func,
227 panel_event_func_t perform_click_func,
228 panel_event_func_t deactivate_func,
229 panel_event_func_t paint_func,
230 panel_event_func_t free_func) {
232 widget->p.panel->data = vdata;
233 widget->p.panel->frameupdate = frameupdate;
234 /*widget->p.panel->init_func = init_func;
235 widget->p.panel->start_func = start_func;
236 widget->p.panel->event_func = event_func;
237 widget->p.panel->set_widget_sel_func = set_widget_sel_func;
238 widget->p.panel->perform_click_func = perform_click_func;
239 widget->p.panel->deactivate_func = deactivate_func;
240 widget->p.panel->paint_func = paint_func;
241 widget->p.panel->free_func = free_func;*/
242 widget->p.panel->event[PANEL_EVENT_INIT].handler = init_func;
243 widget->p.panel->event[PANEL_EVENT_START].handler = start_func;
244 widget->p.panel->event[PANEL_EVENT_EVENT].handler = event_func;
245 widget->p.panel->event[PANEL_EVENT_SEL].handler = set_widget_sel_func;
246 widget->p.panel->event[PANEL_EVENT_CLICK].handler = perform_click_func;
247 widget->p.panel->event[PANEL_EVENT_DEACTIVATE].handler = deactivate_func;
248 widget->p.panel->event[PANEL_EVENT_PAINT].handler = paint_func;
249 widget->p.panel->event[PANEL_EVENT_FREE].handler = free_func;
251 widget->p.panel->nameid = 0;
253 widget->p.panel->accel = 0;
256 void execute_callback(struct xuni_t *xuni, struct xuni_callback_t *callback) {
257 if(callback && callback->func) {
258 (*callback->func)(callback->vdata, xuni);
262 /*! The main xuni loop. Indirectly calls all panel event-handling functions,
263 including initialization functions, event handlers, repaint functions,
264 etc.
266 This function should only be called once, after everything has been loaded
267 and set up. It should only return when the program is exiting -- but it
268 will return, so don't neglect that freeing code.
270 \param xuni The xuni structure, with most of the data required to display
271 and manage a Graphical User Interface (GUI, of course).
272 \param always A callback function that is executed for every loop of the
273 xuni event loop. Note that this can be called considerably more
274 frequently than repaint functions.
275 \param mode The panel to initially display.
277 void main_loop(struct xuni_t *xuni, struct xuni_callback_t *always,
278 panel_type_t mode) {
280 panel_type_t pmode = (size_t)-1;
281 struct panel_data_t *panel
282 = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
283 FPSmanager fpsm;
284 SDL_Event event;
285 int repaint = 1;
287 /*call_init_funcs(data, smode, font, theme, gui, settings);*/
289 SDL_initFramerate(&fpsm);
290 SDL_setFramerate(&fpsm, 30);
292 while(mode != (size_t)-1) {
293 execute_callback(xuni, always);
295 /*dump_widgets_need_repaint(xuni->gui->widget);*/
297 if(get_event(widget_nameid_access(xuni->gui->widget, mode)
298 ->p.panel->frameupdate, &event)) {
300 do {
301 repaint = process_event(&event, &mode, xuni) || repaint;
303 if(mode == (size_t)-1) break;
304 } while(SDL_PollEvent(&event));
306 if(mode == (size_t)-1) break;
309 if(pmode != mode) { /* !!! process_event() is called before the start_func */
310 panel = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
311 set_loop_caption(mode, xuni); /* !!! also pass pmode */
313 pmode = mode;
315 repaint = 1;
318 /* !!! this needs to go in a hover callback function of sorts */
319 set_widget_cursor(xuni);
321 if(xuni->smode->focus) {
322 if(repaint || panel->frameupdate) {
323 if(panel->event[PANEL_EVENT_PAINT].handler) {
324 panel->event[PANEL_EVENT_PAINT].p.paint.mode = mode;
326 (*panel->event[PANEL_EVENT_PAINT].handler)(xuni, panel);
331 repaint = 0;
333 if(!xuni->smode->focus || panel->frameupdate) {
334 SDL_framerateDelay(&fpsm);
338 set_caption("Quitting . . .");
340 call_free_funcs(xuni->gui->widget, xuni);
343 /* An XPM image of a CURSOR_TEXT, used when the mouse is inside textboxes. */
344 static const char *text_cursor[] = {
345 /* width height num_colors chars_per_pixel */
346 " 16 16 3 1",
347 /* colors */
348 "X c #000000",
349 ". c #ffffff",
350 " c None",
351 /* pixels */
352 " XXXXX ",
353 " X.....X ",
354 " XX.XX ",
355 " X.X ",
356 " X.X ",
357 " X.X ",
358 " X.X ",
359 " X.X ",
360 " X.X ",
361 " X.X ",
362 " X.X ",
363 " X.X ",
364 " X.X ",
365 " XX.XX ",
366 " X.....X ",
367 " XXXXX ",
368 "7,7"
371 static SDL_Cursor *sdl_cursor_from_xpm(const char *image[]) {
372 int i, x, y;
373 Uint8 data[4*16] = {0};
374 Uint8 mask[4*16] = {0};
375 int hot_x, hot_y;
377 i = -1;
378 for(x = 4; x < 16 + 4; x ++) {
379 for(y = 0; y < 16; y ++) {
380 if(y % 8) {
381 data[i] <<= 1;
382 mask[i] <<= 1;
384 else i ++;
386 switch (image[x][y]) {
387 case 'X':
388 data[i] |= 0x01;
389 mask[i] |= 0x01;
390 break;
391 case '.':
392 mask[i] |= 0x01;
393 break;
394 case ' ':
395 break;
400 sscanf(image[x], "%d,%d", &hot_x, &hot_y);
402 return SDL_CreateCursor(data, mask, 16, 16, hot_x, hot_y);
405 static void set_widget_cursor(struct xuni_t *xuni) {
406 enum cursor_t now;
408 if(xuni->gui->sel.p.widget
409 && xuni->gui->sel.p.widget->type == WIDGET_TEXTBOX) {
411 now = CURSOR_TEXT;
413 else now = CURSOR_NORMAL;
415 if(xuni->theme->cursors.which != now) {
416 xuni->theme->cursors.which = now;
418 if(xuni->theme->cursors.which) {
419 if(!xuni->theme->cursors.text) {
420 xuni->theme->cursors.text = sdl_cursor_from_xpm(text_cursor);
421 /* save the normal cursor before it is overwritten */
422 xuni->theme->cursors.normal = SDL_GetCursor();
425 SDL_SetCursor(xuni->theme->cursors.text);
427 else {
428 if(!xuni->theme->cursors.normal) {
429 xuni->theme->cursors.normal = SDL_GetCursor();
432 SDL_SetCursor(xuni->theme->cursors.normal);
437 static void set_loop_caption(panel_type_t mode, struct xuni_t *xuni) {
438 struct widget_t *panel = widget_nameid_access(xuni->gui->widget, mode);
439 struct panel_data_t *paneldata = panel->p.panel;
440 int xp, yp;
442 show_cursor(1);
444 clear_gui(xuni, mode, 0);
446 /* !!! set all other panels to invisible */
447 widget_nameid_access(xuni->gui->widget, mode)->visibility
448 |= WIDGET_VISIBILITY_VISIBLE;
450 if(paneldata->event[PANEL_EVENT_START].handler) {
451 (*paneldata->event[PANEL_EVENT_START].handler)(xuni, paneldata);
454 setup_theme_cursor(xuni, widget_nameid_access(xuni->gui->widget, mode));
455 SDL_GetMouseState(&xp, &yp);
456 set_loop_widget_sel(mode, xp, yp, xuni->gui->sel.clickin, xuni);
459 static void setup_theme_cursor(struct xuni_t *xuni, struct widget_t *panel) {
460 struct widget_t *cursor;
462 if(!panel->p.panel->frameupdate) return;
464 cursor = get_theme_widget(xuni, THEME_DEFAULT_CURSOR);
465 if(!cursor) return;
467 prepare_paint_image(xuni, cursor);
469 if(cursor->p.image->image) {
470 show_cursor(0);
474 static int get_event(int frameupdate, SDL_Event *event) {
475 return frameupdate ? SDL_PollEvent(event) : SDL_WaitEvent(event);
478 /* handled should not be used in some cases. For example, a widget might be
479 deselected by a mouse motion event, but a xuni application (like the
480 resource editor) might want to know about the mouse motion event just the
481 same.
483 /*! Handles an event from the SDL. What happens depends on the event itself.
484 However, this function will always either deal with the event itself, call
485 the appropriate event handler function for the currently visible panel, or
486 perhaps do a little bit of both.
488 Some handled events include clicking inside widgets (implemented by
489 calling set_loop_widget_sel()), pressing special keypresses like
490 printscreen (see event_key_down()), and selecting widgets when the mouse
491 moves (set_loop_widget_sel()).
493 \param event The SDL_Event that needs to be dealt with.
494 \param mode The currently visible panel, which could be changed.
495 \param xuni The main xuni_t structure.
496 \return True if the screen needs to be repainted.
498 static int process_event(SDL_Event *event, panel_type_t *mode,
499 struct xuni_t *xuni) {
501 struct panel_data_t *panel
502 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
503 int repaint = 0, handled = 0;
505 switch(event->type) {
506 case SDL_MOUSEBUTTONDOWN:
507 if(event->button.button == SDL_BUTTON_WHEELUP) {
508 scroll_listbox(xuni, -1);
510 repaint = 1;
511 handled = 1;
513 else if(event->button.button == SDL_BUTTON_WHEELDOWN) {
514 scroll_listbox(xuni, 1);
516 repaint = 1;
517 handled = 1;
520 if(event->button.button != 1) break;
522 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
523 1, xuni);
525 if(repaint) handled = 1;
526 break;
527 case SDL_MOUSEBUTTONUP:
528 if(event->button.button != 1) break;
530 repaint = event_mouse_button_up(event->button.x, event->button.y,
531 mode, xuni);
533 if(repaint) handled = 1;
534 break;
535 case SDL_MOUSEMOTION:
536 repaint = set_loop_widget_sel(*mode, event->button.x, event->button.y,
537 xuni->gui->sel.clickin, xuni);
539 /*if(repaint) handled = 1;*/
540 break;
541 case SDL_VIDEORESIZE:
542 if(!resize_screen(xuni->smode, &event->resize)) {
543 widget_event(xuni, xuni->gui->widget, WIDGET_EVENT_RESCALE);
545 xuni->gui->widget->p.panel
546 ->event[PANEL_EVENT_EVENT].p.event.mode = mode;
547 xuni->gui->widget->p.panel
548 ->event[PANEL_EVENT_EVENT].p.event.event = event;
550 panel_event_recursive(xuni,
551 &xuni->gui->widget->p.panel->event[PANEL_EVENT_EVENT],
552 PANEL_EVENT_EVENT, xuni->gui->widget);
555 if(focus_changed(xuni->smode, SDL_APPACTIVE)) {
556 if(xuni->smode->focus) {
557 set_loop_caption(*mode, xuni);
559 else {
560 show_cursor(1);
564 handled = 1;
565 repaint = 1;
566 break;
567 case SDL_VIDEOEXPOSE:
568 repaint = 1;
569 handled = 1;
570 break;
571 case SDL_KEYDOWN:
572 repaint = event_key_down(xuni, &event->key.keysym, mode, &handled);
574 break;
575 case SDL_KEYUP:
576 break;
577 case SDL_ACTIVEEVENT:
578 if(focus_changed(xuni->smode, SDL_APPACTIVE | SDL_APPINPUTFOCUS)) {
579 if(xuni->smode->focus) {
580 /*int x, y;
581 SDL_GetMouseState(&x, &y);
582 printf("activated: %i,%i\n", x, y);*/
583 /* !!! this is only for the cursor */
584 set_loop_caption(*mode, xuni);
585 repaint = 1;
587 else {
588 /* preserve any textbox data etc in the active widget */
589 clear_active(xuni, &xuni->gui->active, 1);
590 show_cursor(1);
594 handled = 1;
595 break;
596 default:
597 break;
600 if(!handled && panel->event[PANEL_EVENT_EVENT].handler) {
601 panel->event[PANEL_EVENT_EVENT].p.event.mode = mode;
602 panel->event[PANEL_EVENT_EVENT].p.event.event = event;
604 repaint |= (*panel->event[PANEL_EVENT_EVENT].handler)(xuni, panel);
607 return repaint;
610 /* !!! bad hack */
611 static void scroll_listbox(struct xuni_t *xuni, int dir) {
612 int step = 5;
614 if(xuni->gui->sel.p.widget
615 && xuni->gui->sel.p.widget->type == WIDGET_LISTBOX) {
617 move_scrollbar(xuni, xuni->gui->sel.p.widget->compose->widget
618 [WID_LISTBOX_VSCROLL], step * dir);
620 widget_event(xuni, xuni->gui->sel.p.widget, WIDGET_EVENT_REPOSITION);
623 if(xuni->gui->sel.p.widget
624 && xuni->gui->sel.p.widget->type == WIDGET_SCROLLBAR) {
626 move_scrollbar(xuni, xuni->gui->sel.p.widget, step * dir);
628 widget_event(xuni, xuni->gui->sel.p.widget->base,
629 WIDGET_EVENT_REPOSITION);
632 if(xuni->gui->sel.p.widget && xuni->gui->sel.p.widget->base
633 && xuni->gui->sel.p.widget->base->type == WIDGET_SCROLLBAR) {
635 move_scrollbar(xuni, xuni->gui->sel.p.widget->base, step * dir);
637 widget_event(xuni, xuni->gui->sel.p.widget->base->base,
638 WIDGET_EVENT_REPOSITION);
642 static int set_loop_widget_sel(panel_type_t mode, int xp, int yp,
643 int click, struct xuni_t *xuni) {
645 struct panel_data_t *panel
646 = widget_nameid_access(xuni->gui->widget, mode)->p.panel;
648 if(!panel->event[PANEL_EVENT_SEL].handler) return 0;
650 panel->event[PANEL_EVENT_SEL].p.sel.mode = mode;
651 panel->event[PANEL_EVENT_SEL].p.sel.xp = xp;
652 panel->event[PANEL_EVENT_SEL].p.sel.yp = yp;
653 panel->event[PANEL_EVENT_SEL].p.sel.click = click;
655 return (*panel->event[PANEL_EVENT_SEL].handler)(xuni, panel);
658 /*! Determines whether any accelerator key combinations have been or are being
659 pressed, and, if so, handles the click event.
661 \param accel This panel's accelerator array. Searched for accelerator
662 combinations matching the current keyboard state.
663 \param key The current keyboard state. Compared with accelerator
664 combinations for the current panel for matches.
665 \param xuni The xuni structure. Used to get a struct widget_t pointer to
666 the current panel.
667 \param mode The currently active panel. Only passed so that
668 call_perform_click_func() may be called.
670 static int find_widget_accelerator(struct xuni_accelerator_t *accel,
671 SDL_keysym *key, struct xuni_t *xuni, panel_type_t *mode) {
673 size_t x;
674 SDL_keysym *akey;
676 if(!accel) return 0;
678 for(x = 0; x < accel->n; x ++) {
679 akey = &accel->key[x].key;
680 if(akey->sym && akey->sym == key->sym
681 && check_sdl_key_mod(akey->mod, key->mod)) {
683 call_perform_click_func(xuni,
684 widget_nameid_access(xuni->gui->widget, *mode)->p.panel,
685 accel->key[x].widget, mode);
687 return 1;
691 return 0;
694 /* !!! needs to be reworked to allow OR, AND, NOT, etc. */
695 static int check_sdl_key_mod(SDLMod mod, SDLMod now) {
696 if(mod != KMOD_NONE) {
697 if((mod & KMOD_CTRL) == KMOD_CTRL) {
698 if(now & KMOD_CTRL) mod &= ~KMOD_CTRL;
699 else return 0;
701 if((mod & KMOD_SHIFT) == KMOD_SHIFT) {
702 if(now & KMOD_SHIFT) mod &= ~KMOD_SHIFT;
703 else return 0;
705 if((mod & KMOD_ALT) == KMOD_ALT) {
706 if(now & KMOD_ALT) mod &= ~KMOD_ALT;
707 else return 0;
711 if(now != mod) return 0;
713 return 1;
716 static int event_mouse_button_up(int xp, int yp, panel_type_t *mode,
717 struct xuni_t *xuni) {
719 struct panel_data_t *panel
720 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
721 int repaint = 0, realclick;
723 if(xuni->gui->sel.p.widget && xuni->gui->sel.clickin
724 && pos_in_rect(xp, yp, xuni->gui->sel.p.widget->pos)) {
726 /*xuni->gui->sel.clickin = 0;*/
727 realclick = !set_widget_sel(&xuni->gui->sel, xp, yp, 1,
728 widget_nameid_access(xuni->gui->widget, *mode));
730 if(realclick) {
731 /* !!! return value ignored: repaint is always set to 1 */
732 call_perform_click_func(xuni, panel, xuni->gui->sel.p.widget,
733 mode);
736 xuni->gui->sel.wasin = 1;
737 xuni->gui->sel.clickin = 0;
738 repaint = 1;
740 if(realclick) {
741 if(xuni->gui->sel.p.widget &&
742 widget_can_be_active(xuni->gui->sel.p.widget)) {
744 xuni->gui->active = xuni->gui->sel.p;
745 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
746 enable_unicode(xuni->gui->active.widget);
748 else if(xuni->gui->active.widget) {
749 repaint = 1;
751 clear_active(xuni, &xuni->gui->active, 1);
754 if(*mode != (size_t)-1) {
755 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
756 xuni);
760 else {
761 repaint = set_loop_widget_sel(*mode, xp, yp, 0, xuni);
763 if(xuni->gui->active.widget &&
764 !pos_in_rect(xp, yp, xuni->gui->active.widget->pos)) {
766 repaint = 1;
768 clear_active(xuni, &xuni->gui->active, 1);
772 return repaint;
775 static int perform_loop_click(struct xuni_t *xuni, panel_type_t *mode,
776 struct widget_t *widget) {
778 struct panel_data_t *panel
779 = widget_nameid_access(xuni->gui->widget, *mode)->p.panel;
780 int repaint, xp = 0, yp = 0; /* !!! */
782 /* !!! return value ignored: repaint is always set to 1 */
783 call_perform_click_func(xuni, panel, widget, mode);
785 xuni->gui->sel.wasin = 1;
786 xuni->gui->sel.clickin = 0;
787 repaint = 1;
789 if(xuni->gui->sel.p.widget &&
790 widget_can_be_active(xuni->gui->sel.p.widget)) {
792 xuni->gui->active = xuni->gui->sel.p;
793 activate_widget(xuni->gui->active.widget, xuni, xp, yp);
794 enable_unicode(xuni->gui->active.widget);
796 else if(xuni->gui->active.widget) {
797 repaint = 1;
799 clear_active(xuni, &xuni->gui->active, 1);
802 if(*mode != (size_t)-1) {
803 set_loop_widget_sel(*mode, xp, yp, xuni->gui->sel.clickin,
804 xuni);
807 return repaint;
810 /* Perhaps functions for handling these keypresses could be registered?
812 Note: a lot of debugging keypresses are here, usually F-keys.
814 /*! Handles the keypresses that are common to all xuni widgets. This includes:
815 - escape, which reverts widgets;
816 - tab and shift-tab, which cycle though widgets;
817 - enter, which might deactivate or select widgets; and
818 - printscreen, which saves a screenshot.
820 \param xuni A pointer to the main xuni structure.
821 \param keysym A structure describing the state of the keyboard, e.g. the
822 key that was pressed along with the state of any modifier keys etc.
823 \param mode The currently visible panel. This could be changed indirectly
824 in the call to perform_loop_click().
825 \param handled Set to true if the keypress described by \a keysym was
826 handled. If the keypress was handled, the xuni application will never
827 see the event.
828 \return True if a repaint of the screen is required. This happens when,
829 for example, the keypress resulted in a widget being deactivated.
831 static int event_key_down(struct xuni_t *xuni, SDL_keysym *keysym,
832 panel_type_t *mode, int *handled) {
834 int repaint = 0;
836 switch(keysym->sym) {
837 case SDLK_ESCAPE:
838 if(xuni->gui->active.widget) {
839 clear_active(xuni, &xuni->gui->active, 0);
841 *handled = 1;
842 repaint = 1;
845 break;
846 case SDLK_PRINT:
847 save_screenshot(xuni->smode->screen);
849 *handled = 1;
850 break;
851 case SDLK_RETURN:
852 if(keysym->mod & KMOD_ALT) {
853 if(!toggle_fullscreen(xuni->smode)) repaint = 1;
855 *handled = 1;
857 else if(xuni->gui->tab.panel != (size_t)-1
858 && xuni->gui->tab.sel != (size_t)-1) {
860 repaint |= perform_loop_click(xuni, mode,
861 widget_nameid_follow(xuni->gui->widget,
862 xuni->gui->tab.panel, xuni->gui->tab.sel,
863 (size_t)-1));
866 break;
867 case SDLK_TAB:
868 if(xuni->gui->tab.panel == (size_t)-1) break;
870 if(keysym->mod & KMOD_SHIFT) {
871 if(xuni->gui->tab.sel-- == (size_t)-1) {
872 xuni->gui->tab.sel =
873 widget_nameid_access(xuni->gui->widget,
874 xuni->gui->tab.panel)->compose->widgets - 1;
877 else {
878 if(++xuni->gui->tab.sel ==
879 widget_nameid_access(xuni->gui->widget,
880 xuni->gui->tab.panel)->compose->widgets) {
882 xuni->gui->tab.sel = (size_t)-1;
886 repaint = 1;
887 *handled = 1;
888 break;
889 case SDLK_F9:
890 if(keysym->mod & KMOD_ALT) {
891 if(!SDL_WM_IconifyWindow()) {
892 /* error !!! */
895 *handled = 1;
897 break;
898 /* keypresses for debugging purposes */
899 case SDLK_F8:
900 if(xuni->gui->sel.p.widget) {
901 print_widget_backtrace(xuni, xuni->gui->sel.p.widget);
904 *handled = 1;
905 break;
906 case SDLK_F7:
907 if(xuni->gui->active.widget) {
908 print_widget_backtrace(xuni, xuni->gui->active.widget);
911 *handled = 1;
912 break;
913 case SDLK_F6:
914 puts("====");
915 print_sel_widgets(xuni->gui->widget);
916 *handled = 1;
917 break;
918 case SDLK_F5:
920 int x, y, xrel, yrel;
921 Uint8 state;
923 state = SDL_GetMouseState(&x, &y);
924 SDL_GetRelativeMouseState(&xrel, &yrel);
926 printf("Mouse: at (%i,%i), moved (%i,%i), buttons %i%i%i\n",
927 x, y, xrel, yrel, state & SDL_BUTTON(1),
928 state & SDL_BUTTON(2), state & SDL_BUTTON(3));
930 SDL_WarpMouse(x, y);
932 break;
933 case SDLK_F4:
934 /* force a repaint */
935 repaint = 1;
936 break;
937 case SDLK_F3:
938 if(xuni->gui->edit.datawidget) {
939 printf("textbox data: \"%s\"\n",
940 xuni->gui->edit.datawidget->p.label->text);
942 else puts("No label selected");
943 break;
944 case SDLK_F2:
945 if(xuni->gui->sel.p.widget->type == WIDGET_TEXTBOX) {
946 printf("textbox leftpos: %i\n",
947 xuni->gui->sel.p.widget->p.textbox->leftpos);
949 break;
950 case SDLK_F1:
951 move_scrollbar(xuni, xuni->gui->widget, 1);
952 break;
953 default:
954 break;
957 if(*mode != (size_t)-1) {
958 if(!*handled && find_widget_accelerator(
959 widget_nameid_access(xuni->gui->widget, *mode)
960 ->p.panel->accel, keysym, xuni, mode)) {
962 *handled = 1;
965 if(xuni->gui->active.widget) {
966 repaint = widget_process_character(xuni, keysym);
968 update_widget(xuni, xuni->gui->active.widget);
972 if(repaint) *handled = 1;
974 return repaint;