FS2_Open
Open source remastering of the Freespace 2 engine
popup.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include <stdarg.h>
13 
14 #include "anim/animplay.h"
15 #include "freespace2/freespace.h"
17 #include "gamesnd/gamesnd.h"
18 #include "globalincs/alphacolors.h"
19 #include "graphics/font.h"
20 #include "io/key.h"
21 #include "io/mouse.h"
22 #include "osapi/osapi.h"
23 #include "parse/parselo.h"
24 #include "playerman/player.h"
25 #include "popup/popup.h"
26 #include "ui/ui.h"
27 
28 
29 #define POPUP_MAX_CHOICES 3 // max number of buttons allowed on popup
30 
31 #define POPUP_MAX_LINE_CHARS 256 // max chars of msg text allowed per line
32 #define POPUP_MAX_LINES 30 // max lines of text allowed
33 #define POPUP_MAX_CHARS 2048 // total max chars
34 #define POPUP_INPUT_MAX_CHARS 255 // max length of input string
35 
36 #define POPUP_NOCHANGE 100
37 #define POPUP_ABORT 101
38 
40  11,
41  19
42 };
43 
45  "slider",
46  "2_slider"
47 };
48 
50  { // GR_640
51  121, 109, 15, 105
52  },
53  { // GR_1024
54  195, 177, 30, 173
55  }
56 };
57 
59 // Internal popup flags
61 #define PF_INPUT (1<<0) // contents of the box is an inputbox and a caption
62 
64 // Popup data struct
66 typedef struct popup_info
67 {
68  int nchoices; // number of choices user can pick
69  char *button_text[POPUP_MAX_CHOICES]; // button text
70  int keypress[POPUP_MAX_CHOICES]; // button keypress shortcut
71  int shortcut_index[POPUP_MAX_CHOICES]; // what char should be underlines for shortcut
72  char raw_text[POPUP_MAX_CHARS]; // the unbroken text for the popup
73  char title[POPUP_MAX_LINE_CHARS]; // title text for popup (optional)
74  int nlines;
75  char msg_lines[POPUP_MAX_LINES][POPUP_MAX_LINE_CHARS]; // lines of text in popup
76  char input_text[POPUP_INPUT_MAX_CHARS]; // input box text (if this is an inputbox popup)
78  int web_cursor_flag[POPUP_MAX_CHOICES]; // flag for using web cursor over button
79 } popup_info;
80 
82 // UI Data and constants
85 UI_BUTTON Popup_buttons[POPUP_MAX_CHOICES]; // actual lit buttons
86 UI_BUTTON Popup_button_regions[POPUP_MAX_CHOICES]; // fake buttons used for mouse detection over text
87 UI_INPUTBOX Popup_input; // input box for the popup
88 UI_SLIDER2 Popup_slider; // if we have more text in the popup than can be displayed at once
89 
90 // extents for message portion of popup
92  { // GR_640
93  137, 106, 343, 113
94  },
95  { // GR_1024
96  219, 169, 558, 182
97  }
98 };
99 
100 // input popup info
101 // offset from the first y text line value to place the centered input box
103  40,
104  40
105 };
106 
107 // offset from the first y text line value to start drawing text
109  30,
110  30
111 };
112 
113 typedef struct popup_background
114 {
115  char *filename; // filename for background
116  int coords[2]; // coords to draw background at
118 
120 // Module globals
122 static int Popup_is_active=0;
123 static int Popup_should_die=0; // popup should quit during the next iteration of its loop
124 
125 static popup_info Popup_info;
126 static int Popup_flags;
127 
128 static int Title_coords[GR_NUM_RESOLUTIONS][5] =
129 {
130  { // GR_640
131  137, // x-left
132  106, // y-top
133  343, // width
134  26, // height
135  308 // center
136  },
137  { // GR_1024
138  220, // x-left
139  169, // y-top
140  553, // width
141  26, // height
142  496 // center
143  }
144 };
145 
146 static int Button_regions[GR_NUM_RESOLUTIONS][3][4] = {
147  { // GR_640
148  {464, 232, 510, 250}, // upper right pixel of text, lower right pixel of button
149  {464, 262, 510, 279},
150  {464, 292, 510, 308}
151  },
152  { // GR_1024
153  {752, 373, 806, 406}, // upper right pixel of text, lower right pixel of button
154  {752, 421, 806, 461},
155  {752, 468, 806, 506}
156  }
157 };
158 
159 static int Button_coords[GR_NUM_RESOLUTIONS][3][2] =
160 {
161  { // GR_640
162  {474, 224}, // upper left pixel
163  {474, 258},
164  {474, 286}
165  },
166  { // GR_1024
167  {758, 358}, // upper left pixel
168  {758, 413},
169  {758, 458},
170  }
171 };
172 
173 static popup_background Popup_background[GR_NUM_RESOLUTIONS][3] =
174 {
175  { // GR_640
176  { "Pop2", { 129, 99 } },
177  { "Pop2", { 129, 99 } },
178  { "Pop3", { 129, 99 } }
179  },
180  { // GR_1024
181  { "2_Pop2", { 206, 158 } },
182  { "2_Pop2", { 206, 158 } },
183  { "2_Pop3", { 206, 158 } }
184  }
185 };
186 
187 #define BUTTON_NEGATIVE 0
188 #define BUTTON_POSITIVE 1
189 #define BUTTON_GENERIC_FIRST 2
190 #define BUTTON_GENERIC_SECOND 3
191 #define BUTTON_GENERIC_THIRD 4
192 static char *Popup_button_filenames[GR_NUM_RESOLUTIONS][2][5] =
193 {
194  { // GR_640
195  {"Pop_00", // negative
196  "Pop_01", // positive
197  "Pop_02", // first generic
198  "Pop_03", // second generic
199  "Pop_04"}, // third generic
200 
201  {"Pop_00", // negative
202  "Pop_01", // positive
203  "PopD_00", // first generic
204  "PopD_01", // second generic
205  "PopD_02"}, // third generic
206  },
207  { // GR_1024
208  {"2_Pop_00", // negative
209  "2_Pop_01", // positive
210  "2_Pop_02", // first generic
211  "2_Pop_03", // second generic
212  "2_Pop_04"}, // third generic
213 
214  {"2_Pop_00", // negative
215  "2_Pop_01", // positive
216  "2_PopD_00", // first generic
217  "2_PopD_01", // second generic
218  "2_PopD_02"}, // third generic
219  }
220 };
221 
223 int Popup_default_choice; // which choice is highlighted (ie what gets choosen when enter is pressed)
224 
225 // see if any popup buttons have been pressed
226 // exit: POPUP_NOCHANGE => no buttons pressed
227 // >=0 => button index that was pressed
229 {
230  int i;
231  UI_BUTTON *b;
232 
233  for ( i = 0; i < pi->nchoices; i++ ) {
234  b = &Popup_button_regions[i];
235  if ( b->pressed() ) {
236  return i;
237  }
238 
239  b = &Popup_buttons[i];
240  if ( b->pressed() ) {
241  return i;
242  }
243  }
244 
245  return POPUP_NOCHANGE;
246 }
247 
248 // maybe play a sound when key up/down is pressed to switch default choice
250 {
251  if ( pi->nchoices > 1 ) {
252  int i, mouse_over=0;
253  UI_BUTTON *br, *b;
254 
255  // only play if mouse not currently highlighting a choice
256 
257  for ( i = 0; i < pi->nchoices; i++ ) {
258  br = &Popup_button_regions[i];
259  b = &Popup_buttons[i];
260  if ( br->button_down() ) {
261  mouse_over=1;
262  break;
263  }
264 
265  if ( br->button_hilighted() && !b->button_down() ) {
266  mouse_over=1;
267  break;
268  }
269 
270  if ( b->button_hilighted() ) {
271  mouse_over=1;
272  }
273  }
274 
275  if (!mouse_over) {
277  }
278  }
279 }
280 
281 // do any key processing here
282 // input: pi => data about the popup
283 // k => key that was pressed
284 //
285 // exit: 0 .. nchoices-1 => choice selected through keypress
286 // POPUP_ABORT => abort the popup
287 // POPUP_NOCHANGE => nothing happenned
288 int popup_process_keys(popup_info *pi, int k, int flags)
289 {
290  int i, masked_k;
291 
292  if ( k <= 0 ) {
293  return POPUP_NOCHANGE;
294  }
295 
296  for ( i = 0; i < pi->nchoices; i++ ) {
297  if ( pi->keypress[i] == key_to_ascii(k) ) {
299  Popup_buttons[i].press_button();
300  return i;
301  }
302  }
303 
304  switch(k) {
305 
306  case KEY_ENTER:
307  // select the current default choice
308  return Popup_default_choice;
309  break;
310 
311  case KEY_ESC:
312  // only process the escape key if this flag is not set
313  if(!(flags & PF_IGNORE_ESC)){
314  return POPUP_ABORT;
315  }
316  break;
317 
318  case KEY_DOWN:
319  case KEY_PAD2:
320  case KEY_TAB:
323  if ( Popup_default_choice >= pi->nchoices ) {
325  }
326  break;
327 
328  case KEY_UP:
329  case KEY_PAD8:
330  case KEY_SHIFTED+KEY_TAB:
333  if ( Popup_default_choice < 0 ) {
335  }
336  break;
337 
338  default:
339  break;
340  } // end switch
341 
342 
343  masked_k = k & ~KEY_CTRLED; // take out CTRL modifier only
344  if ( (Game_mode & GM_IN_MISSION) ) {
346  button_info_do(&Player->bi); // call functions based on status of button_info bit vectors
347  }
348 
349  return POPUP_NOCHANGE;
350 }
351 
352 // Split off the title and break up the body lines
354 {
355  int nlines, i, body_offset = 0;
356  int n_chars[POPUP_MAX_LINES];
357  const char *p_str[POPUP_MAX_LINES];
358 
360  n_chars[0]=0;
361 
362  nlines = split_str(pi->raw_text, 1000, n_chars, p_str, POPUP_MAX_LINES);
363  Assert(nlines >= 0 && nlines <= POPUP_MAX_LINES );
364 
365  if ( flags & (PF_TITLE | PF_TITLE_BIG) ) {
366  // get first line out
367  strncpy(pi->title, p_str[0], n_chars[0]);
368  pi->title[n_chars[0]] = 0;
369  body_offset = 1;
370  }
371 
372  if ( flags & PF_BODY_BIG ) {
374  }
375 
376  nlines = split_str(pi->raw_text, Popup_text_coords[gr_screen.res][2], n_chars, p_str, POPUP_MAX_LINES);
377  Assert(nlines >= 0 && nlines <= POPUP_MAX_LINES );
378 
379  pi->nlines = nlines - body_offset;
380 
381  for ( i = 0; i < pi->nlines; i++ ) {
382  Assert(n_chars[i+body_offset] < POPUP_MAX_LINE_CHARS);
383  strncpy(pi->msg_lines[i], p_str[i+body_offset], n_chars[i+body_offset]);
384  pi->msg_lines[i][n_chars[i+body_offset]] = 0;
385  }
386 
388 }
389 
390 // figure out what filename to use for the button icon
392 {
393  char *fname = NULL;
394  int is_tiny=0;
395 
396  // check for special button texts and if found, use specialized buttons for them.
397  if ((!stricmp(pi->button_text[i], POPUP_OK + 1) || !stricmp(pi->button_text[i], POPUP_YES + 1)) && !(flags & PF_NO_SPECIAL_BUTTONS)){
398  return Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
399  }
400 
401  if ((!stricmp(pi->button_text[i], POPUP_CANCEL + 1) || !stricmp(pi->button_text[i], POPUP_NO + 1)) && !(flags & PF_NO_SPECIAL_BUTTONS)){
402  return Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
403  }
404 
405  switch (pi->nchoices) {
406  case 0:
407  fname = "";
408  break;
409  case 1:
410  if ( (flags & PF_USE_AFFIRMATIVE_ICON) && !(flags & PF_NO_SPECIAL_BUTTONS) ) {
411  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
412  } else if ( flags & PF_USE_NEGATIVE_ICON && !(flags & PF_NO_SPECIAL_BUTTONS) ) {
413  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
414  } else {
415  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
416  }
417  break;
418 
419  case 2:
420  if ( flags & PF_USE_NEGATIVE_ICON && i==0 ) {
421  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_NEGATIVE];
422  break;
423  }
424 
425  if ( flags & PF_USE_AFFIRMATIVE_ICON && i==1 ) {
426  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_POSITIVE];
427  break;
428  }
429 
430  if ( i == 0 ) {
431  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
432  } else {
433  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_SECOND];
434  }
435 
436  break;
437 
438  case 3:
439  switch(i) {
440  case 0:
441  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_FIRST];
442  break;
443  case 1:
444  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_SECOND];
445  break;
446  case 2:
447  fname = Popup_button_filenames[gr_screen.res][is_tiny][BUTTON_GENERIC_THIRD];
448  break;
449  }
450  break;
451  }
452 
453  return fname;
454 }
455 
456 // bogus handling function for slider (we don't need it to do anything)
458 {
459 }
460 
461 // init the Popup window
463 {
464  int i;
465  UI_BUTTON *b;
466  popup_background *pbg;
467  char *fname;
468 
469  if(pi->nchoices == 0){
470  pbg = &Popup_background[gr_screen.res][0];
471  } else {
472  pbg = &Popup_background[gr_screen.res][pi->nchoices-1];
473  }
474 
475  // anytime in single player, and multiplayer, not in mission, go ahead and stop time
477  game_stop_time();
478  }
479 
480  // create base window
481  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);
482  Popup_window.set_foreground_bmap(pbg->filename);
483 
484  // create buttons
485  for (i=0; i<pi->nchoices; i++) {
486  b = &Popup_buttons[i];
487  // accommodate single-choice positive icon being positioned differently
488  if ( (pi->nchoices == 1) && (flags&PF_USE_AFFIRMATIVE_ICON) ) {
489  b->create(&Popup_window, "", Button_coords[gr_screen.res][i+1][0], Button_coords[gr_screen.res][i+1][1], 30, 25, 0, 1);
490  } else {
491  b->create(&Popup_window, "", Button_coords[gr_screen.res][i][0], Button_coords[gr_screen.res][i][1], 30, 25, 0, 1);
492  }
493 
494  fname = popup_get_button_filename(pi, i, flags);
495  b->set_bmaps(fname, 3, 0);
497  if ( pi->keypress[i] >= 0 ) {
498  b->set_hotkey(pi->keypress[i]);
499  }
500 
501  // create invisible buttons to detect mouse presses... can't use mask since button region is dynamically sized
502  int lx, w, h;
503 
504  gr_get_string_size(&w, &h, pi->button_text[i]);
505  lx = Button_regions[gr_screen.res][i][0] - w;
506  b = &Popup_button_regions[i];
507 
508  // accommodate single-choice positive icon being positioned differently
509  if ( (pi->nchoices == 1) && (flags&PF_USE_AFFIRMATIVE_ICON) ) {
510  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);
511  } else {
512  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);
513  }
514 
515  b->hide();
516  }
517 
518  // webcursor setup
519  if (Web_cursor_bitmap >= 0) {
520  if (flags & PF_WEB_CURSOR_1) {
521  Popup_buttons[1].set_custom_cursor_bmap(Web_cursor_bitmap);
522  }
523  if (flags & PF_WEB_CURSOR_2) {
524  Popup_buttons[2].set_custom_cursor_bmap(Web_cursor_bitmap);
525  }
526  }
527 
528  // if this is an input popup, create and center the popup
529  if(flags & PF_INPUT){
531  Popup_input.set_focus();
532  }
533 
535  Popup_should_die = 0;
536 
537  if (flags & PF_RUN_STATE) {
539  } else {
541  }
542 
543  popup_split_lines(pi, flags);
544 
545  // create the popup slider (which we may not need to use
548 
549  return 0;
550 }
551 
552 // called when a popup goes away
553 void popup_close(popup_info *pi, int screen_id)
554 {
555  int i;
556 
557  gamesnd_play_iface(SND_POPUP_DISAPPEAR); // play sound when popup disappears
558 
559  for (i=0; i<pi->nchoices; i++ ) {
560  if ( pi->button_text[i] != NULL ) {
561  vm_free(pi->button_text[i]);
562  pi->button_text[i] = NULL;
563  }
564  }
565 
566  if(screen_id >= 0){
567  gr_free_screen(screen_id);
568  }
569  Popup_window.destroy();
570  anim_ignore_next_frametime(); // to avoid skips in animation since next frametime is saturated
571  game_flush();
572 
573  Popup_is_active = 0;
575 
576  // anytime in single player, and multiplayer, not in mission, go ahead and stop time
578  game_start_time();
579 }
580 
581 // set the popup text color
583 {
584  if ( flags & PF_BODY_RED ) {
586  return;
587  }
588 
589  if ( flags & PF_BODY_GREEN ) {
591  return;
592  }
593 
594  if ( flags & PF_BODY_BLUE ) {
596  return;
597  }
598 
600 }
601 
602 // set the popup text color
604 {
605  if ( flags & PF_TITLE_RED ) {
607  return;
608  }
609 
610  if ( flags & PF_TITLE_GREEN ) {
612  return;
613  }
614 
615  if ( flags & PF_TITLE_BLUE ) {
617  return;
618  }
619 
620  if ( flags & PF_TITLE_WHITE ) {
622  return;
623  }
624 
626 }
627 
628 // Draw the title centered within the popup
629 void popup_draw_title(int sy, char *line, int flags)
630 {
631  int w, h, sx;
632 
633  if ( flags & PF_TITLE_BIG ) {
635  } else {
637  }
638 
639  gr_get_string_size(&w, &h, line);
640  sx = fl2i(Title_coords[gr_screen.res][4] - w/2.0f + 0.5f);
641 
642  popup_set_title_color(flags);
643  gr_string(sx,sy,line,GR_RESIZE_MENU);
644 }
645 
646 // calculate the starting display index
648 {
649  // we're basically ignoring any titles here.
651  return 0;
652  }
653 
654  // otherwise, we want to see what item index the slider is on
655  return Popup_slider.get_currentItem();
656 }
657 
658 // Figure out the y-coord to start drawing the popup text. The text
659 // is centered vertically within the popup.
661 {
662  int sy, total_h=0;
663  int num_lines = pi->nlines > Popup_max_display[gr_screen.res] ? Popup_max_display[gr_screen.res] : pi->nlines;
664 
665  if ( flags & (PF_TITLE | PF_TITLE_BIG) ) {
666  if ( flags & PF_TITLE_BIG ) {
668  } else {
670  }
671  total_h += gr_get_font_height();
672  }
673 
674  if ( flags & PF_BODY_BIG ) {
676  } else {
678  }
679 
680  total_h += num_lines * gr_get_font_height();
681  sy = fl2i((Popup_text_coords[gr_screen.res][1] + Popup_text_coords[gr_screen.res][3]/2.0f) - total_h/2.0f + 0.5f);
682 
683  // if this is an input style box, add in some y
684  if(flags & PF_INPUT){
686  }
687 
688  return sy;
689 }
690 
691 // Draw the message text nicely formatted in the popup
693 {
694  int sx, sy, i, w, h;
695  int line_index;
696  int line_count;
697 
698  // figure out the starting display
699  line_index = popup_calc_starting_index(pi);
700 
701  // figure out the starting y:
702  sy = popup_calc_starting_y(pi, flags);
703 
704  // draw title if required
705  if ( flags & (PF_TITLE | PF_TITLE_BIG) ) {
706  popup_draw_title(sy, pi->title, flags);
707  sy += gr_get_font_height();
708  }
709 
710  // draw body
711  if ( flags & PF_BODY_BIG ) {
713  } else {
715  }
716 
717  popup_set_text_color(flags);
718  line_count = 0;
719  for ( i = line_index; i < pi->nlines; i++, line_count++ ) {
720  // if we've already displayed the max # of lines
721  if(line_count >= Popup_max_display[gr_screen.res]){
722  break;
723  }
724 
725  gr_get_string_size(&w, &h, pi->msg_lines[i]);
726  sx = fl2i(Title_coords[gr_screen.res][4] - w/2.0f + 0.5f);
727  gr_string(sx, sy + line_count * h, pi->msg_lines[i], GR_RESIZE_MENU);
728  }
729 
730  // maybe draw "more"
731  h = 10;
732  if(i < pi->nlines){
734  gr_string(Title_coords[gr_screen.res][4], sy + (Popup_max_display[gr_screen.res]) * h, XSTR("More", 459), GR_RESIZE_MENU);
735  }
736 
737  gr_set_font(FONT1); // reset back to regular font size
738 }
739 
740 // Draw the button text nicely formatted in the popup
742 {
743  int w, h, i, sx, sy;
744 
746 
747  for ( i=0; i < pi->nchoices; i++ ) {
748  gr_get_string_size(&w, &h, pi->button_text[i]);
749 
750  if ( (pi->nchoices == 1) && (flags&PF_USE_AFFIRMATIVE_ICON) ) {
751  sx = Button_regions[gr_screen.res][i+1][0]-w;
752  sy = Button_regions[gr_screen.res][i+1][1]+4;
753  } else {
754  sx = Button_regions[gr_screen.res][i][0]-w;
755  sy = Button_regions[gr_screen.res][i][1]+4;
756  }
757 
758  gr_string(sx, sy, pi->button_text[i], GR_RESIZE_MENU);
759 
760  // figure out where to draw underline char
761  if ( pi->shortcut_index[i] > 0 ) {
762  int cut=pi->shortcut_index[i];
763  char save_char=pi->button_text[i][cut];
764  pi->button_text[i][cut] = 0;
765  gr_get_string_size(&w, &h, pi->button_text[i]);
766  pi->button_text[i][cut] = save_char;
767  sx += w;
768  }
769 
770  if ( pi->shortcut_index[i] >= 0 ) {
771  gr_printf_menu(sx, sy, NOX("%c"), 95);
772  }
773  }
774 }
775 
776 // See if any of the button should change appearance based on mouse position
778 {
779  int i,mouse_is_highlighting=0;
780  UI_BUTTON *br, *b;
781 
782  for ( i = 0; i < pi->nchoices; i++ ) {
783  br = &Popup_button_regions[i];
784  b = &Popup_buttons[i];
785  if ( br->button_down() ) {
786  b->draw_forced(2);
787  mouse_is_highlighting=1;
788  continue;
789  }
790 
791  if ( (b->button_hilighted()) || (br->button_hilighted() && !b->button_down()) ) {
793  mouse_is_highlighting=1;
794  b->draw_forced(1);
795  }
796  }
797 
798  // Only if mouse is not highlighting an option, let the default choice be drawn highlighted
799  if ( (!mouse_is_highlighting) && (pi->nchoices>1) ) {
800  for ( i = 0; i < pi->nchoices; i++ ) {
801  b = &Popup_buttons[i];
802  // highlight the default choice
803  if ( i == Popup_default_choice ) {
804  b->draw_forced(1);
805  }
806  }
807  }
808 }
809 
810 // exit: -1 => error
811 // 0..nchoices-1 => choice
812 int popup_do(popup_info *pi, int flags)
813 {
814  int screen_id, choice = -1, done = 0;
815 
816  if ( popup_init(pi, flags) == -1 ){
817  return -1;
818  }
819 
820  screen_id = gr_save_screen();
821 
822  int old_max_w_unscaled = gr_screen.max_w_unscaled;
823  int old_max_h_unscaled = gr_screen.max_h_unscaled;
824  int old_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed;
825  int old_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed;
826 
828 
829  while(!done) {
830  int k;
831 
832  os_poll();
833 
834  // if we were killed by a call to popup_kill_any_active(), kill the popup
835  if(Popup_should_die){
836  choice = -1;
837  break;
838  }
839 
840  // if we're flagged as should be running the state underneath, then do so
841  if(flags & PF_RUN_STATE){
843  }
844  // otherwise just run the common functions (for networking,etc)
845  else {
846  game_set_frametime(-1);
847  game_do_state_common(gameseq_get_state(),flags & PF_NO_NETWORKING); // do stuff common to all states
848  }
849 
850  k = Popup_window.process(); // poll for input, handle mouse
851  choice = popup_process_keys(pi, k, flags);
852  if ( choice != POPUP_NOCHANGE ) {
853  done=1;
854  }
855 
856  if ( !done ) {
857  choice = popup_check_buttons(pi);
858  if ( choice != POPUP_NOCHANGE ) {
859  done=1;
860  }
861  }
862 
863  // don't draw anything
864  if(!(flags & PF_RUN_STATE)){
865  gr_restore_screen(screen_id);
866  }
867 
868  // if this is an input popup, store the input text
869  if(flags & PF_INPUT){
870  Popup_input.get_text(pi->input_text);
871  }
872 
873  Popup_window.draw();
875  popup_draw_msg_text(pi, flags);
876  popup_draw_button_text(pi, flags);
877  gr_flip();
878  }
879 
880  gr_set_screen_scale(old_max_w_unscaled, old_max_h_unscaled, old_max_w_unscaled_zoomed, old_max_h_unscaled_zoomed);
881 
882  popup_close(pi,screen_id);
883  return choice;
884 }
885 
887 {
888  int screen_id, choice = -1, done = 0;
889  int test;
890  screen_id = gr_save_screen();
891  if ( popup_init(pi, flags) == -1 )
892  return -1;
893 
894  int old_max_w_unscaled = gr_screen.max_w_unscaled;
895  int old_max_h_unscaled = gr_screen.max_h_unscaled;
896  int old_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed;
897  int old_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed;
898 
900 
901  while(!done) {
902  int k;
903 
904  os_poll();
905 
906  game_set_frametime(-1);
907  game_do_state_common(gameseq_get_state()); // do stuff common to all states
908  gr_restore_screen(screen_id);
909 
910  // draw one frame first
911  Popup_window.draw();
913  popup_draw_msg_text(pi, flags);
914  popup_draw_button_text(pi, flags);
915  gr_flip();
916 
917  // test the condition function or process for the window
918  if ((test = condition()) > 0) {
919  done = 1;
920  choice = test;
921  } else {
922  k = Popup_window.process(); // poll for input, handle mouse
923  choice = popup_process_keys(pi, k, flags);
924  if ( choice != POPUP_NOCHANGE ) {
925  done=1;
926  }
927 
928  if ( !done ) {
929  choice = popup_check_buttons(pi);
930  if ( choice != POPUP_NOCHANGE ) {
931  done=1;
932  }
933  }
934  }
935  }
936 
937  gr_set_screen_scale(old_max_w_unscaled, old_max_h_unscaled, old_max_w_unscaled_zoomed, old_max_h_unscaled_zoomed);
938 
939  popup_close(pi,screen_id);
940  return choice;
941 }
942 
943 
944 // maybe assign a keyboard shortcut to this button
945 // input: pi => popup information so far
946 // i => number of choice
947 // str => string for button press
948 void popup_maybe_assign_keypress(popup_info *pi, int n, char *str)
949 {
950  int i,j,len=0,next_char_is_shortcut=0;
951 
952  pi->keypress[n]=-1;
953  pi->shortcut_index[n]=-1;
954 
955  len=strlen(str)+1;
956 
957  pi->button_text[n] = (char*)vm_malloc(len);
958  memset(pi->button_text[n], 0, len);
959 
960  j=0;
961  // copy chars over, watching for underline meta-char '&'
962  for (i=0; i<len-1; i++) {
963  if ( str[i] == '&' ) {
964  pi->shortcut_index[n]=i;
965  next_char_is_shortcut=1;
966  } else {
967  if ( next_char_is_shortcut ) {
968  next_char_is_shortcut=0;
969  char first_char_string[2];
970  first_char_string[0]=str[i];
971  first_char_string[1]=0;
972  strlwr(first_char_string);
973  pi->keypress[n] = first_char_string[0];
974  }
975  pi->button_text[n][j++]=str[i];
976  }
977  }
978 }
979 
980 // input: flags => flags => formatting specificatons (PF_...)
981 // nchoices => number of choices popup has
982 // text_1 => text for first button
983 // ... =>
984 // text_n => text for last button
985 // msg text => text msg for popup (can be of form "%s",pl->text)
986 //
987 // exit: choice selected (0..nchoices-1)
988 // will return -1 if there was an error or popup was aborted
989 //
990 // typical usage:
991 //
992 // rval = popup(0, 2, POPUP_OK, POPUP_CANCEL, "Sorry %s, try again", pl->callsign);
993 int popup(int flags, int nchoices, ... )
994 {
995  int i, choice;
996  char *format, *s;
997  va_list args;
998 
999  if ( Popup_is_active ) {
1000  Int3(); // should never happen
1001  return -1;
1002  }
1003 
1004  Popup_flags = flags;
1005 
1006  Assert( nchoices > 0 && nchoices <= POPUP_MAX_CHOICES );
1007  Popup_info.nchoices = nchoices;
1008 
1009  va_start(args, nchoices );
1010 
1011  // get button text
1012  for (i=0; i<nchoices; i++ ) {
1013  s = va_arg( args, char * );
1014  Popup_info.button_text[i] = NULL;
1015  popup_maybe_assign_keypress(&Popup_info, i, s);
1016  }
1017 
1018  // get msg text
1019  format = va_arg( args, char * );
1020  vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text)-1, format, args);
1021  va_end(args);
1022  Popup_info.raw_text[sizeof(Popup_info.raw_text)-1] = '\0';
1023 
1024  gamesnd_play_iface(SND_POPUP_APPEAR); // play sound when popup appears
1025 
1026  Mouse_hidden = 0;
1027  Popup_is_active = 1;
1028 
1029  choice = popup_do( &Popup_info, flags );
1030  switch(choice) {
1031  case POPUP_ABORT:
1032  return -1;
1033  default:
1034  return choice;
1035  } // end switch
1036 }
1037 
1038 // determine if a popup is being drawn
1040 {
1041  return Popup_is_active;
1042 }
1043 
1044 // This function displays a popup message box and every frame it checks the condition() function
1045 // which is passed in as an argument.
1046 // If the condition() returns TRUE, the message box ends itself. This function returns whatever
1047 // the condition() did if the condition() occurred, and FALSE if the cancel button was pressed.
1048 int popup_till_condition(int (*condition)(), ...)
1049 {
1050  int choice;
1051  char *format, *s;
1052  va_list args;
1053  int flags = 0;
1054 
1055  if ( Popup_is_active ) {
1056  Int3(); // should never happen
1057  return -1;
1058  }
1059  //int nchoices = 1;
1060  Popup_info.nchoices = 1;
1061 
1062  Popup_flags = 0;
1063 
1064  va_start(args, condition );
1065 
1066  // get button text
1067  s = va_arg( args, char * );
1068  Popup_info.button_text[0] = NULL;
1069  popup_maybe_assign_keypress(&Popup_info, 0, s);
1070 
1071  // get msg text
1072  format = va_arg( args, char * );
1073  vsnprintf(Popup_info.raw_text, sizeof(Popup_info.raw_text)-1, format, args);
1074  va_end(args);
1075  Popup_info.raw_text[sizeof(Popup_info.raw_text)-1] = '\0';
1076 
1077  gamesnd_play_iface(SND_POPUP_APPEAR); // play sound when popup appears
1078 
1079  Mouse_hidden = 0;
1080  Popup_is_active = 1;
1081 
1082  choice = popup_do_with_condition( &Popup_info, flags, condition );
1083  switch(choice) {
1084  case POPUP_ABORT:
1085  return 0;
1086  default:
1087  return choice;
1088  } // end switch
1089 }
1090 
1091 // popup to return the value from an input box
1092 char *popup_input(int flags, const char *caption, int max_output_len)
1093 {
1094  if ( Popup_is_active ) {
1095  Int3(); // should never happen
1096  return NULL;
1097  }
1098 
1099  // make it an inputbox type popup
1100  Popup_flags = flags;
1101  Popup_flags |= PF_INPUT;
1102 
1103  // add a cancel button
1104  Popup_info.nchoices = 0;
1105  // popup_maybe_assign_keypress(&Popup_info, 0, "&Cancel");
1106 
1107  // get msg text
1108  Assert(caption != NULL);
1109  strcpy_s(Popup_info.raw_text, caption);
1110  Assert(strlen(Popup_info.raw_text) < POPUP_MAX_CHARS );
1111 
1112  // set input text length
1113  if((max_output_len > POPUP_INPUT_MAX_CHARS) || (max_output_len == -1)){
1114  Popup_info.max_input_text_len = POPUP_INPUT_MAX_CHARS - 1;
1115  } else {
1116  Popup_info.max_input_text_len = max_output_len;
1117  }
1118 
1119  // zero the popup input text
1120  memset(Popup_info.input_text, 0, POPUP_INPUT_MAX_CHARS);
1121 
1122  gamesnd_play_iface(SND_POPUP_APPEAR); // play sound when popup appears
1123 
1124  Mouse_hidden = 0;
1125  Popup_is_active = 1;
1126 
1127  // if the user cancelled
1128  if(popup_do(&Popup_info, Popup_flags) == POPUP_ABORT){
1129  return NULL;
1130  }
1131 
1132  // otherwise return the
1133  return Popup_info.input_text;
1134 }
1135 
1137 {
1138  return Popup_running_state;
1139 }
1140 
1141 // kill any active popup, forcing it to return -1 (similar to ESC)
1143 {
1144  if(Popup_is_active){
1145  Popup_should_die = 1;
1146  }
1147 }
1148 
1149 // change the text inside of the popup
1150 void popup_change_text(const char *new_text)
1151 {
1152  // copy the raw text
1153  strcpy_s(Popup_info.raw_text,new_text);
1154 
1155  // recalculate all display information
1156  popup_split_lines(&Popup_info,Popup_flags);
1157 }
void game_flush()
Definition: fredstubs.cpp:83
GLenum GLsizei GLenum format
Definition: Gl.h:1509
void set_highlight_action(void(*_user_function)(void))
Definition: button.cpp:375
int i
Definition: multi_pxo.cpp:466
#define vm_free(ptr)
Definition: pstypes.h:548
int button_hilighted()
Definition: button.cpp:387
#define KEY_DOWN
Definition: key.h:180
void game_do_state_common(int, int)
Definition: fredstubs.cpp:195
void game_set_frametime(int)
Definition: fredstubs.cpp:196
int Game_mode
Definition: systemvars.cpp:24
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
void gr_flip()
Definition: 2d.cpp:2113
#define GR_RESIZE_MENU
Definition: 2d.h:684
color Color_red
Definition: alphacolors.cpp:34
#define KEY_PAD8
Definition: key.h:164
GLenum condition
Definition: Glext.h:8726
#define gr_restore_screen
Definition: 2d.h:826
int Web_cursor_bitmap
Definition: 2d.cpp:60
void set_foreground_bmap(char *fname)
Definition: window.cpp:127
int max_input_text_len
Definition: popup.cpp:77
int gameseq_get_state(void)
Definition: fredstubs.cpp:60
Assert(pm!=NULL)
#define GR_NUM_RESOLUTIONS
Definition: 2d.h:651
__inline void gr_string(int x, int y, const char *string, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:769
int res
Definition: 2d.h:370
int max_h_unscaled
Definition: 2d.h:361
void game_start_time()
Definition: fredstubs.cpp:188
void set_focus()
Definition: gadget.cpp:321
int Mouse_hidden
Definition: mouse.cpp:52
int Dead_key_set_size
Definition: keycontrol.cpp:475
void gr_set_screen_scale(int w, int h, int zoom_w, int zoom_h, int max_w, int max_h, int center_w, int center_h, bool force_stretch)
Definition: 2d.cpp:104
virtual void hide(int n)
Definition: gadget.cpp:207
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
#define UI_INPUTBOX_FLAG_ESC_FOC
Definition: ui.h:38
Definition: ui.h:195
int max_w_unscaled
Definition: 2d.h:361
#define Int3()
Definition: pstypes.h:292
int key_to_ascii(int keycode)
Definition: key.cpp:336
color Color_blue
Definition: alphacolors.cpp:31
popup dialog box appeared
Definition: gamesnd.h:315
void draw_forced(int frame_num)
Definition: button.cpp:104
void destroy()
Definition: window.cpp:189
int nchoices
Definition: popup.cpp:68
void press_button()
Definition: button.cpp:399
int set_bmaps(char *ani_filename, int nframes=3, int start_frame=1)
Definition: gadget.cpp:71
void game_do_state(int)
Definition: fredstubs.cpp:205
char title[POPUP_MAX_LINE_CHARS]
Definition: popup.cpp:73
int get_currentItem()
Definition: slider2.cpp:212
GLuint coords
Definition: Glext.h:6925
#define GM_MULTIPLAYER
Definition: systemvars.h:18
#define gr_save_screen
Definition: 2d.h:825
void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _h, int _numberItems, char *_bitmapSliderControl, void(*_upCallback)(), void(*_downCallback)(), void(*_captureCallback)())
Definition: slider2.cpp:21
char input_text[POPUP_INPUT_MAX_CHARS]
Definition: popup.cpp:76
int pressed()
Definition: button.cpp:325
#define KEY_SHIFTED
Definition: key.h:62
void process_set_of_keys(int key, int count, int *list)
char * filename
Definition: popup.cpp:115
#define w(p)
Definition: modelsinc.h:68
#define KEY_ENTER
Definition: key.h:125
#define UI_INPUTBOX_FLAG_TEXT_CEN
Definition: ui.h:44
int Dead_key_set[]
Definition: keycontrol.cpp:336
GLdouble s
Definition: Glext.h:5321
Definition: ui.h:428
int split_str(const char *src, int max_pixel_w, int *n_chars, const char **p_str, int max_lines, char ignore_char)
Definition: parselo.cpp:3412
int nlines
Definition: popup.cpp:74
GLclampd n
Definition: Glext.h:7286
void os_poll()
Definition: osapi.cpp:748
const char * XSTR(const char *str, int index)
Definition: localize.cpp:851
user_click (mouse selects a control)
Definition: gamesnd.h:305
#define GM_IN_MISSION
Definition: systemvars.h:23
#define NOX(s)
Definition: pstypes.h:473
void anim_ignore_next_frametime()
Definition: animplay.cpp:1062
GLbitfield flags
Definition: Glext.h:6722
popup dialog box goes away
Definition: gamesnd.h:316
#define vm_malloc(size)
Definition: pstypes.h:547
void set_hotkey(int keycode)
Definition: gadget.cpp:280
void button_info_do(button_info *bi)
int max_w_unscaled_zoomed
Definition: 2d.h:362
GLboolean GLboolean GLboolean b
Definition: Glext.h:5781
color Color_green
Definition: alphacolors.cpp:31
color Color_bright_white
Definition: alphacolors.cpp:32
#define FONT1
Definition: font.h:65
void create(UI_WINDOW *wnd, char *_text, int _x, int _y, int _w, int _h, int do_repeat=0, int ignore_focus=0)
Definition: button.cpp:26
#define KEY_ESC
Definition: key.h:124
int web_cursor_flag[POPUP_MAX_CHOICES]
Definition: popup.cpp:78
#define FONT2
Definition: font.h:66
int button_down()
Definition: button.cpp:363
#define fl2i(fl)
Definition: floating.h:33
void create(int _x, int _y, int _w, int _h, int _flags, int _f_id=-1)
Definition: window.cpp:140
player * Player
Definition: ui.h:584
screen gr_screen
Definition: 2d.cpp:46
void gr_get_string_size(int *w, int *h, const char *text, int len=9999)
Definition: font.cpp:196
int gr_get_font_height()
Definition: font.cpp:187
void common_play_highlight_sound()
Definition: gamesnd.cpp:1151
void create(UI_WINDOW *wnd, int _x, int _y, int _w, int _textlen, char *_text, int _flags=0, int pixel_lim=-1, color *clr=NULL)
Definition: inputbox.cpp:101
void gr_reset_screen_scale()
Definition: 2d.cpp:176
color Color_more_bright
Definition: alphacolors.cpp:28
#define UI_INPUTBOX_FLAG_INVIS
Definition: ui.h:35
#define gr_free_screen
Definition: 2d.h:827
#define KEY_CTRLED
Definition: key.h:64
#define KEY_TAB
Definition: key.h:127
int keypress[POPUP_MAX_CHOICES]
Definition: popup.cpp:70
char raw_text[POPUP_MAX_CHARS]
Definition: popup.cpp:72
void get_text(char *out)
Definition: inputbox.cpp:484
#define KEY_UP
Definition: key.h:179
int coords[2]
Definition: popup.cpp:116
GLenum GLsizei len
Definition: Glext.h:6283
#define GM_NORMAL
Definition: systemvars.h:19
color Color_bright_blue
Definition: alphacolors.cpp:31
void gamesnd_play_iface(int n)
Definition: gamesnd.cpp:260
char * button_text[POPUP_MAX_CHOICES]
Definition: popup.cpp:69
#define KEY_PAD2
Definition: key.h:158
void draw()
Definition: window.cpp:220
void gr_set_font(int fontnum)
Definition: font.cpp:566
int shortcut_index[POPUP_MAX_CHOICES]
Definition: popup.cpp:71
void game_stop_time()
Definition: fredstubs.cpp:189
char msg_lines[POPUP_MAX_LINES][POPUP_MAX_LINE_CHARS]
Definition: popup.cpp:75
void strlwr(char *s)
int process(int key_in=-1, int process_mouse=1)
Definition: window.cpp:401
void _cdecl gr_printf_menu(int x, int y, const char *format,...)
Definition: font.cpp:314
#define UI_INPUTBOX_FLAG_KEYTHRU
Definition: ui.h:36
#define stricmp(s1, s2)
Definition: config.h:271
button_info bi
Definition: player.h:125
int max_h_unscaled_zoomed
Definition: 2d.h:362
#define UI_INPUTBOX_FLAG_ESC_CLR
Definition: ui.h:37
#define strcpy_s(...)
Definition: safe_strings.h:67
void set_custom_cursor_bmap(int bmap_id)
Definition: ui.h:248