2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Ui/INPUTBOX.cpp $
15 * Code to implement input boxes
18 * Revision 1.3 2004/09/20 01:31:45 theoddone33
21 * Revision 1.2 2002/06/09 04:41:29 relnev
22 * added copyright header
24 * Revision 1.1.1.1 2002/05/03 03:28:11 root
28 * 8 9/07/99 4:01p Dave
29 * Fixed up a string.tbl paroblem (self destruct message). Make sure IPX
30 * does everything properly (setting up address when binding). Remove
31 * black rectangle background from UI_INPUTBOX.
33 * 7 7/15/99 7:14p Jefff
35 * 6 6/25/99 11:59a Dave
36 * Multi options screen.
38 * 5 2/11/99 3:08p Dave
39 * PXO refresh button. Very preliminary squad war support.
41 * 4 12/02/98 5:47p Dave
42 * Put in interface xstr code. Converted barracks screen to new format.
44 * 3 10/13/98 9:29a Dave
45 * Started neatening up freespace.h. Many variables renamed and
46 * reorganized. Added AlphaColors.[h,cpp]
48 * 2 10/07/98 10:54a Dave
51 * 1 10/07/98 10:51a Dave
53 * 43 5/14/98 6:29p Hoffoss
54 * Fixed some warnings a release rebuild all turned up.
56 * 42 4/14/98 5:06p Dave
57 * Don't load or send invalid pilot pics. Fixed chatbox graphic errors.
58 * Made chatbox display team icons in a team vs. team game. Fixed up pause
59 * and endgame sequencing issues.
61 * 41 4/12/98 5:31p Lawrance
62 * use timer_get_milliseconds() instead of gettime()
64 * 40 4/10/98 5:36p Dave
65 * Put in user notification of illegal values in multi host options
66 * screen. Fixed server respawn ship class problem.
68 * 39 4/09/98 3:10p Mike
69 * Fix shockingly stupid typo.
71 * 38 4/09/98 2:46p Mike
72 * Fix bug preventing first letter in pilot name being capitalized.
74 * 37 4/09/98 12:12p Mike
75 * Separate versioning for demo and full versions.
78 * 36 4/06/98 5:14p Hoffoss
79 * Added some needed asserts.
81 * 35 4/06/98 4:25p Allender
82 * Fix strncpy bug in inputbox
84 * 34 4/06/98 3:58p Frank
85 * added initialization to try and fix a bug.
87 * 33 4/01/98 10:26a Hoffoss
88 * Changed input box code to not draw a cursor if the input box is
91 * 32 3/27/98 4:01p Hoffoss
92 * Fixed bug where input box text wsn't being null terminated! Bad!
94 * 31 3/27/98 3:04p Mitri
95 * Fixed bug in set_text(). 'length' is the number of characters allowed,
96 * EXCLUDING the null terminator.
98 * 30 3/27/98 11:20a Hoffoss
99 * Changed input to use bright white text instead of bright green.
101 * 29 3/10/98 11:29a Hoffoss
102 * Make first_time ignored and text not disappear after a return is
105 * 28 2/26/98 4:21p Dave
106 * More robust multiplayer voice.
108 * 27 1/23/98 5:43p Dave
109 * Finished bringing standalone up to speed. Coded in new host options
112 * 26 1/20/98 10:36a Hoffoss
113 * Fixed optimized warnings.
115 * 25 1/17/98 5:51p Dave
116 * Bug fixes for bugs generated by multiplayer testing.
118 * 24 1/16/98 7:57p Lawrance
119 * support animating input box cursor
121 * 23 1/15/98 5:12p Hoffoss
122 * Fixed inputbox so clicking on it gives it focus.
124 * 22 1/15/98 5:10p Allender
125 * ton of interface changes. chatbox in multiplayer now behaves
126 * differently than before. It's always active in any screen that uses
127 * it. Only non-printatble characters will get passed back out from
130 * 21 1/14/98 6:43p Hoffoss
131 * Massive changes to UI code. A lot cleaner and better now. Did all
132 * this to get the new UI_DOT_SLIDER to work properly, which the old code
133 * wasn't flexible enough to handle.
135 * 20 1/05/98 1:15p John
136 * Made inputbox skip the initial "selected" phase when you first tab to
139 * 19 12/22/97 5:08p Hoffoss
140 * Changed inputbox class to be able to accept only certain keys, changed
141 * pilot screens to utilize this feature. Added to assert with pilot file
144 * 18 12/11/97 8:15p Dave
145 * Put in network options screen. Xed out olf protocol selection screen.
147 * 17 12/08/97 6:22p Lawrance
148 * blink cursor on inputbox
150 * 16 12/06/97 4:27p Dave
151 * Another load of interface and multiplayer bug fixes.
153 * 15 11/25/97 3:51p Hoffoss
154 * Changed edit background rect position slightly.
156 * 14 10/24/97 10:58p Hoffoss
157 * Made some changes to the UI code to do some things I need it to do.
159 * 13 10/09/97 11:05a Allender
160 * ignore controled and alted keys in input box
162 * 12 10/01/97 4:39p Lawrance
163 * null out text when free'ed
165 * 11 9/09/97 3:39p Sandeep
166 * warning level 4 bugs
168 * 10 8/21/97 12:13p Dave
169 * Made it possible for input box to ignore esc to lose focus.
171 * 9 8/19/97 1:28p Dave
172 * Made it possible to limit characters by pixel width.
174 * 8 8/15/97 8:21p Dave
175 * Modified UI_INPUTBOX so that it is possible to draw it invisibly. That
176 * is, only the text is displayed.
178 * 7 8/11/97 9:48p Lawrance
179 * reset clip after drawing
181 * 6 7/01/97 2:10p Dave
182 * Fixed bug which caused the box to stop responding after user called
183 * set_text(...) with a 0 length string.
185 * 5 6/12/97 12:39p John
186 * made ui use freespace colors
188 * 4 6/11/97 1:13p John
189 * Started fixing all the text colors in the game.
191 * 3 12/03/96 3:46p Lawrance
192 * added ability to set contents of input box
194 * 2 11/15/96 11:43a John
196 * 1 11/15/96 8:21a John
206 #include "alphacolors.h"
209 #define INPUTBOX_PASSWD_CHAR '*' // the password protected char
211 // Retuen true if c is a letter, else return false.
212 int is_letter(char c)
214 return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
217 // if the passed key is keypad number, return the ascii value, otherwise -1
218 int keypad_to_ascii(int c)
222 return key_to_ascii(SDLK_0);
225 return key_to_ascii(SDLK_1);
228 return key_to_ascii(SDLK_2);
231 return key_to_ascii(SDLK_3);
234 return key_to_ascii(SDLK_4);
237 return key_to_ascii(SDLK_5);
240 return key_to_ascii(SDLK_6);
243 return key_to_ascii(SDLK_7);
246 return key_to_ascii(SDLK_8);
249 return key_to_ascii(SDLK_9);
252 return key_to_ascii(SDLK_PERIOD);
260 // insert character c into string s at position p.
261 void strcins(char *s, int p, char c)
264 for (n=strlen(s)-p; n>=0; n-- )
265 *(s+p+n+1) = *(s+p+n); // Move everything over
266 *(s+p) = c; // then insert the character
269 // delete n character from string s starting at position p
271 void strndel(char *s, int p, int n)
273 for (; (*(s+p) = *(s+p+n)) != '\0'; s++ )
274 *(s+p+n) = '\0'; // Delete and zero fill
277 void UI_INPUTBOX::init_cursor()
279 cursor_first_frame = bm_load_animation("cursor1", &cursor_nframes, &cursor_fps);
280 if ( cursor_first_frame < 0 ) {
281 Warning(LOCATION,"Cannot load input box cursor: cursor1.ani\n");
284 cursor_elapsed_time=0;
285 cursor_current_frame=0;
288 void UI_INPUTBOX::create(UI_WINDOW *wnd, int _x, int _y, int _w, int _text_len, const char *_text, int _flags, int pixel_lim, color *clr)
292 Assert(_text_len >= 0);
293 Assert((int) strlen(_text) <= _text_len);
294 gr_set_font(wnd->f_id);
295 gr_get_string_size( &tw, &th, "*" );
297 // check to see if the user passed in a text color otherwise use the default green color
301 text_color = &CBRIGHT;
304 base_create( wnd, UI_KIND_INPUTBOX, _x, _y, _w, th+4 );
305 text = (char *) malloc( _text_len + 1);
307 // input boxes no longer use background
308 _flags |= UI_INPUTBOX_FLAG_NO_BACK;
310 // if its in "password" mode, allocate a second string
312 if (_flags & UI_INPUTBOX_FLAG_PASSWD) {
313 passwd_text = (char *) malloc(_text_len + 1);
314 memset(passwd_text, INPUTBOX_PASSWD_CHAR, strlen(_text));
315 passwd_text[strlen(_text)] = 0;
323 if ( _text_len > 0 ) {
324 strncpy( text, _text, _text_len );
327 position = strlen(_text);
328 oldposition = position;
334 pixel_limit = pixel_lim;
337 invalid_chars = NULL;
340 void UI_INPUTBOX::set_valid_chars(const char *vchars)
342 // free up any existing string
343 if(valid_chars != NULL){
348 valid_chars = strdup(vchars);
351 void UI_INPUTBOX::set_invalid_chars(const char *ichars)
353 // free up any existing string
354 if(invalid_chars != NULL){
356 invalid_chars = NULL;
359 invalid_chars = strdup(ichars);
362 void UI_INPUTBOX::destroy()
369 // free any valid chars
370 if(valid_chars != NULL){
375 // free any invalid chars
376 if(invalid_chars != NULL){
378 invalid_chars = NULL;
381 if ((flags & UI_INPUTBOX_FLAG_PASSWD) && passwd_text) {
386 UI_GADGET::destroy();
389 void UI_INPUTBOX::draw()
391 int invis, w1, h1, tw, th;
400 invis = flags & UI_INPUTBOX_FLAG_INVIS;
402 gr_set_font(my_wnd->f_id);
404 if (!invis && !(flags & UI_INPUTBOX_FLAG_NO_BACK)) {
405 // draw the entire text box region
406 ui_draw_sunken_border( x-2, y-2, x+w+1, y+h+1 );
407 gr_set_color_fast( &CBLACK );
408 gr_rect( 0, 0, w, h );
411 gr_set_clip( x + 1, y + 1, w1 + 1, h1 + 1 );
413 gr_set_clip( x - 1, y - 1, w1 + 1, h1 + 1 );
416 if (flags & UI_INPUTBOX_FLAG_PASSWD){
417 gr_get_string_size(&tw, &th, passwd_text);
419 gr_get_string_size(&tw, &th, text);
422 // If first_time is set, that means this input box got
423 // focus, but nothing is typed yet, so all the text is
424 // selected, if you type a character it will replace the
425 // text, if you type an arrow it will unselect it.
426 // So it needs to be colored differently to show this.
427 if (!disabled_flag && !(flags & UI_INPUTBOX_FLAG_NO_BACK)) {
428 // if ( (my_wnd->selected_gadget == this) && first_time ) {
429 // gr_set_color_fast( text_color );
432 gr_set_color_fast( &CBLACK );
435 // color the background behind the text
436 gr_rect( 0, 0, tw + 1, th );
439 if ( (my_wnd->selected_gadget == this) || disabled_flag ) {
440 gr_set_color_fast(text_color);
442 gr_set_color_fast(&CWHITE);
445 // coords of where to draw the text
448 if(flags & UI_INPUTBOX_FLAG_TEXT_CEN){
449 // if we fit within the text area, draw it centered
451 text_x += (w1 - tw)/2;
456 if (flags & UI_INPUTBOX_FLAG_PASSWD){
457 gr_string(text_x, text_y, passwd_text);
459 gr_string(text_x, text_y, text);
463 if (!disabled_flag) {
464 if (my_wnd->selected_gadget == this) {
465 if (cursor_first_frame == -1) {
466 gr_set_color_fast(text_color);
467 ui_vline(1, h1, text_x + tw + 4);
468 ui_vline(1, h1, text_x + tw + 5);
470 // draw animating cursor
471 int time_delta = timer_get_milliseconds() - cursor_elapsed_time;
473 if ( (time_delta / 1000.0f) > (1.0f / cursor_fps) ) {
475 cursor_elapsed_time += time_delta;
476 cursor_current_frame++;
477 if (cursor_current_frame >= cursor_nframes) {
478 cursor_current_frame = 0;
482 // draw current frame
483 gr_set_bitmap(cursor_first_frame + cursor_current_frame, GR_ALPHABLEND_NONE, GR_BITBLT_MODE_NORMAL, 1.0f, -1, -1);
484 gr_bitmap(text_x + tw + 4, 1);
492 int UI_INPUTBOX::validate_input(int chr)
494 if (chr < 32) { // weed out control characters
498 // if we're disallowing letters altogether
499 if((flags & UI_INPUTBOX_FLAG_NO_LETTERS) && isalpha(chr)){
503 // if we're disallowing numbers altogether
504 if((flags & UI_INPUTBOX_FLAG_NO_NUMERALS) && isdigit(chr)){
508 // otherwise allow numbers and alpha chars by
509 if (isdigit(chr) || isalpha(chr)){
513 // if we have specified no valid or invalid chars, accept everything
514 if(!valid_chars && !invalid_chars){
518 // otherwise compare against the valid chars list
519 if((valid_chars) && strchr(valid_chars, chr)){
523 // otherwise compare against the invalid chars list0
524 if((invalid_chars) && !strchr(invalid_chars,chr)){
531 void UI_INPUTBOX::process(int focus)
533 int ascii, clear_lastkey, key, key_used, key_check;
535 // check if mouse is pressed
536 if (B1_PRESSED && is_mouse_on()) {
544 if (my_wnd->selected_gadget == this)
551 oldposition = position;
553 clear_lastkey = (flags & UI_INPUTBOX_FLAG_KEYTHRU) ? 0 : 1;
556 key = my_wnd->keypress;
567 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
568 passwd_text[position] = 0;
590 if (flags & UI_INPUTBOX_FLAG_ESC_CLR){
601 if (flags & UI_INPUTBOX_FLAG_ESC_FOC) {
609 // MWA -- determine if alt or ctrl held down on this key and don't process if it is. We
610 // need to be able to pass these keys back to the top level. (And anyway -- ctrl-a shouldn't
611 // print out an A in the input window
612 if ( key & (KEY_ALTED | KEY_CTRLED) ) {
617 // get an ascii char from the input if possible
618 key_check = keypad_to_ascii(key);
620 key_check = key_to_ascii(key);
623 ascii = validate_input(key_check);
624 if ((ascii > 0) && (ascii < 255)) {
625 if (flags & UI_INPUTBOX_FLAG_LETTER_FIRST) {
626 if ((position == 0) && !is_letter((char) ascii))
631 // if (should_reset) {
641 if ( position < length ) {
642 text[position] = (char) ascii;
643 text[position + 1] = 0;
645 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
646 passwd_text[position] = (char) INPUTBOX_PASSWD_CHAR;
647 passwd_text[position + 1] = 0;
652 // check to see if we should limit by pixel width
653 if (pixel_limit > -1) {
656 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
657 gr_get_string_size(&w, NULL, passwd_text);
660 gr_get_string_size(&w, NULL, text);
663 if (w > pixel_limit) {
668 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
669 passwd_text[position] = 0;
682 if (clear_lastkey || (key_used && (flags & UI_INPUTBOX_FLAG_EAT_USED)) )
683 my_wnd->last_keypress=0;
690 int UI_INPUTBOX::changed()
695 int UI_INPUTBOX::pressed()
700 void UI_INPUTBOX::get_text(char *out)
702 strncpy(out, text, length);
706 void UI_INPUTBOX::set_text(const char *in)
710 in_length = strlen(in);
711 if (in_length > length)
712 Assert(0); // tried to force text into an input box that won't fit into allocated memory
716 if (flags & UI_INPUTBOX_FLAG_PASSWD) {
717 memset(passwd_text, INPUTBOX_PASSWD_CHAR, strlen(text));
718 passwd_text[strlen(text)] = 0;
721 position = in_length; // fixes the zero-length-I-don't-think-so bug