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