1 /*
2  * Tux Racer
3  * Copyright (C) 1999-2001 Jasmin F. Patry
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  */
19 
20 #include "tuxracer.h"
21 #include "listbox.h"
22 #include "fonts.h"
23 #include "ui_mgr.h"
24 #include "ui_theme.h"
25 #include "render_util.h"
26 #include "button.h"
27 
28 #define DEFAULT_ARROW_BUTTON_HEIGHT 15
29 #define DEFAULT_ARROW_BUTTON_WIDTH  27
30 #define DEFAULT_ARROW_REGION_WIDTH  36
31 #define DEFAULT_ARROW_VERT_SEPARATION  4
32 #define DEFAULT_BORDER_WIDTH 4
33 #define DEFAULT_TEXT_PAD 8
34 
35 struct listbox_ {
36     point2d_t pos;
37     scalar_t w;
38     scalar_t h;
39     scalar_t arrow_width;
40     scalar_t border_width;
41     scalar_t text_pad;
42     scalar_t arrow_vert_separation;
43     char *font_binding;
44     colour_t border_colour;
45     colour_t background_colour;
46     button_t *up_button;
47     button_t *down_button;
48     listbox_item_change_event_cb_t item_change_cb;
49     void *item_change_cb_userdata;
50     list_t item_list;
51     list_elem_t cur_item;
52     bool_t visible;
53     bool_t active;
54 
55     listbox_list_elem_to_string_fptr_t label_gen_func;
56 };
57 
58 
59 /*---------------------------------------------------------------------------*/
60 /*!
61   Default function for converting list elements to character strings (to be
62   drawn in listbox)
63   \return  String represenation of elem
64   \author  jfpatry
65   \date    Created:  2000-09-17
66   \date    Modified: 2000-09-17
67 */
listbox_default_list_elem_to_string_func(list_elem_data_t data)68 char * listbox_default_list_elem_to_string_func( list_elem_data_t data )
69 {
70     return (char*) data;
71 }
72 
73 
74 /*---------------------------------------------------------------------------*/
75 /*!
76   Updates the enabled states of the arrow buttons
77   \author  jfpatry
78   \date    Created:  2000-09-17
79   \date    Modified: 2000-09-17
80 */
update_button_enabled_states(listbox_t * listbox)81 static void update_button_enabled_states( listbox_t *listbox )
82 {
83     check_assertion( listbox != NULL, "listbox is NULL" );
84 
85     if ( listbox->cur_item == NULL ) {
86 	/* No items in list */
87 	button_set_enabled( listbox->up_button, False );
88 	button_set_enabled( listbox->down_button, False );
89     } else {
90 	if ( listbox->cur_item == get_list_head( listbox->item_list ) ) {
91 	    button_set_enabled( listbox->up_button, False );
92 	} else {
93 	    button_set_enabled( listbox->up_button, True );
94 	}
95 
96 	if ( listbox->cur_item == get_list_tail( listbox->item_list ) ) {
97 	    button_set_enabled( listbox->down_button, False );
98 	} else {
99 	    button_set_enabled( listbox->down_button, True );
100 	}
101     }
102 }
103 
104 
105 
106 /*---------------------------------------------------------------------------*/
107 /*!
108   Updates positions of buttons
109   \return  None
110   \author  jfpatry
111   \date    Created:  2000-09-17
112   \date    Modified: 2000-09-17
113 */
update_button_positions(listbox_t * listbox)114 static void update_button_positions( listbox_t *listbox )
115 {
116     check_assertion( listbox != NULL, "listbox is NULL" );
117 
118     button_set_position(
119 	listbox->up_button,
120 	make_point2d(
121 	    listbox->pos.x + listbox->w -
122 	    button_get_width( listbox->up_button ),
123 	    listbox->pos.y + listbox->h / 2.0 +
124 	    listbox->arrow_vert_separation / 2.0 ) );
125 
126     button_set_position(
127 	listbox->down_button,
128 	make_point2d(
129 	    listbox->pos.x + listbox->w -
130 	    button_get_width( listbox->down_button ),
131 	    listbox->pos.y + listbox->h / 2.0 -
132 	    listbox->arrow_vert_separation / 2.0 -
133 	    button_get_height( listbox->up_button ) ) );
134 }
135 
136 
137 /*---------------------------------------------------------------------------*/
138 /*!
139   Callback called when one of the arrow buttons is pressed
140   \return  None
141   \author  jfpatry
142   \date    Created:  2000-09-17
143   \date    Modified: 2000-09-17
144 */
listbox_arrow_click_cb(button_t * button,void * userdata)145 static void listbox_arrow_click_cb( button_t *button, void* userdata )
146 {
147     listbox_t *listbox = (listbox_t*)userdata;
148 
149     check_assertion( listbox != NULL, "listbox is NULL" );
150 
151     if ( button == listbox->up_button ) {
152 	listbox->cur_item = get_prev_list_elem( listbox->item_list,
153 						listbox->cur_item );
154     } else if ( button == listbox->down_button ) {
155 	listbox->cur_item = get_next_list_elem( listbox->item_list,
156 						listbox->cur_item );
157 
158     } else {
159 	check_assertion( 0, "listbox arrow click callback called with "
160 			 "unknown button" );
161     }
162 
163     if ( listbox->item_change_cb != NULL ) {
164 	listbox->item_change_cb( listbox, listbox->item_change_cb_userdata );
165     }
166 
167     update_button_enabled_states( listbox );
168 
169     ui_set_dirty();
170 }
171 
172 
173 /*---------------------------------------------------------------------------*/
174 /*!
175   Advances to the next item in the list.  Returns true if successful, or
176   false if the current item (before the change) is the last item
177   \author  jfpatry
178   \date    Created:  2000-09-30
179   \date    Modified: 2000-09-30
180 */
listbox_goto_next_item(listbox_t * listbox)181 bool_t listbox_goto_next_item( listbox_t *listbox )
182 {
183     check_assertion( listbox != NULL, "listbox is NULL" );
184 
185     if ( listbox->cur_item == get_list_tail( listbox->item_list ) ) {
186 	return False;
187     }
188 
189     button_simulate_mouse_click( listbox->down_button );
190 
191     return True;
192 }
193 
194 
195 
196 /*---------------------------------------------------------------------------*/
197 /*!
198   Moves to the previous item in the list. Returns true if successful, or
199   false if the current item (before then change) is the first item.
200   \author  jfpatry
201   \date    Created:  2000-09-30
202   \date    Modified: 2000-09-30
203 */
listbox_goto_prev_item(listbox_t * listbox)204 bool_t listbox_goto_prev_item( listbox_t *listbox )
205 {
206     check_assertion( listbox != NULL, "listbox is NULL" );
207 
208     if ( listbox->cur_item == get_list_head( listbox->item_list ) ) {
209 	return False;
210     }
211 
212     button_simulate_mouse_click( listbox->up_button );
213 
214     return True;
215 }
216 
217 /*---------------------------------------------------------------------------*/
218 /*!
219   Draws the listbox
220   \return  None
221   \author  jfpatry
222   \date    Created:  2000-09-18
223   \date    Modified: 2000-09-18
224 */
listbox_draw(listbox_t * listbox)225 void listbox_draw( listbox_t *listbox )
226 {
227     font_t *font;
228 
229     check_assertion( listbox != NULL, "listbox is NULL" );
230 
231     glDisable( GL_TEXTURE_2D );
232 
233     glColor3dv( (scalar_t*)&listbox->border_colour );
234 
235     glRectf( listbox->pos.x,
236 	     listbox->pos.y,
237 	     listbox->pos.x + listbox->w - listbox->arrow_width,
238 	     listbox->pos.y + listbox->h );
239 
240     glColor3dv( (scalar_t*)&listbox->background_colour );
241 
242     glRectf( listbox->pos.x + listbox->border_width,
243 	     listbox->pos.y + listbox->border_width,
244 	     listbox->pos.x + listbox->w - listbox->border_width -
245 	     listbox->arrow_width,
246 	     listbox->pos.y + listbox->h - listbox->border_width );
247 
248     glEnable( GL_TEXTURE_2D );
249 
250     if ( !get_font_binding( listbox->font_binding, &font ) ) {
251 	print_warning( IMPORTANT_WARNING,
252 		       "Couldn't get font object for binding %s",
253 		       listbox->font_binding );
254     } else {
255 	int w, asc, desc;
256 	char *string;
257 
258 	string = listbox->label_gen_func(
259 	    get_list_elem_data( listbox->cur_item ) );
260 
261 	get_font_metrics( font, string, &w, &asc, &desc );
262 
263 	bind_font_texture( font );
264 
265 	glColor3f( 1.0, 1.0, 1.0 );
266 
267 	glPushMatrix();
268 	{
269 	    glTranslatef(
270 		listbox->pos.x + listbox->border_width + listbox->text_pad,
271 		listbox->pos.y + listbox->h/2.0 - asc/2.0 + desc/2.0,
272 		0 );
273 
274 	    draw_string( font, string );
275 	}
276 	glPopMatrix();
277     }
278 
279     button_draw( listbox->up_button );
280     button_draw( listbox->down_button );
281 }
282 
283 
284 /*---------------------------------------------------------------------------*/
285 /*!
286   Callback to draws the listbox
287   \return  None
288   \author  jfpatry
289   \date    Created:  2000-09-17
290   \date    Modified: 2000-09-17
291 */
listbox_draw_cb(void * widget)292 static void listbox_draw_cb( void *widget )
293 {
294     check_assertion( widget != NULL, "widget is NULL" );
295 
296     listbox_draw( (listbox_t*) widget );
297 }
298 
299 
300 /*---------------------------------------------------------------------------*/
301 /*!
302   Creates a new listbox
303   \return  The new listbox object
304   \author  jfpatry
305   \date    Created:  2000-09-17
306   \date    Modified: 2000-09-17
307 */
listbox_create(point2d_t pos,scalar_t w,scalar_t h,char * font_binding,list_t item_list,listbox_list_elem_to_string_fptr_t func)308 listbox_t* listbox_create( point2d_t pos, scalar_t w, scalar_t h,
309 			   char *font_binding, list_t item_list,
310 			   listbox_list_elem_to_string_fptr_t func )
311 {
312     listbox_t *listbox;
313     char *binding;
314     point2d_t ll;
315     point2d_t ur;
316 
317     listbox = (listbox_t*)malloc( sizeof(listbox_t) );
318 
319     check_assertion( listbox != NULL, "out of memory" );
320 
321     listbox->pos = pos;
322     listbox->w = w;
323     listbox->h = h;
324     listbox->arrow_width = DEFAULT_ARROW_REGION_WIDTH;
325     listbox->border_width = DEFAULT_BORDER_WIDTH;
326     listbox->text_pad = DEFAULT_TEXT_PAD;
327     listbox->arrow_vert_separation = DEFAULT_ARROW_VERT_SEPARATION;
328     listbox->font_binding = font_binding;
329     listbox->border_colour = ui_foreground_colour;
330     listbox->background_colour = ui_background_colour;
331 
332     /* Create up arrow button */
333     listbox->up_button = button_create(
334 	make_point2d( 0, 0 ), /* position will be set later */
335 	DEFAULT_ARROW_BUTTON_WIDTH,
336 	DEFAULT_ARROW_BUTTON_HEIGHT,
337 	NULL,
338 	NULL );
339 
340     binding = "listbox_arrows";
341 
342     ll = make_point2d( 2.0/64.0, 17.0/64.0 );
343     ur = make_point2d( 29.0/64.0, 32.0/64.0 );
344     button_set_image( listbox->up_button, binding, ll, ur, white );
345 
346     ll = make_point2d( 34.0/64.0, 17.0/64.0 );
347     ur = make_point2d( 61.0/64.0, 32.0/64.0 );
348     button_set_disabled_image( listbox->up_button, binding, ll, ur,
349 			       white );
350 
351     ll = make_point2d( 34.0/64.0, 49.0/64.0 );
352     ur = make_point2d( 61.0/64.0, 64.0/64.0 );
353     button_set_hilit_image( listbox->up_button, binding, ll, ur,
354 			    white );
355 
356     ll = make_point2d( 2.0/64.0, 49.0/64.0 );
357     ur = make_point2d( 29.0/64.0, 64.0/64.0 );
358     button_set_clicked_image( listbox->up_button, binding, ll, ur,
359 			      white );
360 
361 
362     button_set_click_event_cb( listbox->up_button,
363 			       listbox_arrow_click_cb,
364 			       listbox );
365 
366     /* Create down arrow button */
367     listbox->down_button = button_create(
368 	make_point2d( 0, 0 ), /* position will be set later */
369 	DEFAULT_ARROW_BUTTON_WIDTH,
370 	DEFAULT_ARROW_BUTTON_HEIGHT,
371 	NULL,
372 	NULL );
373 
374     binding = "listbox_arrows";
375 
376     ll = make_point2d( 2.0/64.0, 1.0/64.0 );
377     ur = make_point2d( 29.0/64.0, 16.0/64.0 );
378     button_set_image( listbox->down_button, binding, ll, ur, white );
379 
380     ll = make_point2d( 34.0/64.0, 1.0/64.0 );
381     ur = make_point2d( 61.0/64.0, 16.0/64.0 );
382     button_set_disabled_image( listbox->down_button, binding, ll, ur,
383 			       white );
384 
385     ll = make_point2d( 34.0/64.0, 33.0/64.0 );
386     ur = make_point2d( 61.0/64.0, 48.0/64.0 );
387     button_set_hilit_image( listbox->down_button, binding, ll, ur,
388 			    white );
389 
390     ll = make_point2d( 2.0/64.0, 33.0/64.0 );
391     ur = make_point2d( 29.0/64.0, 48.0/64.0 );
392     button_set_clicked_image( listbox->down_button, binding, ll, ur,
393 			      white );
394 
395     button_set_click_event_cb( listbox->down_button,
396 			       listbox_arrow_click_cb,
397 			       listbox );
398 
399     button_set_click_event_cb( listbox->down_button,
400 			       listbox_arrow_click_cb,
401 			       listbox );
402 
403     listbox->item_change_cb = NULL;
404     listbox->item_change_cb_userdata = NULL;
405 
406     listbox->item_list = item_list;
407     listbox->cur_item = get_list_head( listbox->item_list );
408 
409     listbox->label_gen_func = func;
410 
411     listbox->visible = False;
412     listbox->active = False;
413 
414     update_button_enabled_states( listbox );
415 
416     update_button_positions( listbox );
417 
418     return listbox;
419 }
420 
421 
422 /*---------------------------------------------------------------------------*/
423 /*!
424   Deletes a listbox
425   \return  None
426   \author  jfpatry
427   \date    Created:  2000-09-17
428   \date    Modified: 2000-09-17
429 */
listbox_delete(listbox_t * listbox)430 void listbox_delete( listbox_t *listbox )
431 {
432     check_assertion( listbox != NULL, "listbox is NULL" );
433 
434     listbox_set_visible( listbox, False );
435     listbox_set_active( listbox, False );
436 
437     button_delete( listbox->up_button );
438     button_delete( listbox->down_button );
439 
440     free(listbox);
441 }
442 
443 
444 
445 /*---------------------------------------------------------------------------*/
446 /*!
447   Registers a callback to be called when the current item changes
448   \author  jfpatry
449   \date    Created:  2000-09-20
450   \date    Modified: 2000-09-20
451 */
listbox_set_item_change_event_cb(listbox_t * listbox,listbox_item_change_event_cb_t cb,void * userdata)452 void listbox_set_item_change_event_cb( listbox_t *listbox,
453 				       listbox_item_change_event_cb_t cb,
454 				       void *userdata )
455 {
456     check_assertion( listbox != NULL, "listbox is NULL" );
457 
458     listbox->item_change_cb = cb;
459     listbox->item_change_cb_userdata = userdata;
460 }
461 
462 
463 /*---------------------------------------------------------------------------*/
464 /*!
465   Sets the item list for the listbox
466   \author  jfpatry
467   \date    Created:  2000-09-23
468   \date    Modified: 2000-09-23
469 */
listbox_set_item_list(listbox_t * listbox,list_t item_list,listbox_list_elem_to_string_fptr_t func)470 void listbox_set_item_list( listbox_t *listbox,
471 			    list_t item_list,
472 			    listbox_list_elem_to_string_fptr_t func )
473 {
474     check_assertion( listbox != NULL, "listbox is NULL" );
475 
476     listbox->item_list = item_list;
477     listbox->cur_item = get_list_head(item_list);
478     listbox->label_gen_func = func;
479 
480     update_button_enabled_states( listbox );
481 
482     ui_set_dirty();
483 }
484 
485 /*---------------------------------------------------------------------------*/
486 /*!
487   Returns the listbox's current item
488   \return  The currently selected listbox item
489   \author  jfpatry
490   \date    Created:  2000-09-17
491   \date    Modified: 2000-09-17
492 */
listbox_get_current_item(listbox_t * listbox)493 list_elem_t listbox_get_current_item( listbox_t *listbox )
494 {
495     check_assertion( listbox != NULL, "listbox is NULL" );
496 
497     return listbox->cur_item;
498 }
499 
500 
501 
502 /*---------------------------------------------------------------------------*/
503 /*!
504   Sets the current item in the listbox
505   \author  jfpatry
506   \date    Created:  2000-09-21
507   \date    Modified: 2000-09-21
508 */
listbox_set_current_item(listbox_t * listbox,list_elem_t item)509 void listbox_set_current_item( listbox_t *listbox, list_elem_t item )
510 {
511     check_assertion( listbox != NULL, "listbox is NULL" );
512 
513     listbox->cur_item = item;
514 
515     update_button_enabled_states( listbox );
516 }
517 
518 
519 
520 /*---------------------------------------------------------------------------*/
521 /*!
522   Sets the position of the listbox
523   \return  None
524   \author  jfpatry
525   \date    Created:  2000-09-17
526   \date    Modified: 2000-09-17
527 */
listbox_set_position(listbox_t * listbox,point2d_t pos)528 void listbox_set_position( listbox_t *listbox, point2d_t pos )
529 {
530     check_assertion( listbox != NULL, "listbox is NULL" );
531 
532     listbox->pos = pos;
533     update_button_positions( listbox );
534 }
535 
536 
537 
538 /*---------------------------------------------------------------------------*/
539 /*!
540   Returns the width of the listbox
541   \return  The width of the listbox
542   \author  jfpatry
543   \date    Created:  2000-09-17
544   \date    Modified: 2000-09-17
545 */
listbox_get_width(listbox_t * listbox)546 scalar_t listbox_get_width( listbox_t *listbox )
547 {
548     check_assertion( listbox != NULL, "listbox is NULL" );
549 
550     return listbox->w;
551 }
552 
553 
554 /*---------------------------------------------------------------------------*/
555 /*!
556   Returns the height of the listbox
557   \return  The height of the listbox
558   \author  jfpatry
559   \date    Created:  2000-09-17
560   \date    Modified: 2000-09-17
561 */
listbox_get_height(listbox_t * listbox)562 scalar_t listbox_get_height( listbox_t *listbox )
563 {
564     check_assertion( listbox != NULL, "listbox is NULL" );
565 
566     return listbox->h;
567 }
568 
569 
570 
571 /*---------------------------------------------------------------------------*/
572 /*!
573   Sets the visibility of the listbox.
574   Also sets the active state to the value of \c visible.
575   \return  None
576   \author  jfpatry
577   \date    Created:  2000-09-17
578   \date    Modified: 2000-09-17
579 */
listbox_set_visible(listbox_t * listbox,bool_t visible)580 void listbox_set_visible( listbox_t *listbox, bool_t visible )
581 {
582     check_assertion( listbox != NULL, "listbox is NULL" );
583 
584     /* This check is necessary to prevent infinite mutual recursion */
585     if ( listbox->active != visible ) {
586 	listbox_set_active( listbox, visible );
587     }
588 
589     if ( !listbox->visible && visible ) {
590 	ui_add_widget_draw_callback( listbox, listbox_draw_cb );
591 
592 	ui_set_dirty();
593     } else if ( listbox->visible && !visible ) {
594 	ui_delete_widget_draw_callback( listbox );
595 
596 	ui_set_dirty();
597     }
598 
599     listbox->visible = visible;
600 }
601 
602 
603 /*---------------------------------------------------------------------------*/
604 /*!
605   Returns the visibility status of the specified listbox
606   \return  True iff listbox is visible
607   \author  jfpatry
608   \date    Created:  2000-09-17
609   \date    Modified: 2000-09-17
610 */
listbox_is_visible(listbox_t * listbox)611 bool_t listbox_is_visible( listbox_t *listbox )
612 {
613     check_assertion( listbox != NULL, "listbox is NULL" );
614 
615     return listbox->visible;
616 }
617 
618 
619 /*---------------------------------------------------------------------------*/
620 /*!
621   Sets the active state of the listbox button.
622   If \c active == False, then the listbox's visibility state is set to False.
623   \return  None
624   \author  jfpatry
625   \date    Created:  2000-09-18
626   \date    Modified: 2000-09-18
627 */
listbox_set_active(listbox_t * listbox,bool_t active)628 void listbox_set_active( listbox_t *listbox, bool_t active )
629 {
630     check_assertion( listbox != NULL, "listbox is NULL" );
631 
632     button_set_active( listbox->up_button, active );
633     button_set_active( listbox->down_button, active );
634 
635     listbox->active = active;
636 
637     if ( !active ) {
638 	listbox_set_visible( listbox, False );
639     }
640 
641 }
642 
643 
644 /*---------------------------------------------------------------------------*/
645 /*!
646   Returns the active state of the listbox
647   \return  Active state of listbox
648   \author  jfpatry
649   \date    Created:  2000-09-18
650   \date    Modified: 2000-09-18
651 */
listbox_is_active(listbox_t * listbox)652 bool_t listbox_is_active( listbox_t *listbox )
653 {
654     check_assertion( listbox != NULL, "listbox is NULL" );
655 
656     return listbox->active;
657 }
658 
659 
660 /* EOF */
661