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