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