1 /***************************************************************************
2 gui_widget.c - description
3 -------------------
4 begin : Fri Oct 11 2002
5 copyright : (C) 2002 by Michael Speck
6 email : kulkanie@gmx.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include "gui_widget.h"
21
22 extern GuiTheme *gui_theme;
23 extern SDL_Surface *stk_display;
24 extern List *gui_root_stack;
25 extern List *gui_visible_stack;
26 extern List *gui_timed_stack;
27
28 GuiWidget *gui_key_widget = 0;
29 /* is the last child widget of _all_ visible key grabbing
30 widgets that was clicked on */
31 GuiWidget *gui_clicked_widget = 0;
32 /* is the last child widget of _all_ visible widgets
33 that was clicked on (used to clear 'pressed' flag) */
34 GuiWidget *gui_focused_widget = 0;
35 /* the very toplevel widget of all visible widgets
36 the mouse pointer hovers above */
37
38 /*
39 ====================================================================
40 LOCALS
41 ====================================================================
42 */
43
44 /*
45 ====================================================================
46 Free the memory of a widget and all its subwidgets.
47 ====================================================================
48 */
gui_widget_delete_intern(GuiWidget * widget)49 static void gui_widget_delete_intern( GuiWidget *widget )
50 {
51 GuiWidget *child;
52 if ( widget ) {
53 /* go recursive */
54 list_reset( widget->widgets );
55 while ( ( child = list_next( widget->widgets ) ) )
56 gui_widget_delete_intern( child );
57 /* free common data */
58 list_delete( widget->widgets );
59 stk_surface_free( &widget->surface );
60 /* free special data */
61 widget->default_event_handler(
62 widget, gui_event_get_simple( GUI_DESTROY ) );
63 /* widget itself */
64 free( widget );
65 }
66 }
67
68 /*
69 ====================================================================
70 Draw the widget and its children. gui_widget_draw() disabled
71 the display monitoring and will add the widgets screen_region
72 as update rectangle.
73 ====================================================================
74 */
gui_widget_draw_intern(GuiWidget * widget)75 static void gui_widget_draw_intern( GuiWidget *widget )
76 {
77 GuiWidget *child = 0;
78 /* draw widget */
79 widget->default_event_handler(
80 widget, gui_event_get_simple( GUI_DRAW ) );
81 /* draw children */
82 list_reset( widget->widgets );
83 while ( ( child = list_next( widget->widgets ) ) )
84 gui_widget_draw_intern( child );
85 }
86
87 /*
88 ====================================================================
89 Set visible flag to 'visible' for widget and all children
90 recursively.
91 ====================================================================
92 */
gui_widget_show_intern(GuiWidget * widget,int visible)93 static void gui_widget_show_intern( GuiWidget *widget, int visible )
94 {
95 GuiWidget *child;
96 widget->visible = visible;
97 list_reset( widget->widgets );
98 while ( ( child = list_next( widget->widgets ) ) )
99 gui_widget_show_intern( child, visible );
100 }
101
102 /*
103 ====================================================================
104 If there are any root windows above this widget in the stack that
105 overlap this one they need to be redrawn. We clip to the widgets
106 screen region and redraw all root widgets above.
107 ====================================================================
108 */
gui_widget_redraw_overlapping_roots(GuiWidget * widget)109 static void gui_widget_redraw_overlapping_roots(
110 GuiWidget *widget )
111 {
112 ListEntry *entry;
113 if ( gui_visible_stack->count > 1 )
114 if ( list_last( gui_visible_stack ) != widget->root ) {
115 /* clip */
116 stk_surface_clip( stk_display,
117 widget->screen_region.x, widget->screen_region.y,
118 widget->screen_region.w, widget->screen_region.h );
119 /* get first root widget above */
120 entry = list_entry(
121 gui_visible_stack, widget->root );
122 if ( !entry ) {
123 fprintf( stderr,
124 "'visible_stack' corrupted: root widget not found\n" );
125 return;
126 }
127 entry = entry->next;
128 /* redraw */
129 while ( entry != gui_visible_stack->tail ) {
130 gui_widget_draw_intern( (GuiWidget*)entry->item );
131 entry = entry->next;
132 }
133 /* unclip */
134 stk_surface_clip( stk_display, 0,0,-1,-1 );
135 }
136 }
137
138 /*
139 ====================================================================
140 Move position by relative value for all widgets.
141 ====================================================================
142 */
gui_widget_move_intern(GuiWidget * widget,int rel_x,int rel_y)143 static void gui_widget_move_intern(
144 GuiWidget *widget, int rel_x, int rel_y)
145 {
146 GuiWidget *child;
147 widget->screen_region.x += rel_x;
148 widget->screen_region.y += rel_y;
149 widget->parent_region.x += rel_x;
150 widget->parent_region.y += rel_y;
151 list_reset( widget->widgets );
152 while ( ( child = list_next( widget->widgets ) ) )
153 gui_widget_move_intern( child, rel_x, rel_y );
154 }
155
156 /*
157 ====================================================================
158 PUBLICS
159 ====================================================================
160 */
161
162 /*
163 ====================================================================
164 GUI events
165 ====================================================================
166 */
167
168 /*
169 ====================================================================
170 Return pointer to simple event (one that doesn't need
171 additional data)
172 ====================================================================
173 */
174 static GuiEvent aux_event;
gui_event_get_simple(int type)175 GuiEvent *gui_event_get_simple( int type )
176 {
177 aux_event.type = type;
178 return &aux_event;
179 }
180
181 /*
182 ====================================================================
183 Initiate a GUI event from an SDL event.
184 ====================================================================
185 */
gui_event_init(GuiEvent * gui_event,SDL_Event * sdl_event)186 void gui_event_init( GuiEvent *gui_event, SDL_Event *sdl_event )
187 {
188 int i;
189 memset( gui_event, 0, sizeof( GuiEvent ) );
190 switch ( sdl_event->type ) {
191 case SDL_MOUSEMOTION:
192 gui_event->type = GUI_MOTION;
193 gui_event->motion.x = sdl_event->motion.x;
194 gui_event->motion.y = sdl_event->motion.y;
195 gui_event->motion.xrel = sdl_event->motion.xrel;
196 gui_event->motion.yrel = sdl_event->motion.yrel;
197 gui_event->motion.state = sdl_event->motion.state;
198 for ( i = 1; i <= 3; i++ )
199 if ( sdl_event->motion.state & SDL_BUTTON(i) ) {
200 gui_event->motion.button = i;
201 break;
202 }
203 break;
204 case SDL_MOUSEBUTTONUP:
205 case SDL_MOUSEBUTTONDOWN:
206 if ( sdl_event->type == SDL_MOUSEBUTTONUP )
207 gui_event->type = GUI_BUTTON_RELEASED;
208 else
209 gui_event->type = GUI_BUTTON_PRESSED;
210 gui_event->button.x = sdl_event->button.x;
211 gui_event->button.y = sdl_event->button.y;
212 gui_event->button.button = sdl_event->button.button;
213 break;
214 case SDL_KEYUP:
215 case SDL_KEYDOWN:
216 if ( sdl_event->type == SDL_KEYUP )
217 gui_event->type = GUI_KEY_RELEASED;
218 else
219 gui_event->type = GUI_KEY_PRESSED;
220 gui_event->key.keysym = sdl_event->key.keysym.sym;
221 gui_event->key.unicode = sdl_event->key.keysym.unicode;
222 break;
223 }
224 }
225
226 /*
227 ====================================================================
228 GUI widget
229 ====================================================================
230 */
231
232 /*
233 ====================================================================
234 Create a basic widget and setup things all different widget types
235 have in common. If a parent is specified this widget is added to
236 it's 'widgets' list. Per default following events are enabled:
237 GUI_ACTIVATED, GUI_DEACTIVATED, GUI_DRAW, GUI_DESTROY,
238 GUI_FOCUS_IN, GUI_FOCUS_OUT. 'x' or 'y' -1 means to center the
239 widget.
240 ====================================================================
241 */
gui_widget_create(GuiWidget * parent,int type,int x,int y,int width,int height,void (* default_event_handler)(GuiWidget *,GuiEvent *),void (* user_event_handler)(GuiWidget *,GuiEvent *))242 GuiWidget* gui_widget_create(
243 GuiWidget *parent, int type,
244 int x, int y, int width, int height,
245 void (*default_event_handler)(GuiWidget*,GuiEvent*),
246 void (*user_event_handler)(GuiWidget*,GuiEvent*) )
247 {
248 GuiWidget *widget = calloc( 1, sizeof( GuiWidget ) );
249 if ( widget == 0 )
250 GUI_ABORT( "Out Of Memory" )
251 /* create empty children list */
252 if ( ( widget->widgets =
253 list_create( LIST_NO_AUTO_DELETE,
254 LIST_NO_CALLBACK ) ) == 0 )
255 GUI_ABORT( "Out Of Memory" )
256 /* various assignments */
257 if ( parent ) {
258 widget->parent = parent;
259 list_add( widget->parent->widgets, widget );
260 }
261 widget->type = type;
262 widget->root = (parent==0)?widget:parent->root;
263 widget->active = 1;
264 widget->default_event_handler = default_event_handler;
265 widget->user_event_handler = user_event_handler;
266 /* adjust x,y */
267 if ( x == -1 ) {
268 if ( parent )
269 x = ( parent->width - width ) / 2;
270 else
271 x = ( stk_display->w - width ) / 2;
272 }
273 if ( y == -1 ) {
274 if ( parent )
275 y = ( parent->height - height ) / 2;
276 else
277 y = ( stk_display->h - height ) / 2;
278 }
279 /* region in parent */
280 if ( parent == 0 ) {
281 widget->parent_region.x = x;
282 widget->parent_region.y = y;
283 }
284 else {
285 widget->parent_region.x = x + widget->parent->border;
286 widget->parent_region.y = y + widget->parent->border;
287 }
288 widget->parent_region.w = width;
289 widget->parent_region.h = height;
290 /* screen region */
291 if ( widget->parent == 0 )
292 widget->screen_region = widget->parent_region;
293 else {
294 widget->screen_region.x =
295 widget->parent_region.x + parent->screen_region.x;
296 widget->screen_region.y =
297 widget->parent_region.y + parent->screen_region.y;
298 widget->screen_region.w = widget->parent_region.w;
299 widget->screen_region.h = widget->parent_region.h;
300 }
301 /* children size */
302 widget->width = width; widget->height = height;
303 /* events */
304 gui_widget_enable_event( widget, GUI_ACTIVATED );
305 gui_widget_enable_event( widget, GUI_DEACTIVATED );
306 gui_widget_enable_event( widget, GUI_DRAW );
307 gui_widget_enable_event( widget, GUI_DESTROY );
308 gui_widget_enable_event( widget, GUI_FOCUS_IN );
309 gui_widget_enable_event( widget, GUI_FOCUS_OUT );
310 /* if this is a root widget add it to root_stack */
311 if ( widget->root == widget )
312 list_add( gui_root_stack, widget );
313 /* done */
314 return widget;
315 }
316
317 /*
318 ====================================================================
319 This function will delete a root widget including all subwidgets.
320 Subwidgets can't be directly deleted. Resets the widget
321 pointer to NULL.
322 ====================================================================
323 */
gui_widget_delete(GuiWidget ** widget)324 void gui_widget_delete( GuiWidget **widget )
325 {
326 if ( *widget == 0 ) return;
327 if ( (*widget)->root != *widget ) {
328 fprintf( stderr, "You can only delete root widgets!\n" );
329 return;
330 }
331 if ( (*widget)->visible )
332 list_delete_item( gui_visible_stack, *widget );
333 if ( (*widget)->event_mask & GUI_TIME_PASSED )
334 list_delete_item( gui_timed_stack, *widget );
335 list_delete_item( gui_root_stack, *widget );
336 gui_widget_delete_intern( *widget );
337 *widget = 0;
338 }
339
340
341 /*
342 ====================================================================
343 If button is deactivated no input events (key,button,motion)
344 are handled.
345 ====================================================================
346 */
gui_widget_set_active(GuiWidget * widget,int active)347 void gui_widget_set_active( GuiWidget *widget, int active )
348 {
349 int type;
350 if ( widget->active == active ) return;
351 widget->active = active;
352 type = (active)?GUI_ACTIVATED:GUI_DEACTIVATED;
353 widget->default_event_handler(
354 widget, gui_event_get_simple( type ) );
355 gui_widget_call_user_event_handler(
356 widget, gui_event_get_simple( type ) );
357 }
358
359 /*
360 ====================================================================
361 Draw the widget and its children if visible.
362 ====================================================================
363 */
gui_widget_draw(GuiWidget * widget)364 void gui_widget_draw( GuiWidget *widget )
365 {
366 /* update only if visible */
367 if ( !widget->visible ) return;
368 /* recursively draw widget */
369 gui_widget_draw_intern( widget );
370 /* redraw higher level roots that overlap this widget. */
371 gui_widget_redraw_overlapping_roots( widget );
372 /* store update rect */
373 stk_display_store_rect( &widget->screen_region );
374 }
375
376 /*
377 ====================================================================
378 Set 'visible' flag and draw widget (store update rects)
379 if either parent is visible or it has no parent.
380 (thus is a root window). If it is a root window add it to the
381 root window stack. This new window will handle incoming events
382 first.
383 ====================================================================
384 */
gui_widget_show(GuiWidget * widget)385 void gui_widget_show( GuiWidget *widget )
386 {
387 if ( widget->visible ) return;
388 if ( widget->root == widget ) {
389 list_add( gui_visible_stack, widget );
390 if ( widget->event_mask & GUI_TIME_PASSED )
391 list_add( gui_timed_stack, widget );
392 #ifdef GUI_DEBUG
393 printf( "show root widget: %i,%i,%i,%i\n",
394 widget->screen_region.x, widget->screen_region.y,
395 widget->screen_region.w, widget->screen_region.h );
396 #endif
397 /* if this root widget grabs the input the old
398 gui_key_widget/gui_clicked_widget is obsolete */
399 if ( widget->grab_input ) {
400 if ( gui_key_widget ) {
401 gui_key_widget->default_event_handler(
402 gui_key_widget,
403 gui_event_get_simple( GUI_FOCUS_OUT ) );
404 gui_key_widget = 0;
405 }
406 gui_clicked_widget = 0;
407 }
408 /* maybe there is a default key grab widget? */
409 if ( widget->default_key_widget )
410 gui_key_widget = widget->default_key_widget;
411 }
412 gui_widget_show_intern( widget, 1 );
413 if ( widget->parent == 0 || widget->parent->visible )
414 gui_widget_draw( widget );
415 }
416
417 /*
418 ====================================================================
419 Clear 'visible' flag and restore widget if parent is visible.
420 If there is no parent (thus is a root window) remove it from
421 stack and redraw the underlying window (which regains control). If
422 a root widget is hidden the background cannot be restored
423 as it is unknown.
424 ====================================================================
425 */
gui_widget_hide(GuiWidget * widget)426 void gui_widget_hide( GuiWidget *widget )
427 {
428 ListEntry *entry;
429 if ( !widget->visible ) return;
430 gui_widget_show_intern( widget, 0 );
431 if ( widget->root == widget ) {
432 entry = list_entry( gui_visible_stack, widget );
433 if ( entry ) {
434 /* remove */
435 entry = entry->next;
436 list_delete_entry(
437 gui_visible_stack, entry->prev );
438 /* FIX ME! redraw all open roots */
439 entry = gui_visible_stack->head->next;
440 while ( entry != gui_visible_stack->tail ) {
441 gui_widget_draw( (GuiWidget*)entry->item );
442 entry = entry->next;
443 }
444 }
445 if ( widget->event_mask & GUI_TIME_PASSED )
446 list_delete_item( gui_timed_stack, widget );
447 }
448 else {
449 /* draw parent's background */
450 stk_surface_blit( widget->parent->surface,
451 widget->parent_region.x, widget->parent_region.y,
452 widget->parent_region.w, widget->parent_region.h,
453 stk_display,
454 widget->screen_region.x, widget->screen_region.y );
455 /* redraw higher level roots that overlap this widget. */
456 gui_widget_redraw_overlapping_roots( widget );
457 /* store update rect */
458 stk_display_store_rect( &widget->screen_region );
459 }
460 /* check if gui_key_widget is still valid */
461 if ( gui_key_widget )
462 if ( widget == gui_key_widget ||
463 widget == gui_key_widget->parent ||
464 widget == gui_key_widget->root )
465 gui_key_widget = 0;
466 }
467
468 /*
469 ====================================================================
470 Modify the event mask of a widget to define which events will
471 be passed to user_event_handler.
472 ====================================================================
473 */
gui_widget_enable_event(GuiWidget * widget,int event)474 void gui_widget_enable_event( GuiWidget *widget, int event )
475 {
476 widget->event_mask |= (1L << event);
477 }
gui_widget_disable_event(GuiWidget * widget,int event)478 void gui_widget_disable_event( GuiWidget *widget, int event )
479 {
480 widget->event_mask &= ~(1L << event);
481 }
482
483 /*
484 ====================================================================
485 Pass GuiEvent to user defined callback if it has been installed
486 and the event mask flag is True for this event.
487 ====================================================================
488 */
gui_widget_call_user_event_handler(GuiWidget * widget,GuiEvent * event)489 void gui_widget_call_user_event_handler(
490 GuiWidget *widget, GuiEvent *event )
491 {
492 if ( widget->user_event_handler )
493 if ( widget->event_mask & (1L << event->type) )
494 widget->user_event_handler( widget, event );
495 }
496
497 /*
498 ====================================================================
499 Handle the GUI event by calling the default_event_handler()
500 and the user_event_handler() if one has been installed.
501 ====================================================================
502 */
gui_widget_handle_event(GuiWidget * widget,GuiEvent * event)503 void gui_widget_handle_event( GuiWidget *widget, GuiEvent *event )
504 {
505 widget->default_event_handler( widget, event );
506 gui_widget_call_user_event_handler( widget, event );
507 }
508
509 /*
510 ====================================================================
511 Move widget within parent window by a relative value. If the
512 widget is visible the changes will be drawn to screen.
513 ====================================================================
514 */
gui_widget_move(GuiWidget * widget,int rel_x,int rel_y)515 void gui_widget_move( GuiWidget *widget, int rel_x, int rel_y )
516 {
517 if ( !widget->visible )
518 gui_widget_move_intern( widget, rel_x, rel_y );
519 else {
520 gui_widget_hide( widget );
521 gui_widget_move_intern( widget, rel_x, rel_y );
522 gui_widget_show( widget );
523 }
524 }
525
526 /*
527 ====================================================================
528 Move widget within parent window by an absolute value. If the
529 widget is visible the changes will be drawn to screen.
530 ====================================================================
531 */
gui_widget_warp(GuiWidget * widget,int abs_x,int abs_y)532 void gui_widget_warp( GuiWidget *widget, int abs_x, int abs_y )
533 {
534 if ( widget->parent )
535 gui_widget_move( widget,
536 abs_x - widget->parent_region.x +
537 widget->parent->border,
538 abs_y - widget->parent_region.y +
539 widget->parent->border );
540 else
541 gui_widget_move( widget,
542 abs_x - widget->parent_region.x,
543 abs_y - widget->parent_region.y );
544 }
545
546 /*
547 ====================================================================
548 Apply parents background or wallpaper within the frame (if
549 any) of the widget's surface keeping the frame.
550 ====================================================================
551 */
gui_widget_apply_wallpaper(GuiWidget * widget,SDL_Surface * wallpaper,int alpha)552 void gui_widget_apply_wallpaper(
553 GuiWidget *widget, SDL_Surface *wallpaper, int alpha )
554 {
555 if ( widget->parent )
556 stk_surface_blit( widget->parent->surface,
557 widget->parent_region.x +
558 widget->border,
559 widget->parent_region.y +
560 widget->border,
561 widget->parent_region.w -
562 (widget->border<<1),
563 widget->parent_region.h -
564 (widget->border<<1),
565 widget->surface,
566 widget->border, widget->border );
567 else
568 stk_surface_apply_wallpaper(
569 widget->surface, widget->border, widget->border,
570 widget->parent_region.w - (widget->border<<1),
571 widget->parent_region.h - (widget->border<<1),
572 wallpaper, alpha );
573 }
574
575 /*
576 ====================================================================
577 Browse the widget tree and set 'focused' true for all widgets
578 that have the mouse pointer above them. 'focused_widget'
579 returns the deepest widget that is focused.
580 ====================================================================
581 */
gui_widget_update_focus(GuiWidget * widget,int mx,int my,GuiWidget ** focused_widget)582 void gui_widget_update_focus(
583 GuiWidget *widget, int mx, int my, GuiWidget **focused_widget )
584 {
585 GuiWidget *child;
586 if ( !widget->active )
587 return;
588 if ( !STK_IN_RECT( widget->screen_region, mx, my ) )
589 return;
590 widget->focused = 1;
591 *focused_widget = widget;
592 /* handle children recursively */
593 list_reset( widget->widgets );
594 while ( ( child = list_next( widget->widgets ) ) )
595 gui_widget_update_focus( child, mx, my, focused_widget );
596 }
597
598 /*
599 ====================================================================
600 Get direct access to widget's surface.
601 ====================================================================
602 */
gui_widget_get_surface(GuiWidget * widget)603 SDL_Surface *gui_widget_get_surface( GuiWidget *widget )
604 {
605 return widget->surface;
606 }
607
608 /*
609 ====================================================================
610 That key grabbing child of a root widget.
611 ====================================================================
612 */
gui_widget_set_default_key_widget(GuiWidget * root,GuiWidget * key_widget)613 void gui_widget_set_default_key_widget(
614 GuiWidget *root, GuiWidget *key_widget )
615 {
616 if ( root->root != root ) return;
617 root->default_key_widget = key_widget;
618 }
619