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