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