]> icculus.org git repositories - taylor/freespace2.git/blob - src/ui/listbox.cpp
fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
[taylor/freespace2.git] / src / ui / listbox.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/UI/LISTBOX.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code to implement a listbox gadget.
16  *
17  * $Log$
18  * Revision 1.4  2004/07/04 11:39:06  taylor
19  * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
20  *
21  * Revision 1.3  2002/06/09 04:41:29  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:53  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:11  root
28  * Initial import.
29  *
30  * 
31  * 4     12/02/98 5:47p Dave
32  * Put in interface xstr code. Converted barracks screen to new format.
33  * 
34  * 3     10/13/98 9:29a Dave
35  * Started neatening up freespace.h. Many variables renamed and
36  * reorganized. Added AlphaColors.[h,cpp]
37  * 
38  * 2     10/07/98 10:54a Dave
39  * Initial checkin.
40  * 
41  * 1     10/07/98 10:51a Dave
42  * 
43  * 20    3/23/98 5:48p Hoffoss
44  * Improved listbox handling.  Most notibly the scrollbar arrows work now.
45  * 
46  * 19    2/03/98 4:21p Hoffoss
47  * Made UI controls draw white text when disabled.
48  * 
49  * 18    1/20/98 10:36a Hoffoss
50  * Fixed optimized warnings.
51  * 
52  * 17    1/14/98 6:43p Hoffoss
53  * Massive changes to UI code.  A lot cleaner and better now.  Did all
54  * this to get the new UI_DOT_SLIDER to work properly, which the old code
55  * wasn't flexible enough to handle.
56  * 
57  * 16    9/18/97 10:31p Lawrance
58  * allow listbox to change text
59  * 
60  * 15    9/09/97 4:32p Dave
61  * Added sel_changed() function to UI_LISTBOX to check is the selection
62  * has changed.
63  * 
64  * 14    9/09/97 3:39p Sandeep
65  * warning level 4 bugs
66  * 
67  * 13    8/19/97 1:27p Dave
68  * Made it possible to create an empty list box and summarily add items.
69  * 
70  * 12    8/14/97 5:23p Dave
71  * Added clear_all_items() to the UI_LISTBOX
72  * 
73  * 11    6/26/97 5:53p Lawrance
74  * change code slightly, so current_item is not forced to 0 when was -1
75  * 
76  * 10    6/12/97 12:39p John
77  * made ui use freespace colors
78  * 
79  * 9     6/11/97 1:13p John
80  * Started fixing all the text colors in the game.
81  * 
82  * 8     5/26/97 10:26a Lawrance
83  * get slider control working 100%
84  * 
85  * 7     4/22/97 10:11a John
86  * Added checkbox lists to listboxes
87  * 
88  * 6     4/15/97 3:47p Allender
89  * moved type selection of list box items into actual UI code.  Made it
90  * behave more like windows listboxes do
91  * 
92  * 5     12/23/96 2:42p Lawrance
93  * allowing keys to select list box items in the mission load screen
94  * 
95  * 4     12/02/96 2:17p John
96  * Made right button drag UI gadgets around and
97  * Ctrl+Shift+Alt+F12 dumps out where they are.
98  * 
99  * 3     12/01/96 3:48a Lawrance
100  * added function set_current to UI_LISTBOX
101  * 
102  * 2     11/15/96 11:43a John
103  * 
104  * 1     11/14/96 6:55p John
105  *
106  * $NoKeywords: $
107  */
108
109 #include "uidefs.h"
110 #include "ui.h"
111 #include "timer.h"
112 #include "key.h"
113 #include "alphacolors.h"
114
115 #define KEY_BUFFER_TIMEOUT              1000            // time to clear buffer in milliseconds
116
117 #define DEFAULT_LISTBOX_ITEM_LENGTH 40
118
119 // --------------------------------------------------------------------
120 // UI_LISTBOX::link_hotspot
121 //
122 //
123 void UI_LISTBOX::link_hotspot(int up_button_num, int down_button_num)
124 {
125         scrollbar.link_hotspot(up_button_num,down_button_num);
126 }
127
128 // --------------------------------------------------------------------
129 // UI_LISTBOX::set_bmaps
130 //
131 // Call the UI_SCROLLBAR::set_bmaps() function for the scroll bar gadget.
132 //
133 // returns:             -1 ==> error
134 //                                       0 ==> success
135 //
136 int UI_LISTBOX::set_bmaps(char *lbox_fname, char *b_up_fname, char *b_down_fname, char *sb_fname)
137 {
138         if (has_scrollbar) {
139                 scrollbar.set_bmaps(b_up_fname, b_down_fname, sb_fname);
140         }
141         
142         // set the bitmaps for the list box rectangle
143         UI_GADGET::set_bmaps(lbox_fname);
144         uses_bmaps = 1;
145         return 0;
146 }
147
148 void UI_LISTBOX::create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, int _numitems, char **_list, char *_check_list, int _max_items)
149 {
150         int tw, th, nrows;
151         int real_h;
152
153         gr_set_font(wnd->f_id);
154         gr_get_string_size(&tw, &th, "*");
155
156         nrows = _h / th;
157         real_h = nrows * th;
158
159         base_create( wnd, UI_KIND_LISTBOX, _x, _y, _w, real_h );
160
161    max_items = _max_items;      
162    list = _list;
163         num_items = _numitems;
164         check_list = _check_list;       
165         num_items_displayed = nrows;
166
167         first_item = 0;
168         current_item = -1;
169         toggled_item = -1;
170         last_scrolled = 0;
171         textheight = th;
172         dragging = 0;
173         selected_item = -1;
174         key_buffer_count = 0;
175         last_typed = timer_get_milliseconds();
176
177         if (_numitems > nrows) {
178                 scrollbar.create( wnd, _x+_w+3, _y, real_h, 0, _numitems-nrows, 0, nrows );
179                 scrollbar.set_parent(this);
180                 has_scrollbar = 1;
181
182         } else {
183                 has_scrollbar = 0;
184         }
185 };
186
187 void UI_LISTBOX::draw()
188 {
189         int i, x1, y1, stop;
190         int w1, h1;
191
192         UI_GADGET::draw();
193         gr_set_font(my_wnd->f_id);
194
195         if (uses_bmaps) {
196                 if (disabled_flag) {
197                         if ( bmap_ids[LBOX_DISABLED] >= 0 ) {
198                                 gr_set_bitmap(bmap_ids[LBOX_DISABLED]);
199                                 gr_bitmap(x, y);
200                         }
201
202                 } else {
203                         if ( bmap_ids[LBOX_NORMAL] >= 0 ) {
204                                 gr_set_bitmap(bmap_ids[LBOX_NORMAL]);
205                                 gr_bitmap(x, y);
206                         }
207                 }
208
209         } else {
210                 gr_set_color_fast(&CBLACK);
211                 gr_set_clip( x, y, w, h );
212                 ui_rect( 0, 0, w-1, h-1 );
213                 gr_reset_clip();                
214                 if (has_scrollbar) {
215                         ui_draw_sunken_border( x-2, y-2, x+w+scrollbar.w+4, y+h+1 );
216
217                 } else {
218                         ui_draw_sunken_border( x-2, y-2, x+w+4, y+h+1 );
219                 }
220         }
221
222         stop = first_item+num_items_displayed;
223         if (stop>num_items) stop = num_items;
224
225         x1 = y1 = 0;
226         gr_set_clip( x, y, w, h );
227
228         for ( i=first_item; i<stop; i++ ) {
229                 gr_get_string_size( &w1, &h1,list[i] );
230
231                 if (check_list)
232                         w1 += 18;
233
234                 if (i !=current_item) {
235 /*
236                         if ((current_item == -1) && (my_wnd->selected_gadget == this ) && (i == first_item)  )  {
237                                 if ( !uses_bmaps ) {
238                                         gr_set_color_fast( &CBLACK );
239                                         gr_rect( x1, y1, w1+2, h1 );
240                                 }
241                                 current_item = first_item;
242                                 gr_set_color_fast( &CBRIGHT_GREEN );
243                         } else {
244                                 if ( !uses_bmaps ) {
245                                         gr_set_color_fast( &CBLACK );
246                                         gr_rect( x1, y1, w1+2, h1 );
247                                 }
248                                 gr_set_color_fast( &CWHITE );
249                         }
250 */
251                         if (!uses_bmaps) {
252                                 gr_set_color_fast( &CBLACK );
253                                 gr_rect( x1, y1, w1+2, h1 );
254                         }
255
256                         gr_set_color_fast(&CWHITE);
257
258                 } else {
259                         if (my_wnd->selected_gadget == this) {
260                                 gr_set_color_fast( &CGRAY );
261                                 gr_rect( x1, y1, w1+2, h1 );
262                                 gr_set_color_fast( &CBRIGHT_GREEN );
263
264                         } else {
265                                 gr_set_color_fast( &CGRAY );
266                                 gr_rect( x1, y1, w1+2, h1 );
267                                 gr_set_color_fast( &CBLACK );
268                         }
269                 }
270
271                 if ( check_list )       {
272                         if ( check_list[i] )    {
273                                 gr_string( x1+2, y1, "X" );
274                         }
275
276                         gr_string( x1+16, y1, list[i] );
277
278                 } else
279                         gr_string( x1+2, y1, list[i] );
280
281                 if (i==current_item)
282                         gr_set_color_fast( &CGRAY );
283                 else
284                         gr_set_color_fast( &CBLACK );
285
286                 if ( !uses_bmaps ) {
287                         ui_rect( x1+w1+2, y1, w-1, y1+h1-1 );
288                         ui_rect( x1, y1, x1+1, y1+h1-1 );
289                 }
290
291                 y1 += h1;
292         }
293
294         if (stop < num_items_displayed-1 && !uses_bmaps) {
295                 gr_set_color_fast(&CBLACK);
296                 ui_rect( x1, y1, w-1, h-1 );
297         }
298 }
299
300 void UI_LISTBOX::process(int focus)
301 {
302         int OnMe, mitem, oldbarpos, kf = 0;
303         int i, j;
304
305         selected_item = -1;
306         toggled_item = -1;
307
308         if (disabled_flag)
309                 return;
310
311         if (my_wnd->selected_gadget == this)
312                 focus = 1;
313
314         if (has_scrollbar) {
315                 scrollbar.process(0);
316                 if (my_wnd->selected_gadget == &scrollbar) {
317                         set_focus();
318                         focus = 1;
319                 }
320         }
321
322         if (num_items < 1) {
323                 current_item = -1;
324                 first_item = 0;
325                 old_current_item = current_item;
326                 old_first_item = first_item;
327                 
328 //              if (my_wnd->selected_gadget == this) {
329 //                      my_wnd->selected_gadget == get_next();
330 //              }
331
332                 return;
333         }
334
335         old_current_item = current_item;
336         old_first_item = first_item;
337
338         OnMe = is_mouse_on();
339
340         if (has_scrollbar) {
341                 if (scrollbar.moved) {
342                         first_item = scrollbar.position;
343                         Assert(first_item >= 0);
344
345                         if (current_item<first_item)
346                                 current_item = first_item;
347
348                         if (current_item > first_item + num_items_displayed - 1)
349                                 current_item = first_item + num_items_displayed - 1;
350                 }
351         }
352
353         if (!B1_PRESSED)
354                 dragging = 0;
355
356         if (B1_PRESSED && OnMe) {
357                 set_focus();
358                 dragging = 1;
359         }
360
361         if ( key_buffer_count && (timer_get_milliseconds() > last_typed + KEY_BUFFER_TIMEOUT) )
362                 key_buffer_count = 0;
363
364         if (focus) {
365                 if (my_wnd->keypress) {
366                         kf = 0;
367
368                         switch (my_wnd->keypress) {
369                                 case KEY_ENTER:
370                                         selected_item = current_item;
371                                         break;
372
373                                 case KEY_SPACEBAR:
374                                         toggled_item = current_item;
375                                         break;
376
377                                 case KEY_UP:
378                                         current_item--;
379                                         kf = 1;
380                                         break;
381
382                                 case KEY_DOWN:
383                                         current_item++;
384                                         kf = 1;
385                                         break;
386
387                                 case KEY_HOME:
388                                         current_item = 0;
389                                         kf = 1;
390                                         break;
391
392                                 case KEY_END:
393                                         current_item=num_items - 1;
394                                         kf = 1;
395                                         break;
396
397                                 case KEY_PAGEUP:
398                                         current_item -= num_items_displayed;
399                                         kf = 1;
400                                         break;
401
402                                 case KEY_PAGEDOWN:
403                                         current_item += num_items_displayed;
404                                         kf = 1;
405                                         break;
406
407                                 default:                // enter the key in the key buffer
408                                         if (my_wnd->keypress == KEY_BACKSP) {
409                                                 key_buffer_count = 0;
410
411                                         } else if (key_buffer_count < MAX_KEY_BUFFER) {
412                                                 key_buffer[key_buffer_count++] = (char) my_wnd->keypress;
413                                                 last_typed = timer_get_milliseconds();
414                                         }
415
416                                         if (!key_buffer_count)
417                                                 break;
418
419                                         for (i=0; i<num_items; i++) {
420                                                 char *current_text;
421                                                 
422                                                 current_text = get_string(i);
423                                                 for (j=0; j<key_buffer_count; j++)
424                                                         if ( (current_text[j] != ascii_table[(int)key_buffer[j]]) && (current_text[j] != shifted_ascii_table[(int)key_buffer[j]]) )
425                                                                 break;
426
427                                                 if (j == key_buffer_count) {
428                                                         set_first_item(i - num_items_displayed / 2);
429                                                         set_current(i);
430                                                         break;
431                                                 }
432                                         }
433                         }
434                 }
435
436                 if (kf == 1) {
437                         if (current_item < 0)
438                                 current_item = 0;
439
440                         if (current_item >= num_items)
441                                 current_item = num_items - 1;
442
443                         if (current_item < first_item)
444                                 first_item = current_item;
445
446                         if (current_item >= first_item + num_items_displayed)
447                                 first_item = current_item - num_items_displayed + 1;
448
449                         if (num_items <= num_items_displayed ) {
450                                 first_item = 0;
451
452                         } else {
453                                 if (has_scrollbar) {
454                                         oldbarpos = scrollbar.position;
455                                         scrollbar.position = first_item;
456
457                                         scrollbar.bar_position = scrollbar.position - scrollbar.start;
458                                         scrollbar.bar_position *= scrollbar.h - scrollbar.bar_size;
459                                         scrollbar.bar_position /= scrollbar.stop - scrollbar.start;
460
461                                         if (scrollbar.bar_position < 0) {
462                                                 scrollbar.bar_position = 0;
463                                         }
464                 
465                                         if (scrollbar.bar_position > scrollbar.h - scrollbar.bar_size) {
466                                                 scrollbar.bar_position = scrollbar.h - scrollbar.bar_size;
467                                         }
468                                 }       
469         
470                         }
471                 }
472         }
473
474         if (focus) {
475                 if (B1_PRESSED && dragging) {
476                         if (ui_mouse.y < y )
477                                 mitem = -1;
478                         else
479                                 mitem = (ui_mouse.y - y)/textheight;
480
481                         if ( (mitem < 0) && (timer_get_milliseconds() > last_scrolled + 1000 / 18) ) {
482                                 current_item--;
483                                 last_scrolled = timer_get_milliseconds();
484                         }
485
486                         if ( (mitem >= num_items_displayed) && (timer_get_milliseconds() > last_scrolled + 1000 / 18) ) {
487                                 current_item++;
488                                 last_scrolled = timer_get_milliseconds();
489                         }
490
491                         if ((mitem >= 0) && (mitem<num_items_displayed)) {
492                                 current_item = mitem + first_item;
493                         }
494
495                         if (current_item < 0)
496                                 current_item = 0;
497
498                         if (current_item >= num_items)
499                                 current_item = num_items - 1;
500
501                         if (current_item < first_item)
502                                 first_item = current_item;
503
504                         if (current_item >= first_item + num_items_displayed)
505                                 first_item = current_item - num_items_displayed + 1;
506
507                         if (num_items <= num_items_displayed) {
508                                 first_item = 0;
509
510                         } else if (has_scrollbar) {
511                                 oldbarpos = scrollbar.position;
512                                 scrollbar.position = first_item;
513
514                                 scrollbar.bar_position = scrollbar.position - scrollbar.start;
515                                 scrollbar.bar_position *= scrollbar.h - scrollbar.bar_size;
516                                 scrollbar.bar_position /= scrollbar.stop - scrollbar.start;
517
518                                 if (scrollbar.bar_position < 0) {
519                                         scrollbar.bar_position = 0;
520                                 }
521
522                                 if (scrollbar.bar_position > scrollbar.h - scrollbar.bar_size) {
523                                         scrollbar.bar_position = scrollbar.h - scrollbar.bar_size;
524                                 }
525                         }
526                 }
527
528                 if (check_list) {
529                         if (B1_JUST_RELEASED)
530                                 toggled_item = current_item;
531                 }
532
533                 if (B1_DOUBLE_CLICKED) {
534                         selected_item = current_item;
535                 }
536         }
537 }
538
539 int UI_LISTBOX::toggled()
540 {
541         if (check_list) {
542                 return toggled_item;
543         } else {
544                 return -1;
545         }
546 }
547
548 int UI_LISTBOX::selected()
549 {
550         if (check_list) {
551                 return -1;
552         } else {
553                 return selected_item;
554         }
555 }
556
557 int UI_LISTBOX::current()
558 {
559         return current_item;
560 }
561
562 void UI_LISTBOX::set_current(int _index)
563 {
564         current_item = _index;
565 }
566
567 void UI_LISTBOX::set_first_item(int _index)
568 {
569         if (_index < 0)
570                 _index = 0;
571         else if (_index > num_items)
572                 _index = num_items;
573
574         first_item = _index;
575 }
576
577 char *UI_LISTBOX::get_string(int _index)
578 {
579         return list[_index];
580 }
581
582 void UI_LISTBOX::clear_all_items()
583 {
584    int idx;
585
586         for ( idx=0; idx<num_items; idx++ )
587                 list[idx][0] = 0;
588
589         num_items = 0;
590         first_item = 0;
591 }
592
593 void UI_LISTBOX::set_new_list(int _numitems, char **_list)
594 {
595         num_items = _numitems;
596         list = _list;
597         current_item = 0;
598 }
599
600 int UI_LISTBOX::add_string(char *str)
601 {
602    if (max_items < 0)  // only if we created an "empty" listbox can we add items
603                 return 0;
604
605         else {
606                 if ( (num_items == max_items - 1) || (strlen(str) > DEFAULT_LISTBOX_ITEM_LENGTH) )
607                         return 0;                     // we've reached our limit
608
609                 else {
610                         list[num_items] = strdup(str);
611                         num_items++;
612                         return 1;
613                 }
614         }
615 }
616
617 int UI_LISTBOX::sel_changed()
618 {
619         return old_current_item == current_item ? 0 : 1;
620 }
621