]> icculus.org git repositories - taylor/freespace2.git/blob - src/popup/popup.cpp
fix issue with looping audio streams
[taylor/freespace2.git] / src / popup / popup.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/Popup/Popup.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * Code for displaying pop-up dialog boxes
16  *
17  * $Log$
18  * Revision 1.4  2003/05/25 02:30:43  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.3  2002/06/09 04:41:25  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:51  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:10  root
28  * Initial import.
29  *
30  * 
31  * 12    10/14/99 2:00p Jefff
32  * added include for os_poll to fix build error
33  * 
34  * 11    10/14/99 10:18a Daveb
35  * Fixed incorrect CD checking problem on standalone server.
36  * 
37  * 10    8/16/99 9:50a Jefff
38  * added mouseover webcursor options to user-defined popup buttons.
39  * 
40  * 9     8/11/99 5:47p Jefff
41  * fixed button bitmap loading
42  * 
43  * 8     8/04/99 10:53a Dave
44  * Added title to the user tips popup.
45  * 
46  * 7     8/02/99 9:13p Dave
47  * Added popup tips.
48  * 
49  * 6     6/18/99 5:16p Dave
50  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
51  * dialog to PXO screen.
52  * 
53  * 5     6/01/99 3:52p Dave
54  * View footage screen. Fixed xstrings to not display the & symbol. Popup,
55  * dead popup, pxo find player popup, pxo private room popup.
56  * 
57  * 4     2/11/99 3:08p Dave
58  * PXO refresh button. Very preliminary squad war support.
59  * 
60  * 3     10/13/98 9:29a Dave
61  * Started neatening up freespace.h. Many variables renamed and
62  * reorganized. Added AlphaColors.[h,cpp]
63  * 
64  * 2     10/07/98 10:53a Dave
65  * Initial checkin.
66  * 
67  * 1     10/07/98 10:51a Dave
68  * 
69  * 36    5/11/98 11:39p Dave
70  * Stuff.
71  * 
72  * 35    3/22/98 10:59p Allender
73  * don't stop time in multiplayer when "in mission"
74  * 
75  * 34    3/17/98 12:41a Lawrance
76  * Support \n's between title and body
77  * 
78  * 33    3/12/98 4:11p Dave
79  * AL: Check that max popup lines isn't exceeded
80  * 
81  * 32    2/22/98 12:19p John
82  * Externalized some strings
83  * 
84  * 31    2/10/98 3:26p Hoffoss
85  * Don't check for 'dead key set' unless in the mission.
86  * 
87  * 30    2/05/98 11:21p Lawrance
88  * move death popup to below letterbox view
89  * 
90  * 29    2/05/98 11:09a Dave
91  * Fixed an ingame join bug. Fixed a read-only file problem with
92  * multiplauer file xfer.
93  * 
94  * 28    2/03/98 11:52p Lawrance
95  * Don't highlight default choice if there is only one.
96  * 
97  * 27    2/03/98 8:18p Dave
98  * More MT stats transfer stuff
99  * 
100  * 26    1/30/98 3:05p Lawrance
101  * reposition text on death popup
102  * 
103  * 25    1/29/98 6:55p Lawrance
104  * Integrate new art for the death popup
105  * 
106  * 24    1/28/98 6:24p Dave
107  * Made standalone use ~8 megs less memory. Fixed multiplayer submenu
108  * sequencing problem.
109  * 
110  * 23    1/27/98 5:52p Lawrance
111  * support new "tiny" popups
112  * 
113  * 22    1/27/98 5:01p Dave
114  * Put in support for leaving multiplayer games in the pause state. Fixed
115  * a popup bug which freed saved screens incorrectly. Reworked scoring
116  * kill and assist evaluation.
117  * 
118  * 21    1/27/98 3:25p Hoffoss
119  * Made popups use the correct button icons for default positive and
120  * negative buttons.
121  * 
122  * 20    1/26/98 6:28p Lawrance
123  * Use '&' meta char for underlining, change how keyboard usage works so
124  * fits better with mouse usage.
125  * 
126  * 19    1/23/98 12:01a Lawrance
127  * Fix bug when in single-choice popups.
128  * 
129  * 18    1/22/98 10:45p Lawrance
130  * Implment default selections and arrow key navigation.
131  * 
132  * 17    1/20/98 5:52p Lawrance
133  * center popup text in X and Y directions.  Support single affirmative
134  * icon special placement.
135  * 
136  * 16    1/19/98 11:37p Lawrance
137  * Fixing Optimization build warnings
138  * 
139  * 15    1/17/98 10:04p Lawrance
140  * fix errors in popup comments
141  * 
142  * 14    1/15/98 2:52p Lawrance
143  * Fix problem with reading bogus buttons.
144  * 
145  * 13    1/14/98 6:55p Dave
146  * Fixed a slew of multiplayer bugs. Made certain important popups ignore
147  * the escape character.
148  * 
149  * 12    1/14/98 6:42p Hoffoss
150  * Massive changes to UI code.  A lot cleaner and better now.  Did all
151  * this to get the new UI_DOT_SLIDER to work properly, which the old code
152  * wasn't flexible enough to handle.
153  * 
154  * 11    1/14/98 12:23p Lawrance
155  * Support for three choice popups.
156  * 
157  * 10    1/13/98 5:37p Dave
158  * Reworked a lot of standalone interface code. Put in single and
159  * multiplayer popups for death sequence. Solidified multiplayer kick
160  * code.
161  * 
162  * 9     1/13/98 4:06p Lawrance
163  * Add underline char for shortcuts.
164  * 
165  * 8     1/11/98 11:14p Lawrance
166  * Don't grey out background when popup is active.
167  * 
168  * 7     1/08/98 10:32a Lawrance
169  * Grey out screen when a popup appears.
170  * 
171  * 6     1/02/98 9:08p Lawrance
172  * Integrated art for popups, expanded options.
173  * 
174  * 5     12/30/97 4:30p Sandeep
175  * Added conditional popups
176  * 
177  * 4     12/26/97 10:01p Lawrance
178  * Allow keyboard shortcuts for popup buttons
179  * 
180  * 3     12/24/97 9:49p Lawrance
181  * ensure mouse gets drawn when popup menu is up
182  * 
183  * 2     12/24/97 8:54p Lawrance
184  * Integrating new popup code
185  * 
186  * 1     12/24/97 3:51p Lawrance
187  *
188  * $NoKeywords: $
189  */
190
191 #include <stdarg.h>
192 #include <string.h>
193 #include "freespace.h"
194 #include "gamesequence.h"
195 #include "key.h"
196 #include "mouse.h"
197 #include "ui.h"
198 #include "parselo.h"
199 #include "popup.h"
200 #include "gamesnd.h"
201 #include "animplay.h"
202 #include "contexthelp.h"
203 #include "keycontrol.h"
204 #include "player.h"
205 #include "font.h"
206 #include "alphacolors.h"
207 #include "osapi.h"
208
209 #define POPUP_MAX_CHOICES                       3                                       // max number of buttons allowed on popup
210
211 #define POPUP_MAX_LINE_CHARS            256                             // max chars of msg text allowed per line
212 #define POPUP_MAX_LINES                         30                                      // max lines of text allowed
213 #define POPUP_MAX_CHARS                         2048                            // total max chars 
214 #define POPUP_INPUT_MAX_CHARS           255                             // max length of input string
215
216 #define POPUP_NOCHANGE                          100
217 #define POPUP_ABORT                                     101
218
219 int Popup_max_display[GR_NUM_RESOLUTIONS] = {
220         11,
221         19
222 };
223
224 #ifndef MAKE_FS1  // to avoid trying to find the interface tool
225 const char *Popup_slider_name[GR_NUM_RESOLUTIONS] = {
226         "slider",
227         "2_slider"
228 };
229 #endif
230
231 int Popup_slider_coords[GR_NUM_RESOLUTIONS][4] = {
232         { // GR_640
233                 121, 109, 15, 105
234         },
235         { // GR_1024
236                 195, 177, 30, 173
237         }
238 };
239
240 ////////////////////////////////////////////////////////////////
241 // Internal popup flags
242 ////////////////////////////////////////////////////////////////
243 #define PF_INPUT                                                (1<<0)                  // contents of the box is an inputbox and a caption
244
245 ////////////////////////////////////////////////////////////////
246 // Popup data struct
247 ////////////////////////////////////////////////////////////////
248 typedef struct popup_info
249 {
250         int     nchoices;                                                                                                               // number of choices user can pick
251         char    *button_text[POPUP_MAX_CHOICES];                                                        // button text
252         int     keypress[POPUP_MAX_CHOICES];                                                            // button keypress shortcut
253         int     shortcut_index[POPUP_MAX_CHOICES];                                              // what char should be underlines for shortcut
254         char    raw_text[POPUP_MAX_CHARS];                                                                      // the unbroken text for the popup
255         char    title[POPUP_MAX_LINE_CHARS];                                                            // title text for popup (optional)
256         int     nlines;
257         char    msg_lines[POPUP_MAX_LINES][POPUP_MAX_LINE_CHARS];       // lines of text in popup
258         char    input_text[POPUP_INPUT_MAX_CHARS];                                              // input box text (if this is an inputbox popup)
259         int     max_input_text_len;
260         int     web_cursor_flag[POPUP_MAX_CHOICES];                                             // flag for using web cursor over button
261         void (*callback)(int);                                                                          // callback to call on user choice (optional)
262         int flags;                                                                                                      // popup flags (PF_*)
263         int choice;                                                                                                     // choice user made
264         int screen_id;                                                                                          // background screen id
265         int (*condition)();                                                                                     // test condition (optional)
266 } popup_info;
267
268 ////////////////////////////////////////////////////////////////
269 // UI Data and constants
270 ////////////////////////////////////////////////////////////////
271 UI_WINDOW       Popup_window;
272 UI_BUTTON       Popup_buttons[POPUP_MAX_CHOICES];                       // actual lit buttons
273 UI_BUTTON       Popup_button_regions[POPUP_MAX_CHOICES];        // fake buttons used for mouse detection over text
274 UI_INPUTBOX     Popup_input;                                                                            // input box for the popup
275 #ifndef MAKE_FS1
276 UI_SLIDER2      Popup_slider;                                                                           // if we have more text in the popup than can be displayed at once
277 #endif
278
279 // extents for message portion of popup
280 int Popup_text_coords[GR_NUM_RESOLUTIONS][4] = {
281         { // GR_640
282 #ifdef MAKE_FS1
283                 154, 144, 331, 105
284 #else
285                 137, 106, 343, 113
286 #endif
287         },
288         { // GR_1024
289                 219, 169, 558, 182
290         }
291 };
292
293 // input popup info
294 // offset from the first y text line value to place the centered input box
295 int Popup_input_y_offset[GR_NUM_RESOLUTIONS] = {
296         40, 
297         40
298 };
299
300 // offset from the first y text line value to start drawing text
301 int Popup_input_text_y_offset[GR_NUM_RESOLUTIONS] = {
302         30,
303         30
304 };
305
306 typedef struct popup_background
307 {
308         const char      *filename;                                                      // filename for background
309         int     coords[2];                                                      // coords to draw background at
310 } popup_background;
311
312 ////////////////////////////////////////////////////////////////
313 // Module globals
314 ////////////////////////////////////////////////////////////////
315 static int Popup_is_active=0;
316 static int Popup_should_die=0;                  // popup should quit during the next iteration of its loop
317
318 static popup_info Popup_info;
319
320 static int Title_coords[GR_NUM_RESOLUTIONS][5] =
321 {
322         { // GR_640
323 #ifdef MAKE_FS1
324                 154,    // x-left
325                 144,    // y-top
326                 331,    // width
327                 26,             // height
328                 320             // center
329 #else
330                 137,            // x-left
331                 106,            // y-top
332                 343,            // width
333                 26,             //      height
334                 308             // center
335 #endif
336         },
337         { // GR_1024
338                 220,            // x-left
339                 169,            // y-top
340                 553,            // width
341                 26,             //      height
342                 496             // center
343         }
344 };
345
346 static int Button_regions[GR_NUM_RESOLUTIONS][3][4] = {
347         { // GR_640             
348 #ifdef MAKE_FS1
349                 {464, 269, 505, 290},           // upper right pixel of text, lower right pixel of button
350                 {464, 297, 505, 320},
351                 {464, 323, 505, 342}
352 #else
353                 {464, 232, 510, 250},           // upper right pixel of text, lower right pixel of button
354                 {464, 262, 510, 279},           
355                 {464, 292, 510, 308}            
356 #endif
357         },
358         { // GR_1024
359                 {752, 373, 806, 406},           // upper right pixel of text, lower right pixel of button
360                 {752, 421, 806, 461},           
361                 {752, 468, 806, 506}            
362         }
363 };
364
365 static int Button_coords[GR_NUM_RESOLUTIONS][3][2] =
366 {
367         { // GR_640
368 #ifdef MAKE_FS1
369                 {474, 257},             // upper left pixel
370                 {474, 291},
371                 {474, 318}              
372 #else
373                 {474, 224},             // upper left pixel
374                 {474, 258},
375                 {474, 286}              
376 #endif
377         },
378         { // GR_1024
379                 {758, 358},             // upper left pixel
380                 {758, 413},
381                 {758, 458},             
382         }
383 };
384
385 static popup_background Popup_background[GR_NUM_RESOLUTIONS][4] = 
386 {
387         { // GR_640
388 #ifdef MAKE_FS1
389         { "Pop2a",              { 131, 122} },
390         { "Pop2a",              { 131, 122 } },
391         { "Pop3",               { 131, 122 } },
392 #else
393                 { "Pop2",               { 129, 99 } },
394                 { "Pop2",               { 129, 99 } },
395                 { "Pop3",               { 129, 99 } },          
396 #endif
397         },
398         { // GR_1024
399                 { "2_Pop2",             { 206, 158 } },
400                 { "2_Pop2",             { 206, 158 } },
401                 { "2_Pop3",             { 206, 158 } },         
402         }
403 };
404
405 #define BUTTON_NEGATIVE                         0
406 #define BUTTON_POSITIVE                         1
407 #define BUTTON_GENERIC_FIRST            2
408 #define BUTTON_GENERIC_SECOND           3
409 #define BUTTON_GENERIC_THIRD            4
410 static const char *Popup_button_filenames[GR_NUM_RESOLUTIONS][2][5] = 
411 {
412         { // GR_640
413 #ifdef MAKE_FS1
414                 {"Pop2a_00",                    // negative
415                 "Pop2a_01",                             // positive
416                 "Pop2a_02",                             // first generic
417                 "Pop2a_03",                             // second generic
418                 "Pop2a_04"},                    // third generic
419
420                 {"Pop2a_00",                    // negative
421                 "Pop2a_01",                             // positive
422                 "PopD_00",                              // first generic
423                 "PopD_01",                              // second generic
424                 "PopD_02"},                             // third generic
425 #else
426                 {"Pop_00",                              // negative
427                 "Pop_01",                               // positive
428                 "Pop_02",                               // first generic
429                 "Pop_03",                               // second generic
430                 "Pop_04"},                              // third generic
431
432                 {"Pop_00",                              // negative
433                 "Pop_01",                               // positive
434                 "PopD_00",                              // first generic
435                 "PopD_01",                              // second generic
436                 "PopD_02"},                             // third generic
437 #endif
438         },
439         { // GR_1024
440                 {"2_Pop_00",                    // negative
441                 "2_Pop_01",                             // positive
442                 "2_Pop_02",                             // first generic
443                 "2_Pop_03",                             // second generic
444                 "2_Pop_04"},                    // third generic
445
446                 {"2_Pop_00",                    // negative
447                 "2_Pop_01",                             // positive
448                 "2_PopD_00",                    // first generic
449                 "2_PopD_01",                    // second generic
450                 "2_PopD_02"},                   // third generic
451         }
452 };
453
454 int Popup_running_state;
455 int Popup_default_choice;       // which choice is highlighted (ie what gets choosen when enter is pressed)
456
457 // see if any popup buttons have been pressed
458 // exit: POPUP_NOCHANGE         => no buttons pressed
459 //                      >=0                                     =>      button index that was pressed
460 int popup_check_buttons(popup_info *pi)
461 {
462         int                     i;
463         UI_BUTTON       *b;
464
465         for ( i = 0; i < pi->nchoices; i++ ) {
466                 b = &Popup_button_regions[i];
467                 if ( b->pressed() ) {
468                         return i;
469                 }
470
471                 b = &Popup_buttons[i];
472                 if ( b->pressed() ) {
473                         return i;
474                 }
475         }
476
477         return POPUP_NOCHANGE;
478 }
479
480 // maybe play a sound when key up/down is pressed to switch default choice
481 void popup_play_default_change_sound(popup_info *pi)
482 {
483         if ( pi->nchoices > 1 ) {
484                 int i, mouse_over=0;
485                 UI_BUTTON *br, *b;
486
487                 // only play if mouse not currently highlighting a choice
488
489                 for ( i = 0; i < pi->nchoices; i++ ) {
490                         br = &Popup_button_regions[i];
491                         b = &Popup_buttons[i];
492                         if ( br->button_down() ) {
493                                 mouse_over=1;
494                                 break;
495                         }
496
497                         if ( br->button_hilighted() && !b->button_down() ) {
498                                 mouse_over=1;
499                                 break;
500                         }
501
502                         if ( b->button_hilighted() ) {
503                                 mouse_over=1;
504                         }
505                 }
506
507                 if (!mouse_over) {
508                         gamesnd_play_iface(SND_USER_SELECT);
509                 }
510         }
511 }
512
513 // do any key processing here
514 // input:       pi                                      =>      data about the popup
515 //                              k                                       => key that was pressed
516 //
517 // exit: 0 .. nchoices-1        => choice selected through keypress
518 //                      POPUP_ABORT                     =>      abort the popup
519 //                      POPUP_NOCHANGE          => nothing happenned
520 int popup_process_keys(popup_info *pi, int k)
521 {
522         int i, masked_k;
523
524         if ( k <= 0 ) {
525                 return POPUP_NOCHANGE;
526         }
527
528         for ( i = 0; i < pi->nchoices; i++ ) {
529                 if ( pi->keypress[i] == key_get_text_input() ) {
530                         Popup_default_choice=i;
531                         Popup_buttons[i].press_button();
532                         return i;
533                 }
534         }
535         
536         switch(k) {
537
538         case SDLK_RETURN:
539                 // select the current default choice
540                 return Popup_default_choice;
541                 break;
542
543         case SDLK_ESCAPE:
544                 // only process the escape key if this flag is not set
545                 if(!(pi->flags & PF_IGNORE_ESC)){
546                         return POPUP_ABORT;
547                 }
548                 break;
549
550         case SDLK_DOWN:
551         case SDLK_KP_2:
552         case SDLK_TAB:
553                 popup_play_default_change_sound(pi);
554                 Popup_default_choice++;
555                 if ( Popup_default_choice >= pi->nchoices ) {
556                         Popup_default_choice=0;
557                 }
558                 break;
559
560         case SDLK_UP:
561         case SDLK_KP_8:
562         case KEY_SHIFTED+SDLK_TAB:
563                 popup_play_default_change_sound(pi);
564                 Popup_default_choice--;
565                 if ( Popup_default_choice < 0 ) {
566                         Popup_default_choice=pi->nchoices-1;
567                 }
568                 break;
569
570         default:
571                 break;
572         } // end switch
573
574
575         masked_k = k & ~KEY_CTRLED;     // take out CTRL modifier only
576         if ( (PF_ALLOW_DEAD_KEYS) && (Game_mode & GM_IN_MISSION) ) {
577                 process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set);
578                 button_info_do(&Player->bi);    // call functions based on status of button_info bit vectors
579         }
580
581         return POPUP_NOCHANGE;
582 }
583
584 // Split off the title and break up the body lines
585 void popup_split_lines(popup_info *pi)
586 {
587         int     nlines, i, body_offset = 0;
588         int     n_chars[POPUP_MAX_LINES];
589         char    *p_str[POPUP_MAX_LINES];
590         int len;
591
592         gr_set_font(FONT1);
593         n_chars[0]=0;
594
595         nlines = split_str(pi->raw_text, 1000, n_chars, p_str, POPUP_MAX_LINES);
596         SDL_assert(nlines >= 0 && nlines <= POPUP_MAX_LINES );
597
598         if ( pi->flags & (PF_TITLE | PF_TITLE_BIG) ) {
599                 // get first line out
600                 len = SDL_min(n_chars[0] + 1, POPUP_MAX_LINE_CHARS);
601                 SDL_strlcpy(pi->title, p_str[0], len);
602                 body_offset = 1;
603         }
604
605         if ( pi->flags & PF_BODY_BIG ) {
606                 gr_set_font(FONT2);
607         }
608
609         nlines = split_str(pi->raw_text, Popup_text_coords[gr_screen.res][2], n_chars, p_str, POPUP_MAX_LINES);
610         SDL_assert(nlines >= 0 && nlines <= POPUP_MAX_LINES );
611
612         pi->nlines = nlines - body_offset;
613
614         for ( i = 0; i < pi->nlines; i++ ) {
615                 SDL_assert(n_chars[i+body_offset] < POPUP_MAX_LINE_CHARS);
616                 len = SDL_min(n_chars[i+body_offset] + 1, POPUP_MAX_LINE_CHARS);
617                 SDL_strlcpy(pi->msg_lines[i], p_str[i+body_offset], len);
618         }
619
620         gr_set_font(FONT1);
621 }
622
623 // figure out what filename to use for the button icon
624 const char *popup_get_button_filename(popup_info *pi, int i)
625 {
626         const char *fname = NULL;
627         int is_tiny=0;  
628
629         // check for special button texts and if found, use specialized buttons for them.
630         if ((!SDL_strcasecmp(pi->button_text[i], POPUP_OK + 1) || !SDL_strcasecmp(pi->button_text[i], POPUP_YES + 1)) && !(pi->flags & PF_NO_SPECIAL_BUTTONS)){
631                 return Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
632         }
633
634         if ((!SDL_strcasecmp(pi->button_text[i], POPUP_CANCEL + 1) || !SDL_strcasecmp(pi->button_text[i], POPUP_NO + 1)) && !(pi->flags & PF_NO_SPECIAL_BUTTONS)){
635                 return Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
636         }
637
638         switch (pi->nchoices) {
639         case 0:
640                 fname = "";
641                 break;
642         case 1:
643                 if ( (pi->flags & PF_USE_AFFIRMATIVE_ICON) && !(pi->flags & PF_NO_SPECIAL_BUTTONS) ) {
644                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
645                 } else if ( pi->flags & PF_USE_NEGATIVE_ICON && !(pi->flags & PF_NO_SPECIAL_BUTTONS) ) {
646                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
647                 } else {
648                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
649                 }
650                 break;
651
652         case 2:
653                 if ( pi->flags & PF_USE_NEGATIVE_ICON && i==0 ) {
654                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
655                         break;
656                 } 
657
658                 if ( pi->flags & PF_USE_AFFIRMATIVE_ICON && i==1 ) {
659                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
660                         break;
661                 } 
662
663                 if ( i == 0 ) {
664                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
665                 } else {
666                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_SECOND];
667                 }
668
669                 break;
670
671         case 3:
672                 switch(i) {
673                 case 0:
674                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
675                         break;
676                 case 1:
677                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_SECOND];
678                         break;
679                 case 2:
680                         fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_THIRD];
681                         break;
682                 }
683                 break;
684         }
685
686         return fname;
687 }
688
689 // bogus handling function for slider (we don't need it to do anything)
690 void popup_slider_bogus()
691 {
692 }
693
694 // init the Popup window
695 int popup_init(popup_info *pi)
696 {
697         int                                     i;
698         UI_BUTTON                       *b;
699         popup_background        *pbg;
700         const char                      *fname;
701
702         pi->screen_id = -1;
703         pi->choice = POPUP_NOCHANGE;
704
705         if(pi->nchoices == 0){
706                 pbg = &Popup_background[gr_screen.res][0];
707         } else {
708                 pbg = &Popup_background[gr_screen.res][pi->nchoices-1];
709         }
710
711         // anytime in single player, and multiplayer, not in mission, go ahead and stop time
712         if ( (Game_mode & GM_NORMAL) || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)) ){
713                 game_stop_time();
714         }
715
716         // create base window
717         Popup_window.create(pbg->coords[0], pbg->coords[1], Popup_text_coords[gr_screen.res][2]+100, Popup_text_coords[gr_screen.res][3]+50, 0);
718         Popup_window.set_foreground_bmap(pbg->filename);
719
720         // create buttons
721         for (i=0; i<pi->nchoices; i++) {
722                 b = &Popup_buttons[i];
723                 // accommodate single-choice positive icon being positioned differently
724                 if ( (pi->nchoices == 1) && (pi->flags&PF_USE_AFFIRMATIVE_ICON) ) {
725                         b->create(&Popup_window, "", Button_coords[gr_screen.res][i+1][0], Button_coords[gr_screen.res][i+1][1], 30, 25, 0, 1);
726                 } else {
727                         b->create(&Popup_window, "", Button_coords[gr_screen.res][i][0], Button_coords[gr_screen.res][i][1], 30, 25, 0, 1);
728                 }
729
730                 fname = popup_get_button_filename(pi, i);
731                 b->set_bmaps(fname, 3, 0);
732                 b->set_highlight_action(common_play_highlight_sound);
733                 if ( pi->keypress[i] >= 0 ) {
734                         b->set_hotkey(pi->keypress[i]);
735                 }
736
737                 // create invisible buttons to detect mouse presses... can't use mask since button region is dynamically sized
738                 int lx, w, h;
739
740                 gr_get_string_size(&w, &h, pi->button_text[i]);
741                 lx = Button_regions[gr_screen.res][i][0] - w;
742                 b = &Popup_button_regions[i];   
743
744                 // accommodate single-choice positive icon being positioned differently
745                 if ( (pi->nchoices == 1) && (pi->flags&PF_USE_AFFIRMATIVE_ICON) ) {
746                         b->create(&Popup_window, "", lx, Button_regions[gr_screen.res][i+1][1], Button_regions[gr_screen.res][i+1][2]-lx, Button_regions[gr_screen.res][i+1][3]-Button_regions[gr_screen.res][i+1][1], 0, 1);
747                 } else {
748                         b->create(&Popup_window, "", lx, Button_regions[gr_screen.res][i][1], Button_regions[gr_screen.res][i][2]-lx, Button_regions[gr_screen.res][i][3]-Button_regions[gr_screen.res][i][1], 0, 1);
749                 }
750
751                 b->hide();
752         }
753
754         // webcursor setup
755         if (Web_cursor_bitmap >= 0) {
756                 if (pi->flags & PF_WEB_CURSOR_1) {
757                         Popup_buttons[1].set_custom_cursor_bmap(Web_cursor_bitmap);
758                 }
759                 if (pi->flags & PF_WEB_CURSOR_2) {
760                         Popup_buttons[2].set_custom_cursor_bmap(Web_cursor_bitmap);
761                 }
762         }
763
764         // if this is an input popup, create and center the popup
765         if(pi->flags & PF_INPUT){
766                 Popup_input.create(&Popup_window, Popup_text_coords[gr_screen.res][0], pbg->coords[1] + Popup_input_y_offset[gr_screen.res], Popup_text_coords[gr_screen.res][2], pi->max_input_text_len, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_ESC_FOC | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_TEXT_CEN);
767                 Popup_input.set_focus();
768         }       
769         
770         Popup_default_choice=0;
771         Popup_should_die = 0;
772
773         if (pi->flags & PF_RUN_STATE) {
774                 Popup_running_state = 1;
775         } else {
776                 Popup_running_state = 0;
777         }
778
779         popup_split_lines(pi);
780
781 #ifndef MAKE_FS1
782         // create the popup slider (which we may not need to use
783         Popup_slider.create(&Popup_window, Popup_slider_coords[gr_screen.res][0], Popup_slider_coords[gr_screen.res][1], Popup_slider_coords[gr_screen.res][2], Popup_slider_coords[gr_screen.res][3], pi->nlines > Popup_max_display[gr_screen.res] ? pi->nlines - Popup_max_display[gr_screen.res] : 0,
784                                                                 Popup_slider_name[gr_screen.res], popup_slider_bogus, popup_slider_bogus, NULL);
785 #endif
786
787         return 0;
788 }
789
790 // called when a popup goes away
791 void popup_close(popup_info *pi)
792 {
793         int i;
794         
795         gamesnd_play_iface(SND_POPUP_DISAPPEAR);        // play sound when popup disappears
796
797         for (i=0; i<pi->nchoices; i++ ) {
798                 if ( pi->button_text[i] != NULL ) {
799                         free(pi->button_text[i]);
800                         pi->button_text[i] = NULL;
801                 }
802         }
803
804         if(pi->screen_id >= 0){
805                 gr_free_screen(pi->screen_id);
806                 pi->screen_id = -1;
807         }
808
809         pi->callback = NULL;
810         pi->condition = NULL;
811
812         Popup_window.destroy();
813         anim_ignore_next_frametime();                                   // to avoid skips in animation since next frametime is saturated
814         game_flush();
815
816         Popup_is_active = 0;
817         Popup_running_state = 0;
818
819         // anytime in single player, and multiplayer, not in mission, go ahead and stop time
820         if ( (Game_mode & GM_NORMAL) || ((Game_mode & GM_MULTIPLAYER) && !(Game_mode & GM_IN_MISSION)) )
821                 game_start_time();
822 }
823
824 void popup_done()
825 {
826         if ( !Popup_is_active ) {
827                 return;
828         }
829
830         popup_info *pi = &Popup_info;
831
832         popup_close(pi);
833 }
834
835 // set the popup text color
836 void popup_set_text_color(int flags)
837 {
838         if ( flags & PF_BODY_RED ) {
839                 gr_set_color_fast(&Color_red);
840                 return;
841         }
842
843         if ( flags & PF_BODY_GREEN ) {
844                 gr_set_color_fast(&Color_green);
845                 return;
846         }
847
848         if ( flags & PF_BODY_BLUE ) {
849                 gr_set_color_fast(&Color_blue);
850                 return;
851         }
852
853         gr_set_color_fast(&Color_bright_blue);
854 }
855
856 // set the popup text color
857 void popup_set_title_color(int flags)
858 {
859         if ( flags & PF_TITLE_RED ) {
860                 gr_set_color_fast(&Color_red);
861                 return;
862         }
863
864         if ( flags & PF_TITLE_GREEN ) {
865                 gr_set_color_fast(&Color_green);
866                 return;
867         }
868
869         if ( flags & PF_TITLE_BLUE ) {
870                 gr_set_color_fast(&Color_blue);
871                 return;
872         }
873
874         if ( flags & PF_TITLE_WHITE ) {
875                 gr_set_color_fast(&Color_bright_white);
876                 return;
877         }
878
879         gr_set_color_fast(&Color_bright_blue);
880 }
881
882 // Draw the title centered within the popup
883 void popup_draw_title(int sy, const char *line, int flags)
884 {
885         int w, h, sx;
886
887         if ( flags & PF_TITLE_BIG ) {
888                 gr_set_font(FONT2);
889         } else {
890                 gr_set_font(FONT1);
891         }
892
893         gr_get_string_size(&w, &h, line);
894         sx = fl2i(Title_coords[gr_screen.res][4] - w/2.0f + 0.5f);
895
896         popup_set_title_color(flags);
897         gr_string(sx,sy,line);
898 }
899
900 // calculate the starting display index
901 int popup_calc_starting_index(popup_info *pi)
902 {
903         // we're basically ignoring any titles here. 
904         if(pi->nlines <= Popup_max_display[gr_screen.res]){
905                 return 0;
906         }
907
908 #ifndef MAKE_FS1
909         // otherwise, we want to see what item index the slider is on
910         return Popup_slider.get_currentItem();
911 #else
912         return 0;
913 #endif
914 }
915
916 // Figure out the y-coord to start drawing the popup text.  The text
917 // is centered vertically within the popup.
918 int popup_calc_starting_y(popup_info *pi)
919 {
920         int sy, total_h=0;
921         int num_lines = pi->nlines > Popup_max_display[gr_screen.res] ? Popup_max_display[gr_screen.res] : pi->nlines;
922
923         if ( pi->flags & (PF_TITLE | PF_TITLE_BIG) ) {
924                 if ( pi->flags & PF_TITLE_BIG ) {
925                         gr_set_font(FONT2);
926                 } else {
927                         gr_set_font(FONT1);
928                 }
929                 total_h += gr_get_font_height();
930         }
931
932         if ( pi->flags & PF_BODY_BIG ) {
933                 gr_set_font(FONT2);
934         } else {
935                 gr_set_font(FONT1);
936         }
937
938         total_h += num_lines * gr_get_font_height();
939         sy = fl2i((Popup_text_coords[gr_screen.res][1] + Popup_text_coords[gr_screen.res][3]/2.0f) - total_h/2.0f + 0.5f);
940
941         // if this is an input style box, add in some y
942         if(pi->flags & PF_INPUT){
943                 sy += Popup_input_text_y_offset[gr_screen.res];
944         }
945
946         return sy;
947 }
948
949 // Draw the message text nicely formatted in the popup
950 void popup_draw_msg_text(popup_info *pi)
951 {
952         int sx, sy, i, w, h;
953         int line_index;
954         int line_count;
955
956         // figure out the starting display 
957         line_index = popup_calc_starting_index(pi);
958
959         // figure out the starting y:
960         sy = popup_calc_starting_y(pi);
961
962         // draw title if required
963         if ( pi->flags & (PF_TITLE | PF_TITLE_BIG) ) {
964                 popup_draw_title(sy, pi->title, pi->flags);
965                 sy += gr_get_font_height();
966         }
967
968         // draw body 
969         if ( pi->flags & PF_BODY_BIG ) {
970                 gr_set_font(FONT2);
971         } else {
972                 gr_set_font(FONT1);
973         }
974
975         popup_set_text_color(pi->flags);
976         line_count = 0;
977         for ( i = line_index; i < pi->nlines; i++, line_count++ ) {
978                 // if we've already displayed the max # of lines
979                 if(line_count >= Popup_max_display[gr_screen.res]){
980                         break;
981                 }
982
983                 gr_get_string_size(&w, &h, pi->msg_lines[i]);
984                 sx = fl2i(Title_coords[gr_screen.res][4] - w/2.0f + 0.5f);
985                 gr_string(sx, sy + line_count * h, pi->msg_lines[i]);
986         }
987
988         // maybe draw "more"
989         h = 10;
990         if(i < pi->nlines){
991                 gr_set_color_fast(&Color_bright_red);
992                 gr_string(Title_coords[gr_screen.res][4], sy + (Popup_max_display[gr_screen.res]) * h, XSTR("More", 459));
993         }
994
995         gr_set_font(FONT1);     // reset back to regular font size
996 }
997
998 // Draw the button text nicely formatted in the popup
999 void popup_draw_button_text(popup_info *pi)
1000 {
1001         int w, h, i, sx, sy;
1002
1003         gr_set_color_fast(&Color_bright_blue);
1004
1005         for ( i=0; i < pi->nchoices; i++ ) {
1006                 gr_get_string_size(&w, &h, pi->button_text[i]);
1007
1008                 if ( (pi->nchoices == 1) && (pi->flags & PF_USE_AFFIRMATIVE_ICON) ) {
1009                         sx = Button_regions[gr_screen.res][i+1][0]-w;
1010                         sy = Button_regions[gr_screen.res][i+1][1]+4;
1011                 } else {
1012                         sx = Button_regions[gr_screen.res][i][0]-w;
1013                         sy = Button_regions[gr_screen.res][i][1]+4;
1014                 }
1015
1016                 gr_string(sx, sy, pi->button_text[i]);
1017
1018                 // figure out where to draw underline char
1019                 if ( pi->shortcut_index[i] > 0 ) {
1020                         int     cut=pi->shortcut_index[i];
1021                         char    save_char=pi->button_text[i][cut];
1022                         pi->button_text[i][cut] = 0;
1023                         gr_get_string_size(&w, &h, pi->button_text[i]);
1024                         pi->button_text[i][cut] = save_char;
1025                         sx += w;
1026                 }
1027                 
1028                 if ( pi->shortcut_index[i] >= 0 ) {
1029                         gr_printf(sx, sy, NOX("%c"), 95);
1030                 }
1031         }
1032 }
1033
1034 // See if any of the button should change appearance based on mouse position
1035 void popup_force_draw_buttons(popup_info *pi)
1036 {
1037         int i,mouse_is_highlighting=0;
1038         UI_BUTTON *br, *b;
1039
1040         for ( i = 0; i < pi->nchoices; i++ ) {
1041                 br = &Popup_button_regions[i];
1042                 b = &Popup_buttons[i];
1043                 if ( br->button_down() ) {
1044                         b->draw_forced(2);
1045                         mouse_is_highlighting=1;
1046                         continue;
1047                 }
1048
1049                 if ( (b->button_hilighted()) || (br->button_hilighted() && !b->button_down()) ) {
1050                         Popup_default_choice=i;
1051                         mouse_is_highlighting=1;
1052                         b->draw_forced(1);
1053                 }
1054         }
1055
1056         // Only if mouse is not highlighting an option, let the default choice be drawn highlighted
1057         if ( (!mouse_is_highlighting) && (pi->nchoices>1) ) {
1058                 for ( i = 0; i < pi->nchoices; i++ ) {
1059                         b = &Popup_buttons[i];
1060                         // highlight the default choice
1061                         if ( i == Popup_default_choice ) {
1062                                 b->draw_forced(1);
1063                         }
1064                 }
1065         }
1066 }
1067
1068 static void popup_do(popup_info *pi)
1069 {
1070         int test = -1;
1071
1072         os_poll();
1073
1074         // if we were killed by a call to popup_kill_any_active(), kill the popup
1075         if (Popup_should_die) {
1076                 pi->choice = -1;
1077
1078                 if (pi->callback) {
1079                         (*(pi->callback))(-1);
1080                 } else {
1081                         popup_close(pi);
1082                 }
1083
1084                 return;
1085         }
1086
1087         // if we're flagged as should be running the state underneath, then do so
1088         if (pi->flags & PF_RUN_STATE) {
1089                 game_do_state(gameseq_get_state());
1090         }
1091         // otherwise just run the common functions (for networking,etc)
1092         else {
1093                 game_set_frametime(-1);
1094                 game_do_state_common(gameseq_get_state(), pi->flags & PF_NO_NETWORKING);        // do stuff common to all states
1095         }
1096
1097         // test the condition function or process for the window
1098         if (pi->condition && ((test = (*(pi->condition))()) > 0)) {
1099                 pi->choice = test;
1100
1101                 if (pi->callback) {
1102                         (*(pi->callback))(pi->choice);
1103                 } else {
1104                         popup_close(pi);
1105                 }
1106
1107                 return;
1108         } else {
1109                 int k = Popup_window.process();                                         // poll for input, handle mouse
1110
1111                 pi->choice = popup_process_keys(pi, k);
1112
1113                 if (pi->choice != POPUP_NOCHANGE) {
1114                         if (pi->callback) {
1115                                 (*(pi->callback))(pi->choice);
1116                         } else {
1117                                 popup_close(pi);
1118                         }
1119
1120                         return;
1121                 }
1122
1123                 pi->choice = popup_check_buttons(pi);
1124
1125                 if (pi->choice != POPUP_NOCHANGE) {
1126                         if (pi->callback) {
1127                                 (*(pi->callback))(pi->choice);
1128                         } else {
1129                                 popup_close(pi);
1130                         }
1131
1132                         return;
1133                 }
1134         }
1135
1136         // don't draw anything
1137         if ( !(pi->flags & PF_RUN_STATE) ) {
1138                 gr_restore_screen(pi->screen_id);
1139         }
1140
1141         // if this is an input popup, store the input text
1142         if (pi->flags & PF_INPUT) {
1143                 Popup_input.get_text(pi->input_text);
1144         }
1145
1146         Popup_window.draw();
1147         popup_force_draw_buttons(pi);
1148         popup_draw_msg_text(pi);
1149         popup_draw_button_text(pi);
1150
1151         gr_flip();
1152 }
1153
1154 void popup_do_frame()
1155 {
1156         if ( !Popup_is_active ) {
1157                 return;
1158         }
1159
1160         popup_info *pi = &Popup_info;
1161
1162         popup_do(pi);
1163 }
1164
1165 static int popup_do_sync()
1166 {
1167         popup_info *pi = &Popup_info;
1168
1169         SDL_assert(Popup_is_active);
1170
1171         popup_do(pi);
1172
1173         return pi->choice;
1174 }
1175
1176 // maybe assign a keyboard shortcut to this button
1177 // input:       pi              =>      popup information so far
1178 //                              i               =>      number of choice
1179 //                              str     => string for button press      
1180 void popup_maybe_assign_keypress(popup_info *pi, int n, char *str)
1181 {
1182         int i,j,len=0,next_char_is_shortcut=0;
1183
1184         pi->keypress[n]=-1;
1185         pi->shortcut_index[n]=-1;
1186
1187         len=strlen(str)+1;
1188
1189         pi->button_text[n] = (char*)malloc(len);
1190         memset(pi->button_text[n], 0, len);
1191
1192         j=0;
1193         // copy chars over, watching for underline meta-char '&'
1194         for (i=0; i<len-1; i++) {
1195                 if ( str[i] == '&' ) {
1196                         pi->shortcut_index[n]=i;
1197                         next_char_is_shortcut=1;
1198                 } else {
1199                         if ( next_char_is_shortcut ) {
1200                                 next_char_is_shortcut=0;
1201                                 char first_char_string[2];
1202                                 first_char_string[0]=str[i];
1203                                 first_char_string[1]=0;
1204                                 SDL_strlwr(first_char_string);
1205                                 pi->keypress[n] = first_char_string[0];
1206                         }
1207                         pi->button_text[n][j++]=str[i]; 
1208                 }
1209         }
1210 }
1211
1212 static void popup_internal(void (*callback)(int), int (*condition)(), int flags, int nchoices, va_list args)
1213 {
1214         int                     i;
1215         char                    *format, *s;
1216         popup_info *pi = &Popup_info;
1217
1218         pi->flags = flags;
1219         pi->callback = callback;
1220         pi->condition = condition;
1221
1222         SDL_assert( nchoices > 0 && nchoices <= POPUP_MAX_CHOICES );
1223         pi->nchoices = nchoices;
1224
1225         // get button text
1226         for (i=0; i<nchoices; i++ )     {
1227                 s = va_arg( args, char * );
1228                 pi->button_text[i] = NULL;
1229                 popup_maybe_assign_keypress(pi, i, s);
1230         }
1231
1232         // get msg text
1233         format = va_arg( args, char * );
1234         pi->raw_text[0] = 0;
1235         SDL_vsnprintf(pi->raw_text, SDL_arraysize(pi->raw_text), format, args);
1236
1237         if ( popup_init(pi) == -1 ) {
1238                 if (callback) {
1239                         (*callback)(-1);
1240                 }
1241
1242                 pi->choice = POPUP_ABORT;
1243                 popup_close(pi);
1244                 return;
1245         }
1246
1247         pi->screen_id = gr_save_screen();
1248
1249         gamesnd_play_iface(SND_POPUP_APPEAR);   // play sound when popup appears
1250
1251         Mouse_hidden = 0;
1252         Popup_is_active = 1;
1253 }
1254
1255 // input:               callback                =>              function to call on user action (popup done, etc.)
1256 //                              flags                   =>              formatting specificatons (PF_... shown above)
1257 //                              nchoices                =>              number of choices popup has
1258 //                              text_1          =>              text for first button
1259 //                              ...                     =>
1260 //                              text_n          =>              text for last button
1261 //                              msg text                =>              text msg for popup (can be of form "%s",pl->text)
1262 //
1263 // typical usage:
1264 //      popup(NULL, 0, 2, POPUP_YES, POPUP_NO, "Hey %s, do you want to quit", pl->callsign);
1265 void popup_callback(void (*callback)(int), int flags, int nchoices, ... )
1266 {
1267         va_list args;
1268
1269         if ( Popup_is_active ) {
1270                 Int3();         // should never happen
1271                 return;
1272         }
1273
1274         va_start(args, nchoices);
1275
1276         popup_internal(callback, NULL, flags, nchoices, args);
1277
1278         va_end(args);
1279 }
1280
1281 void popup(int flags, int nchoices, ...)
1282 {
1283         va_list args;
1284
1285         if ( Popup_is_active ) {
1286                 Int3();         // should never happen
1287                 return;
1288         }
1289
1290         va_start(args, nchoices);
1291
1292         popup_internal(NULL, NULL, flags, nchoices, args);
1293
1294         va_end(args);
1295 }
1296
1297 int popup_sync(int flags, int nchoices, ...)
1298 {
1299         popup_info *pi = &Popup_info;
1300         va_list args;
1301
1302         if ( Popup_is_active ) {
1303                 Int3();         // should never happen
1304                 return -1;
1305         }
1306
1307         va_start(args, nchoices);
1308
1309         popup_internal(NULL, NULL, flags, nchoices, args);
1310
1311         va_end(args);
1312
1313         int rval = pi->choice;  // if init failed, this will be ABORT
1314
1315         while (rval == POPUP_NOCHANGE) {
1316                 rval = popup_do_sync();
1317         }
1318
1319         popup_done();
1320
1321         if (rval == POPUP_ABORT) {
1322                 return -1;
1323         }
1324
1325         return rval;
1326 }
1327
1328 // determine if a popup is being drawn
1329 int popup_active()
1330 {
1331         return Popup_is_active;
1332 }
1333
1334 // This function displays a popup message box and every frame it checks the condition() function
1335 // which is passed in as an argument.
1336 // If the condition() returns TRUE, the message box ends itself.  This function returns whatever
1337 // the condition() did if the condition() occurred, and FALSE if the cancel button was pressed.
1338 int popup_till_condition(int (*condition)(), ...) 
1339 {
1340         popup_info *pi = &Popup_info;
1341         va_list args;
1342
1343         if ( Popup_is_active ) {
1344                 Int3();         // should never happen
1345                 return -1;
1346         }
1347
1348         va_start(args, condition);
1349
1350         popup_internal(NULL, condition, 0, 1, args);
1351
1352         va_end(args);
1353
1354         int rval = pi->choice;  // if init failed, this will be ABORT
1355
1356         while (rval == POPUP_NOCHANGE) {
1357                 rval = popup_do_sync();
1358         }
1359
1360         popup_done();
1361
1362         if (rval == POPUP_ABORT) {
1363                 return 0;
1364         }
1365
1366         return rval;
1367 }
1368
1369 // popup to return the value from an input box
1370 void popup_input(void (*callback)(int), int flags, const char *caption, int max_output_len)
1371 {
1372         popup_info *pi = &Popup_info;
1373
1374         if ( Popup_is_active ) {
1375                 Int3();         // should never happen
1376                 return;
1377         }
1378
1379         // make it an inputbox type popup
1380         pi->flags = flags | PF_INPUT;
1381
1382         pi->callback = NULL;
1383         pi->condition = NULL;
1384
1385         // add a cancel button
1386         pi->nchoices = 0;
1387         // popup_maybe_assign_keypress(&Popup_info, 0, "&Cancel");      
1388
1389         // get msg text
1390         SDL_assert(caption != NULL);
1391         SDL_strlcpy(pi->raw_text, caption, SDL_arraysize(pi->raw_text));
1392         SDL_assert(strlen(pi->raw_text) < POPUP_MAX_CHARS );
1393
1394         // set input text length
1395         if((max_output_len > POPUP_INPUT_MAX_CHARS) || (max_output_len == -1)){
1396                 pi->max_input_text_len = POPUP_INPUT_MAX_CHARS - 1;
1397         } else {
1398                 pi->max_input_text_len = max_output_len;
1399         }
1400
1401         if ( popup_init(pi) == -1 ) {
1402                 if (callback) {
1403                         (*callback)(-1);
1404                 }
1405
1406                 pi->choice = POPUP_ABORT;
1407                 popup_close(pi);
1408                 return;
1409         }
1410
1411         pi->screen_id = gr_save_screen();
1412
1413         // zero the popup input text
1414         SDL_zero(pi->input_text);
1415
1416         gamesnd_play_iface(SND_POPUP_APPEAR);   // play sound when popup appears
1417
1418         Mouse_hidden = 0;
1419         Popup_is_active = 1;
1420 }
1421
1422 const char *popup_input_sync(int flags, const char *caption, int max_output_len)
1423 {
1424         popup_info *pi = &Popup_info;
1425
1426         if ( Popup_is_active ) {
1427                 Int3();         // should never happen
1428                 return NULL;
1429         }
1430
1431         popup_input(NULL, flags, caption, max_output_len);
1432
1433         int rval = pi->choice;  // if init failed, this will be ABORT
1434
1435         while (rval == POPUP_NOCHANGE) {
1436                 rval = popup_do_sync();
1437         }
1438
1439         popup_done();
1440
1441         if (rval == POPUP_ABORT) {
1442                 return NULL;
1443         }
1444
1445         return pi->input_text;
1446 }
1447
1448 const char *popup_get_input_text()
1449 {
1450         popup_info *pi = &Popup_info;
1451
1452         return pi->input_text;
1453 }
1454
1455 int popup_running_state()
1456 {
1457         return Popup_running_state;
1458 }
1459
1460 // kill any active popup, forcing it to return -1 (similar to ESC)
1461 void popup_kill_any_active()
1462 {
1463         if(Popup_is_active){
1464                 Popup_should_die = 1;
1465         }
1466 }
1467
1468 // change the text inside of the popup 
1469 void popup_change_text(const char *new_text)
1470 {
1471         // copy the raw text
1472         SDL_strlcpy(Popup_info.raw_text, new_text, SDL_arraysize(Popup_info.raw_text));
1473
1474         // recalculate all display information
1475         popup_split_lines(&Popup_info);
1476 }
1477