]> icculus.org git repositories - taylor/freespace2.git/blob - src/ui/inputbox.cpp
Initial revision
[taylor/freespace2.git] / src / ui / inputbox.cpp
1 /*
2  * $Logfile: /Freespace2/code/Ui/INPUTBOX.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Code to implement input boxes
8  *
9  * $Log$
10  * Revision 1.1  2002/05/03 03:28:11  root
11  * Initial revision
12  *
13  * 
14  * 8     9/07/99 4:01p Dave
15  * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX
16  * does everything properly (setting up address when binding). Remove
17  * black rectangle background from UI_INPUTBOX.
18  * 
19  * 7     7/15/99 7:14p Jefff
20  * 
21  * 6     6/25/99 11:59a Dave
22  * Multi options screen.
23  * 
24  * 5     2/11/99 3:08p Dave
25  * PXO refresh button. Very preliminary squad war support.
26  * 
27  * 4     12/02/98 5:47p Dave
28  * Put in interface xstr code. Converted barracks screen to new format.
29  * 
30  * 3     10/13/98 9:29a Dave
31  * Started neatening up freespace.h. Many variables renamed and
32  * reorganized. Added AlphaColors.[h,cpp]
33  * 
34  * 2     10/07/98 10:54a Dave
35  * Initial checkin.
36  * 
37  * 1     10/07/98 10:51a Dave
38  * 
39  * 43    5/14/98 6:29p Hoffoss
40  * Fixed some warnings a release rebuild all turned up.
41  * 
42  * 42    4/14/98 5:06p Dave
43  * Don't load or send invalid pilot pics. Fixed chatbox graphic errors.
44  * Made chatbox display team icons in a team vs. team game. Fixed up pause
45  * and endgame sequencing issues.
46  * 
47  * 41    4/12/98 5:31p Lawrance
48  * use timer_get_milliseconds() instead of gettime()
49  * 
50  * 40    4/10/98 5:36p Dave
51  * Put in user notification of illegal values in multi host options
52  * screen. Fixed server respawn ship class problem.
53  * 
54  * 39    4/09/98 3:10p Mike
55  * Fix shockingly stupid typo.
56  * 
57  * 38    4/09/98 2:46p Mike
58  * Fix bug preventing first letter in pilot name being capitalized.
59  * 
60  * 37    4/09/98 12:12p Mike
61  * Separate versioning for demo and full versions.
62  * Fix inputbox bugs.
63  * 
64  * 36    4/06/98 5:14p Hoffoss
65  * Added some needed asserts.
66  * 
67  * 35    4/06/98 4:25p Allender
68  * Fix strncpy bug in inputbox
69  * 
70  * 34    4/06/98 3:58p Frank
71  * added initialization to try and fix a bug.
72  * 
73  * 33    4/01/98 10:26a Hoffoss
74  * Changed input box code to not draw a cursor if the input box is
75  * disabled.
76  * 
77  * 32    3/27/98 4:01p Hoffoss
78  * Fixed bug where input box text wsn't being null terminated!  Bad!
79  * 
80  * 31    3/27/98 3:04p Mitri
81  * Fixed bug in set_text().  'length' is the number of characters allowed,
82  * EXCLUDING the null terminator.
83  * 
84  * 30    3/27/98 11:20a Hoffoss
85  * Changed input to use bright white text instead of bright green.
86  * 
87  * 29    3/10/98 11:29a Hoffoss
88  * Make first_time ignored and text not disappear after a return is
89  * pressed.
90  * 
91  * 28    2/26/98 4:21p Dave
92  * More robust multiplayer voice.
93  * 
94  * 27    1/23/98 5:43p Dave
95  * Finished bringing standalone up to speed. Coded in new host options
96  * screen.
97  * 
98  * 26    1/20/98 10:36a Hoffoss
99  * Fixed optimized warnings.
100  * 
101  * 25    1/17/98 5:51p Dave
102  * Bug fixes for bugs generated by multiplayer testing.
103  * 
104  * 24    1/16/98 7:57p Lawrance
105  * support animating input box cursor
106  * 
107  * 23    1/15/98 5:12p Hoffoss
108  * Fixed inputbox so clicking on it gives it focus.
109  * 
110  * 22    1/15/98 5:10p Allender
111  * ton of interface changes.  chatbox in multiplayer now behaves
112  * differently than before.  It's always active in any screen that uses
113  * it.  Only non-printatble characters will get passed back out from
114  * chatbox
115  * 
116  * 21    1/14/98 6:43p Hoffoss
117  * Massive changes to UI code.  A lot cleaner and better now.  Did all
118  * this to get the new UI_DOT_SLIDER to work properly, which the old code
119  * wasn't flexible enough to handle.
120  * 
121  * 20    1/05/98 1:15p John
122  * Made inputbox skip the initial "selected" phase when you first tab to
123  * it.
124  * 
125  * 19    12/22/97 5:08p Hoffoss
126  * Changed inputbox class to be able to accept only certain keys, changed
127  * pilot screens to utilize this feature.  Added to assert with pilot file
128  * saving.
129  * 
130  * 18    12/11/97 8:15p Dave
131  * Put in network options screen. Xed out olf protocol selection screen.
132  * 
133  * 17    12/08/97 6:22p Lawrance
134  * blink cursor on inputbox
135  * 
136  * 16    12/06/97 4:27p Dave
137  * Another load of interface and multiplayer bug fixes.
138  * 
139  * 15    11/25/97 3:51p Hoffoss
140  * Changed edit background rect position slightly.
141  * 
142  * 14    10/24/97 10:58p Hoffoss
143  * Made some changes to the UI code to do some things I need it to do.
144  * 
145  * 13    10/09/97 11:05a Allender
146  * ignore controled and alted keys in input box
147  * 
148  * 12    10/01/97 4:39p Lawrance
149  * null out text when free'ed
150  * 
151  * 11    9/09/97 3:39p Sandeep
152  * warning level 4 bugs
153  * 
154  * 10    8/21/97 12:13p Dave
155  * Made it possible for input box to ignore esc to lose focus.
156  * 
157  * 9     8/19/97 1:28p Dave
158  * Made it possible to limit characters by pixel width.
159  * 
160  * 8     8/15/97 8:21p Dave
161  * Modified UI_INPUTBOX so that it is possible to draw it invisibly. That
162  * is, only the text is displayed.
163  * 
164  * 7     8/11/97 9:48p Lawrance
165  * reset clip after drawing
166  * 
167  * 6     7/01/97 2:10p Dave
168  * Fixed bug which caused the box to stop responding after user called
169  * set_text(...) with a 0 length string.
170  * 
171  * 5     6/12/97 12:39p John
172  * made ui use freespace colors
173  * 
174  * 4     6/11/97 1:13p John
175  * Started fixing all the text colors in the game.
176  * 
177  * 3     12/03/96 3:46p Lawrance
178  * added ability to set contents of input box
179  * 
180  * 2     11/15/96 11:43a John
181  * 
182  * 1     11/15/96 8:21a John
183  *
184  * $NoKeywords: $
185  */
186
187 #include <ctype.h>
188 #include "uidefs.h"
189 #include "ui.h"
190 #include "bmpman.h"
191 #include "timer.h"
192 #include "alphacolors.h"
193
194
195 #define INPUTBOX_PASSWD_CHAR        '*'   // the password protected char
196
197 //      Retuen true if c is a letter, else return false.
198 int is_letter(char c)
199 {
200         return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
201 }
202
203 // if the passed key is keypad number, return the ascii value, otherwise -1
204 int keypad_to_ascii(int c)
205 {
206         switch(c){
207         case KEY_PAD0:
208                 return key_to_ascii(KEY_0);
209                 break;
210         case KEY_PAD1:
211                 return key_to_ascii(KEY_1);
212                 break;
213         case KEY_PAD2:
214                 return key_to_ascii(KEY_2);
215                 break;
216         case KEY_PAD3:
217                 return key_to_ascii(KEY_3);
218                 break;
219         case KEY_PAD4:
220                 return key_to_ascii(KEY_4);
221                 break;
222         case KEY_PAD5:
223                 return key_to_ascii(KEY_5);
224                 break;
225         case KEY_PAD6:
226                 return key_to_ascii(KEY_6);
227                 break;
228         case KEY_PAD7:
229                 return key_to_ascii(KEY_7);
230                 break;
231         case KEY_PAD8:
232                 return key_to_ascii(KEY_8);
233                 break;
234         case KEY_PAD9:
235                 return key_to_ascii(KEY_9);
236                 break;
237         case KEY_PADPERIOD:
238                 return key_to_ascii(KEY_PERIOD);
239                 break;
240         default :
241                 return -1;
242                 break;
243         }
244 }
245
246 // insert character c into string s at position p.
247 void strcins(char *s, int p, char c)
248 {
249         int n;
250         for (n=strlen(s)-p; n>=0; n-- )
251                 *(s+p+n+1) = *(s+p+n);   // Move everything over        
252         *(s+p) = c;         // then insert the character
253 }
254
255 // delete n character from string s starting at position p
256
257 void strndel(char *s, int p, int n)
258 {
259         for (; (*(s+p) = *(s+p+n)) != '\0'; s++ )
260                 *(s+p+n) = '\0';    // Delete and zero fill
261 }
262
263 void UI_INPUTBOX::init_cursor()
264 {
265         cursor_first_frame = bm_load_animation("cursor1", &cursor_nframes, &cursor_fps);
266         if ( cursor_first_frame < 0 ) {
267                 Warning(LOCATION,"Cannot load input box cursor: cursor1.ani\n");
268                 return;
269         }
270         cursor_elapsed_time=0;
271         cursor_current_frame=0;
272 }
273
274 void UI_INPUTBOX::create(UI_WINDOW *wnd, int _x, int _y, int _w, int _text_len, char *_text, int _flags, int pixel_lim, color *clr)
275 {
276         int tw, th;
277
278         Assert(_text_len >= 0);
279         Assert((int) strlen(_text) <= _text_len);
280         gr_set_font(wnd->f_id);
281         gr_get_string_size( &tw, &th, "*" );
282
283         // check to see if the user passed in a text color otherwise use the default green color
284         if (clr){
285                 text_color = clr;
286         } else {
287                 text_color = &CBRIGHT;
288         }
289
290         base_create( wnd, UI_KIND_INPUTBOX, _x, _y, _w, th+4 );
291         text = (char *) malloc( _text_len + 1);
292
293         // input boxes no longer use background
294         _flags |= UI_INPUTBOX_FLAG_NO_BACK;
295         
296         // if its in "password" mode, allocate a second string
297         // and copy it
298         if (_flags & UI_INPUTBOX_FLAG_PASSWD) {
299                 passwd_text = (char *) malloc(_text_len + 1);
300                 memset(passwd_text, INPUTBOX_PASSWD_CHAR, strlen(_text));
301                 passwd_text[strlen(_text)] = 0;
302
303         } else {
304                 passwd_text = NULL;
305         }
306
307         init_cursor();
308
309         if ( _text_len > 0 ) {
310                 strncpy( text, _text, _text_len );
311         }
312         text[_text_len] = 0;
313         position = strlen(_text);
314         oldposition = position;
315         length = _text_len;
316         pressed_down = 0;
317 //      first_time = 1;
318         changed_flag = 0;
319         flags = _flags;
320         pixel_limit = pixel_lim;
321         locked = 0;
322         valid_chars = NULL;
323         invalid_chars = NULL;
324 };
325
326 void UI_INPUTBOX::set_valid_chars(char *vchars)
327 {
328         // free up any existing string
329         if(valid_chars != NULL){
330                 free(valid_chars);
331                 valid_chars = NULL;
332         }
333
334         valid_chars = strdup(vchars);
335 }
336
337 void UI_INPUTBOX::set_invalid_chars(char *ichars)
338 {
339         // free up any existing string
340         if(invalid_chars != NULL){
341                 free(invalid_chars);
342                 invalid_chars = NULL;
343         }
344         
345         invalid_chars = strdup(ichars);
346 }
347
348 void UI_INPUTBOX::destroy()
349 {
350         if (text) {
351                 free(text);
352                 text = NULL;
353         }
354
355         // free any valid chars
356         if(valid_chars != NULL){
357                 free(valid_chars);
358                 valid_chars = NULL;
359         }
360
361         // free any invalid chars
362         if(invalid_chars != NULL){
363                 free(invalid_chars);
364                 invalid_chars = NULL;
365         }
366
367         if ((flags & UI_INPUTBOX_FLAG_PASSWD) && passwd_text) {
368                 free(passwd_text);
369                 passwd_text = NULL;
370         }
371
372         UI_GADGET::destroy();
373 }
374
375 void UI_INPUTBOX::draw()
376 {
377         int invis, w1, h1, tw, th;
378         int text_x, text_y;
379
380         if (hidden){
381                 return;
382         }
383
384         w1 = w;
385         h1 = h;
386         invis = flags & UI_INPUTBOX_FLAG_INVIS;
387
388         gr_set_font(my_wnd->f_id);
389         gr_reset_clip();
390         if (!invis && !(flags & UI_INPUTBOX_FLAG_NO_BACK)) {
391                 // draw the entire text box region
392                 ui_draw_sunken_border( x-2, y-2, x+w+1, y+h+1 );
393                 gr_set_color_fast( &CBLACK );
394                 gr_rect( 0, 0, w, h );
395                 w1 -= 4;
396                 h1 -= 4;
397                 gr_set_clip( x + 1, y + 1, w1 + 1, h1 + 1 );
398         } else {
399                 gr_set_clip( x - 1, y - 1, w1 + 1, h1 + 1 );
400         }
401
402         if (flags & UI_INPUTBOX_FLAG_PASSWD){
403                 gr_get_string_size(&tw, &th, passwd_text);
404         } else {
405                 gr_get_string_size(&tw, &th, text);
406         }
407
408         // If first_time is set, that means this input box got
409         // focus, but nothing is typed yet, so all the text is
410         // selected, if you type a character it will replace the
411         // text, if you type an arrow it will unselect it.
412         // So it needs to be colored differently to show this.
413         if (!disabled_flag && !(flags & UI_INPUTBOX_FLAG_NO_BACK)) {
414 //              if ( (my_wnd->selected_gadget == this) && first_time ) {
415 //                      gr_set_color_fast( text_color );
416
417 //              } else {
418                         gr_set_color_fast( &CBLACK );
419 //              }
420
421                 // color the background behind the text 
422                 gr_rect( 0, 0, tw + 1, th );
423         }
424
425         if      ( (my_wnd->selected_gadget == this) || disabled_flag ) {                
426                 gr_set_color_fast(text_color);
427         } else {
428                 gr_set_color_fast(&CWHITE);
429         }
430
431         // coords of where to draw the text
432         text_x = 1;
433         text_y = 1;
434         if(flags & UI_INPUTBOX_FLAG_TEXT_CEN){
435                 // if we fit within the text area, draw it centered
436                 if(tw <= w1 - 5){
437                         text_x += (w1 - tw)/2;
438                 }               
439         }
440
441         // draw the text
442         if (flags & UI_INPUTBOX_FLAG_PASSWD){
443                 gr_string(text_x, text_y, passwd_text);
444         } else {
445                 gr_string(text_x, text_y, text);
446         }
447
448         // draw the "cursor"
449         if (!disabled_flag) {
450                 if (my_wnd->selected_gadget == this) {
451                         if (cursor_first_frame == -1) {
452                                 gr_set_color_fast(text_color);
453                                 ui_vline(1, h1, text_x + tw + 4);
454                                 ui_vline(1, h1, text_x + tw + 5);
455                         } else {
456                                 // draw animating cursor
457                                 int time_delta = timer_get_milliseconds() - cursor_elapsed_time;
458
459                                 if ( (time_delta / 1000.0f) > (1.0f / cursor_fps) ) {
460                                         // advance frame
461                                         cursor_elapsed_time += time_delta;
462                                         cursor_current_frame++;
463                                         if (cursor_current_frame >= cursor_nframes) {
464                                                 cursor_current_frame = 0;
465                                         }
466                                 }
467
468                                 // draw current frame
469                                 gr_set_bitmap(cursor_first_frame + cursor_current_frame);
470                                 gr_bitmap(text_x + tw + 4, 1);
471                         }
472                 }
473         }
474
475         gr_reset_clip();
476 }
477
478 int UI_INPUTBOX::validate_input(int chr)
479 {
480         if (chr < 32) {  // weed out control characters
481                 return 0;
482         }
483
484         // if we're disallowing letters altogether
485         if((flags & UI_INPUTBOX_FLAG_NO_LETTERS) && isalpha(chr)){
486                 return 0;
487         }
488
489         // if we're disallowing numbers altogether
490         if((flags & UI_INPUTBOX_FLAG_NO_NUMERALS) && isdigit(chr)){
491                 return 0;
492         }
493
494         // otherwise allow numbers and alpha chars by
495         if (isdigit(chr) || isalpha(chr)){
496                 return chr;
497         }
498
499         // if we have specified no valid or invalid chars, accept everything
500         if(!valid_chars && !invalid_chars){
501                 return chr;
502         }
503
504         // otherwise compare against the valid chars list
505         if((valid_chars) && strchr(valid_chars, chr)){
506                 return chr;
507         }
508
509         // otherwise compare against the invalid chars list0
510         if((invalid_chars) && !strchr(invalid_chars,chr)){
511                 return chr;
512         }
513
514         return 0;
515 }
516
517 void UI_INPUTBOX::process(int focus)
518 {
519         int ascii, clear_lastkey, key, key_used, key_check;     
520
521         // check if mouse is pressed
522         if (B1_PRESSED && is_mouse_on()) {
523                 set_focus();
524 //              first_time = 1;
525         }
526
527         if (disabled_flag)
528                 return;
529
530         if (my_wnd->selected_gadget == this)
531                 focus = 1;
532 //      else
533 //              first_time = 0;
534
535         key_used = 0;
536         changed_flag = 0;
537         oldposition = position;
538         pressed_down = 0;
539         clear_lastkey = (flags & UI_INPUTBOX_FLAG_KEYTHRU) ? 0 : 1;
540
541         if (focus) {
542                 key = my_wnd->keypress;
543                 switch (key) {
544                         case 0:
545                                 break;
546
547                         //case KEY_LEFT:
548                         case KEY_BACKSP:
549                                 if (position > 0)
550                                         position--;
551
552                                 text[position] = 0;
553                                 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
554                                         passwd_text[position] = 0;
555                                 }
556
557                                 changed_flag = 1;
558                                 key_used = 1;
559 //                              if (first_time)
560 //                                      first_time = 0;
561
562                                 break;
563
564                         case KEY_ENTER:
565                                 pressed_down = 1;
566                                 locked = 0;
567                                 changed_flag = 1;
568                                 key_used = 1;
569 //                              if (first_time)
570 //                                      first_time = 0;
571
572 //                              should_reset = 1;
573                                 break;
574
575                         case KEY_ESC:
576                                 if (flags & UI_INPUTBOX_FLAG_ESC_CLR){
577                                         if (position > 0) {
578                                                 set_text("");
579                                                 key_used = 1;                                           
580
581                                         } else {
582                                                 key_used = 0;
583                                                 clear_lastkey = 0;
584                                         }
585                                 }
586
587                                 if (flags & UI_INPUTBOX_FLAG_ESC_FOC) {
588                                         clear_focus();
589                                 }
590
591                                 break;
592
593                         default:
594                                 if (!locked) {
595                                         // MWA -- determine if alt or ctrl held down on this key and don't process if it is.  We
596                                         // need to be able to pass these keys back to the top level.  (And anyway -- ctrl-a shouldn't
597                                         // print out an A in the input window
598                                         if ( key & (KEY_ALTED | KEY_CTRLED) ) {
599                                                 clear_lastkey = 0;
600                                                 break;
601                                         }
602
603                                         // get an ascii char from the input if possible
604                                         key_check = keypad_to_ascii(key);
605                                         if(key_check == -1){
606                                                 key_check = key_to_ascii(key);
607                                         }
608
609                                         ascii = validate_input(key_check);
610                                         if ((ascii > 0) && (ascii < 255)) {
611                                                 if (flags & UI_INPUTBOX_FLAG_LETTER_FIRST) {
612                                                         if ((position == 0) && !is_letter((char) ascii))
613                                                                 break;
614                                                 }
615
616                                                 key_used = 1;
617 //                                              if (should_reset) {
618 //                                                      should_reset = 0;
619 //                                                      position = 0;
620 //                                              }
621
622 //                                              if (first_time) {
623 //                                                      first_time = 0;
624 //                                                      position = 0;
625 //                                              }
626
627                                                 if ( position < length ) {
628                                                         text[position] = (char) ascii;
629                                                         text[position + 1] = 0;
630
631                                                         if (flags & UI_INPUTBOX_FLAG_PASSWD) {
632                                                                 passwd_text[position] = (char) INPUTBOX_PASSWD_CHAR;
633                                                                 passwd_text[position + 1] = 0;
634                                                         }
635
636                                                         position++;
637
638                                                         // check to see if we should limit by pixel width
639                                                         if (pixel_limit > -1) {
640                                                                 int w;
641
642                                                                 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
643                                                                         gr_get_string_size(&w, NULL, passwd_text);                                                                      
644
645                                                                 } else {
646                                                                         gr_get_string_size(&w, NULL, text);                                                             
647                                                                 }
648
649                                                                 if (w > pixel_limit) {
650                                                                         position--;
651                                                                         locked = 1;
652                                                                         text[position] = 0;
653
654                                                                         if (flags & UI_INPUTBOX_FLAG_PASSWD) {
655                                                                                 passwd_text[position] = 0;
656                                                                         }
657                                                                 }
658                                                         }
659                                                 }
660
661                                                 changed_flag = 1;
662                                         }
663                                 }
664
665                                 break;
666                 }
667
668                 if (clear_lastkey || (key_used && (flags & UI_INPUTBOX_FLAG_EAT_USED)) )
669                         my_wnd->last_keypress=0;
670
671 //      } else {
672 //              first_time = 1;
673         }       
674 }
675
676 int UI_INPUTBOX::changed()
677 {               
678         return changed_flag;
679 }
680
681 int UI_INPUTBOX::pressed()
682 {       
683         return pressed_down;
684 }
685
686 void UI_INPUTBOX::get_text(char *out)
687 {
688         strncpy(out, text, length);
689         out[length] = 0;
690 }
691
692 void UI_INPUTBOX::set_text(char *in)
693 {
694         int in_length;
695         
696         in_length = strlen(in);
697         if (in_length > length)
698                 Assert(0);      // tried to force text into an input box that won't fit into allocated memory
699
700         strcpy(text, in);
701         
702         if (flags & UI_INPUTBOX_FLAG_PASSWD) {
703                 memset(passwd_text, INPUTBOX_PASSWD_CHAR, strlen(text));
704                 passwd_text[strlen(text)] = 0;
705         }
706
707         position = in_length;  // fixes the zero-length-I-don't-think-so bug
708 }
709
710