]> icculus.org git repositories - btb/d2x.git/blob - main/newmenu.c
use the orientation parameter of g3_draw_bitmap
[btb/d2x.git] / main / newmenu.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  *
16  * Routines for menus.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <ctype.h>
29 #if !defined(_MSC_VER) && !defined(macintosh)
30 #include <unistd.h>
31 #endif
32 #include <limits.h>
33 #ifdef MACINTOSH
34 #include <Events.h>
35 #endif
36
37 #include <physfs.h>
38
39 #include "dxxerror.h"
40 #include "gr.h"
41 #include "vid.h"
42 #include "mono.h"
43 #include "key.h"
44 #include "inferno.h"
45 #include "iff.h"
46 #include "u_mem.h"
47 #include "mouse.h"
48 #include "joy.h"
49 #include "timer.h"
50 #if defined (TACTILE)
51  #include "tactile.h"
52 #endif
53
54
55 #define MAXDISPLAYABLEITEMS 15
56
57 #define LHX(x)      ((x)*(MenuHires?2:1))
58 #define LHY(y)      ((y)*(MenuHires?2.4:1))
59
60 #define TITLE_FONT      HUGE_FONT
61 #define NORMAL_FONT     MEDIUM1_FONT    // normal, non-highlighted item
62 #define SELECTED_FONT   MEDIUM2_FONT    // highlighted item
63 #define SUBTITLE_FONT   MEDIUM3_FONT
64
65 #define NORMAL_CHECK_BOX    "\x81"  // 129
66 #define CHECKED_CHECK_BOX   "\x82"  // 130
67
68 #define NORMAL_RADIO_BOX    "\x7f"  // 127
69 #define CHECKED_RADIO_BOX   "\x80"  // 128
70 #define CURSOR_STRING       "_"
71 #define SLIDER_LEFT         "\x83"  // 131
72 #define SLIDER_RIGHT        "\x84"  // 132
73 #define SLIDER_MIDDLE       "\x85"  // 133
74 #define SLIDER_MARKER       "\x86"  // 134
75 #define UP_ARROW_MARKER     "\x87"  // 135
76 #define DOWN_ARROW_MARKER   "\x88"  // 136
77
78 int newmenu_do4( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem, char *filename, int width, int height, int TinyMode );
79 void show_extra_netgame_info(int choice);
80
81 int Newmenu_first_time = 1;
82 //--unused-- int Newmenu_fade_in = 1;
83
84 typedef struct bkg {
85         grs_canvas *menu_canvas;
86         grs_bitmap *saved;              // The background under the menu.
87         grs_bitmap *background;
88 } bkg;
89
90 grs_bitmap nm_background,nm_background_save;
91
92 #define MESSAGEBOX_TEXT_SIZE 2176   // How many characters in messagebox (changed form 300 (fixes crash from show_game_score and friends) - 2000/01/18 Matt Mueller)
93 #define MAX_TEXT_WIDTH 200          // How many pixels wide a input box can be
94
95 ubyte MenuReordering = 0;
96 ubyte SurfingNet = 0;
97 char Pauseable_menu = 0;
98 char already_showing_info = 0;
99
100
101 void newmenu_close(void)
102 {
103         if ( nm_background.bm_data )
104                 d_free(nm_background.bm_data);
105
106         if ( nm_background_save.bm_data )
107                 d_free(nm_background_save.bm_data);
108         Newmenu_first_time = 1;
109 }
110
111
112 ubyte background_palette[768];
113
114
115 //should be called whenever the palette changes
116 void nm_remap_background(void)
117 {
118         if (!Newmenu_first_time) {
119                 if (!nm_background.bm_data)
120                         nm_background.bm_data = d_malloc(nm_background.bm_w * nm_background.bm_h);
121
122                 memcpy(nm_background.bm_data,nm_background_save.bm_data, nm_background.bm_w * nm_background.bm_h);
123
124                 gr_remap_bitmap_good( &nm_background, background_palette, -1, -1 );
125         }
126 }
127
128
129 extern char last_palette_loaded[];
130
131
132 void nm_draw_background1(char *filename)
133 {
134         int pcx_error;
135         grs_bitmap *bmp;
136         ubyte pal[256*3];
137         int width, height;
138
139         //@@//I think this only gets called to fill the whole screen
140         //@@Assert(grd_curcanv->cv_bitmap.bm_w == 320);
141         //@@Assert(grd_curcanv->cv_bitmap.bm_h == 200);
142
143         pcx_error = pcx_get_dimensions(filename, &width, &height);
144         if (pcx_error != PCX_ERROR_NONE)
145                 Error("Could not open pcx file <%s>\n", filename);
146
147         bmp = gr_create_bitmap(width, height);
148
149         pcx_error = pcx_read_bitmap(filename, bmp, bmp->bm_type, pal);
150         Assert(pcx_error == PCX_ERROR_NONE);
151
152         //@@gr_remap_bitmap_good( bmp, pal, -1, -1 );
153
154         {       // remap stuff. this code is kindof a hack
155
156                 // now, before we bring up the menu, we need to
157                 // do some stuff to make sure the palette is ok.  First, we need to
158                 // get our current palette into the 2d's array, so the remapping will
159                 // work.  Second, we need to remap the fonts.  Third, we need to fill
160                 // in part of the fade tables so the darkening of the menu edges works
161
162                 gr_copy_palette(gr_palette, pal, sizeof(gr_palette));
163 #ifdef OGL
164                 gr_palette_load(gr_palette);
165 #endif
166                 remap_fonts_and_menus(1);
167
168         }
169
170         gr_bitmap_fullscr(bmp);
171
172         gr_free_bitmap(bmp);
173
174         strcpy(last_palette_loaded,""); // force palette load next time
175 }
176
177
178 #define MENU_BACKGROUND_BITMAP_HIRES (cfexist("scoresb.pcx")?"scoresb.pcx":"scores.pcx")
179 #define MENU_BACKGROUND_BITMAP_LORES (cfexist("scores.pcx")?"scores.pcx":"scoresb.pcx") // Mac datafiles only have scoresb.pcx
180 #define MENU_BACKGROUND_BITMAP (MenuHires?MENU_BACKGROUND_BITMAP_HIRES:MENU_BACKGROUND_BITMAP_LORES)
181
182 int Background_hires;
183 int No_darkening = 0;
184
185
186 void nm_draw_background(int x1, int y1, int x2, int y2 )
187 {
188         int w, h;
189
190         if (Newmenu_first_time || MenuHires!=Background_hires) {
191                 int pcx_error;
192
193                 if (Newmenu_first_time) {
194                         atexit( newmenu_close );
195                         Newmenu_first_time = 0;
196                         nm_background_save.bm_data = NULL;
197                 } else {
198                         if (nm_background_save.bm_data)
199                                 d_free(nm_background_save.bm_data);
200                         if (nm_background.bm_data)
201                                 d_free(nm_background.bm_data);
202                 }
203
204                 pcx_error = pcx_read_bitmap(MENU_BACKGROUND_BITMAP, &nm_background_save, BM_LINEAR, background_palette);
205                 Assert(pcx_error == PCX_ERROR_NONE);
206
207                 nm_background = nm_background_save;
208                 nm_background.bm_data = NULL;
209                 nm_remap_background();
210
211                 Background_hires = MenuHires;
212         }
213
214         if ( x1 < 0 ) x1 = 0;
215         if ( y1 < 0 ) y1 = 0;
216
217         w = x2 - x1 + 1;
218         h = y2 - y1 + 1;
219
220         //if ( w > nm_background.bm_w ) w = nm_background.bm_w;
221         //if ( h > nm_background.bm_h ) h = nm_background.bm_h;
222
223         x2 = x1 + w - 1;
224         y2 = y1 + h - 1;
225
226         if (No_darkening)
227                 gr_bm_bitblt( w, h, x1, y1, LHX(10), LHY(10), &nm_background, &(grd_curcanv->cv_bitmap) );
228         else
229                 if ( GWIDTH > nm_background.bm_w || GHEIGHT > nm_background.bm_h ) {
230                         // Resize background to fit. Resize so that the original aspect is preserved. -MPM
231                         grs_canvas *tmp, *old;
232                         grs_bitmap bg;
233
234                         old = grd_curcanv;
235                         tmp = gr_create_sub_canvas(old, x1, y1, w, h);
236                         gr_init_sub_bitmap(&bg, &nm_background, 0, 0, w*(nm_background.bm_w)/GWIDTH, h*(nm_background.bm_h)/GHEIGHT); // note that we haven't replaced current_canvas yet, so these macros are still ok.
237                         gr_set_current_canvas(tmp);
238                         gr_bitmap_fullscr( &bg );
239                         gr_set_current_canvas(old);
240                         gr_free_sub_canvas(tmp);
241                 } else {
242                         gr_bm_bitblt( w, h, x1, y1, 0, 0, &nm_background, &(grd_curcanv->cv_bitmap) );
243                 }
244
245         if (!No_darkening) {
246                 Gr_scanline_darkening_level = 2*7;
247
248                 gr_setcolor( BM_XRGB(0,0,0) );
249                 gr_urect( x2-5, y1+5, x2-5, y2-5 );
250                 gr_urect( x2-4, y1+4, x2-4, y2-5 );
251                 gr_urect( x2-3, y1+3, x2-3, y2-5 );
252                 gr_urect( x2-2, y1+2, x2-2, y2-5 );
253                 gr_urect( x2-1, y1+1, x2-1, y2-5 );
254                 gr_urect( x2+0, y1+0, x2-0, y2-5 );
255
256                 gr_urect( x1+5, y2-5, x2, y2-5 );
257                 gr_urect( x1+4, y2-4, x2, y2-4 );
258                 gr_urect( x1+3, y2-3, x2, y2-3 );
259                 gr_urect( x1+2, y2-2, x2, y2-2 );
260                 gr_urect( x1+1, y2-1, x2, y2-1 );
261                 gr_urect( x1+0, y2, x2, y2-0 );
262         }
263
264         Gr_scanline_darkening_level = GR_FADE_LEVELS;
265 }
266
267
268 void nm_restore_background( int x, int y, int w, int h )
269 {
270         int x1, x2, y1, y2;
271
272         x1 = x; x2 = x+w-1;
273         y1 = y; y2 = y+h-1;
274
275         if ( x1 < 0 ) x1 = 0;
276         if ( y1 < 0 ) y1 = 0;
277
278         if ( x2 >= GWIDTH ) x2 = GWIDTH - 1;
279         if ( y2 >= GHEIGHT ) y2 = GHEIGHT - 1;
280
281         w = x2 - x1 + 1;
282         h = y2 - y1 + 1;
283
284         if (GWIDTH > nm_background.bm_w || GHEIGHT > nm_background.bm_h) {
285                 grs_bitmap sbg;
286                 grs_canvas *tmp, *old;
287
288                 old = grd_curcanv;
289                 tmp = gr_create_sub_canvas(old, x1, y1, w, h);
290                 gr_init_sub_bitmap(&sbg, &nm_background, x1*(nm_background.bm_w)/GWIDTH, y1*(nm_background.bm_h)/GHEIGHT, w*(nm_background.bm_w)/GWIDTH, h*(nm_background.bm_h)/GHEIGHT); // use the correctly resized portion of the background. -MPM
291                 gr_set_current_canvas(tmp);
292                 gr_bitmap_fullscr( &sbg );
293                 gr_set_current_canvas(old);
294                 gr_free_sub_canvas(tmp);
295         } else
296                 gr_bm_bitblt(w, h, x1, y1, x1, y1, &nm_background, &(grd_curcanv->cv_bitmap));
297 }
298
299
300 // Draw a left justfied string
301 void nm_string( bkg *b, int w1,int x, int y, char *s )
302 {
303         int w, h, aw, tx = 0, t = 0, i;
304         char *p, *s1, *s2, measure[2];
305         int XTabs[] = { 15, 87, 124, 162, 228, 253 };
306
307         p = s1 = NULL;
308         s2 = d_strdup(s);
309
310         for (i = 0; i < 6; i++) {
311                 XTabs[i] = (LHX(XTabs[i]));
312                 XTabs[i] += x;
313         }
314
315         measure[1] = 0;
316
317         if (!SurfingNet) {
318                 p = strchr( s2, '\t' );
319                 if (p && (w1 > 0) ) {
320                         *p = '\0';
321                         s1 = p+1;
322                 }
323         }
324
325         gr_get_string_size( s2, &w, &h, &aw );
326
327         if (w1 > 0)
328                 w = w1;
329
330         // CHANGED
331         gr_bm_bitblt( b->background->bm_w-15, h+2, 5, y-1, 5, y-1, b->background, &(grd_curcanv->cv_bitmap) );
332         //gr_bm_bitblt(w, h, x, y, x, y, b->background, &(grd_curcanv->cv_bitmap) );
333
334         if (SurfingNet) {
335                 for (i = 0; i < strlen(s2); i++) {
336                         if (s2[i] == '\t' && SurfingNet) {
337                                 x = XTabs[t];
338                                 t++;
339                                 continue;
340                         }
341                         measure[0] = s2[i];
342                         gr_get_string_size(measure, &tx, &h, &aw);
343                         gr_string(x, y, measure);
344                         x += tx;
345                 }
346         } else
347                 gr_string(x, y, s2);
348
349         if (!SurfingNet && p && (w1 > 0) ) {
350                 gr_get_string_size( s1, &w, &h, &aw );
351
352                 gr_string( x+w1-w, y, s1 );
353
354                 *p = '\t';
355         }
356         d_free(s2);
357 }
358
359
360 // Draw a slider and it's string
361 void nm_string_slider( bkg *b, int w1, int x, int y, char *s )
362 {
363         int w, h, aw;
364         char *p, *s1;
365
366         s1 = NULL;
367
368         p = strchr( s, '\t' );
369         if (p) {
370                 *p = '\0';
371                 s1 = p+1;
372         }
373
374         gr_get_string_size( s, &w, &h, &aw );
375         // CHANGED
376
377         gr_bm_bitblt( b->background->bm_w-15, h, 5, y, 5, y, b->background, &(grd_curcanv->cv_bitmap) );
378         //gr_bm_bitblt( w, h, x, y, x, y, b->background, &(grd_curcanv->cv_bitmap) );
379
380         gr_string( x, y, s );
381
382         if (p) {
383                 gr_get_string_size( s1, &w, &h, &aw );
384
385                 // CHANGED
386                 gr_bm_bitblt( w, 1, x+w1-w, y, x+w1-w, y, b->background, &(grd_curcanv->cv_bitmap) );
387                 // CHANGED
388                 gr_bm_bitblt( w, 1, x+w1-w, y+h-1, x+w1-w, y, b->background, &(grd_curcanv->cv_bitmap) );
389
390                 gr_string( x+w1-w, y, s1 );
391
392                 *p = '\t';
393         }
394 }
395
396
397 // Draw a left justfied string with black background.
398 void nm_string_black( bkg *b, int w1, int x, int y, char *s )
399 {
400         int w, h, aw;
401
402         gr_get_string_size( s, &w, &h, &aw );
403         if (w1 == 0) w1 = w;
404
405         gr_setcolor( BM_XRGB(2,2,2) );
406         gr_rect( x-1, y-1, x-1, y+h-1 );
407         gr_rect( x-1, y-1, x+w1-1, y-1 );
408
409         gr_setcolor( BM_XRGB(5,5,5) );
410         gr_rect( x, y+h, x+w1, y+h);
411         gr_rect( x+w1, y-1, x+w1, y+h );
412
413         gr_setcolor( BM_XRGB(0,0,0) );
414         gr_rect( x, y, x+w1-1, y+h-1 );
415
416         gr_string( x+1, y+1, s );
417 }
418
419
420 // Draw a right justfied string
421 void nm_rstring( bkg *b, int w1, int x, int y, char *s )
422 {
423         int w, h, aw;
424
425         gr_get_string_size( s, &w, &h, &aw );
426         x -= 3;
427
428         if (w1 == 0) w1 = w;
429
430         //mprintf( 0, "Width = %d, string='%s'\n", w, s );
431
432         // CHANGED
433         gr_bm_bitblt( w1, h, x-w1, y, x-w1, y, b->background, &(grd_curcanv->cv_bitmap) );
434         gr_string( x-w, y, s );
435 }
436
437
438 // for text items, constantly redraw cursor (to achieve flash)
439 void update_cursor( newmenu_item *item )
440 {
441         int w = 0, h, aw;
442         fix time = timer_get_approx_seconds();
443         int x, y;
444         char *text = item->text;
445
446         Assert(item->type == NM_TYPE_INPUT_MENU || item->type == NM_TYPE_INPUT);
447
448         while( *text ) {
449                 gr_get_string_size( text, &w, &h, &aw );
450                 if ( w > item->w-10 )
451                         text++;
452                 else
453                         break;
454         }
455         if (*text == 0)
456                 w = 0;
457         x = item->x+w; y = item->y;
458
459         if (time & 0x8000)
460                 gr_string( x, y, CURSOR_STRING );
461         else {
462                 gr_setcolor( BM_XRGB(0,0,0) );
463                 gr_rect( x, y, x+grd_curcanv->cv_font->ft_w-1, y+grd_curcanv->cv_font->ft_h-1 );
464         }
465 }
466
467
468 void nm_string_inputbox( bkg *b, int w, int x, int y, char *text, int current )
469 {
470         int w1 = 0, h1, aw;
471
472         while( *text ) {
473                 gr_get_string_size( text, &w1, &h1, &aw );
474                 if ( w1 > w-10 )
475                         text++;
476                 else
477                         break;
478         }
479         if ( *text == 0 )
480                 w1 = 0;
481
482         nm_string_black( b, w, x, y, text );
483
484         if ( current )
485                 gr_string( x+w1+1, y, CURSOR_STRING );
486 }
487
488
489 void draw_item( bkg *b, newmenu_item *item, int is_current, int tiny )
490 {
491         if (tiny) {
492                 if (is_current)
493                         gr_set_fontcolor(gr_find_closest_color_current(57,49,20), -1);
494                 else
495                         gr_set_fontcolor(gr_find_closest_color_current(29,29,47), -1);
496
497                 if (item->text[0]=='\t')
498                         gr_set_fontcolor (gr_find_closest_color_current(63,63,63), -1);
499         } else {
500                 if (is_current)
501                         grd_curcanv->cv_font = SELECTED_FONT;
502                 else
503                         grd_curcanv->cv_font = NORMAL_FONT;
504
505 #ifdef WINDOWS
506                 if (is_current && item->type == NM_TYPE_TEXT)
507                         grd_curcanv->cv_font = NORMAL_FONT;
508 #endif
509         }
510
511         switch( item->type ) {
512         case NM_TYPE_TEXT:
513                 //grd_curcanv->cv_font=TEXT_FONT;
514                 // fall through on purpose
515         case NM_TYPE_MENU:
516                 nm_string( b, item->w, item->x, item->y, item->text );
517                 break;
518         case NM_TYPE_SLIDER: {
519                 int j;
520
521                 if (item->value < item->min_value) item->value = item->min_value;
522                 if (item->value > item->max_value) item->value = item->max_value;
523                 sprintf( item->saved_text, "%s\t%s", item->text, SLIDER_LEFT );
524                 for (j = 0; j < (item->max_value-item->min_value+1); j++ )
525                         strncat( item->saved_text, SLIDER_MIDDLE, sizeof(item->saved_text)-strlen(item->saved_text)-1 );
526                 strncat( item->saved_text, SLIDER_RIGHT, sizeof(item->saved_text)-strlen(item->saved_text)-1 );
527
528                 item->saved_text[item->value+1+strlen(item->text)+1] = SLIDER_MARKER[0];
529
530                 nm_string_slider( b, item->w, item->x, item->y, item->saved_text );
531                 }
532                 break;
533         case NM_TYPE_INPUT_MENU:
534                 if ( item->group == 0 )
535                         nm_string( b, item->w, item->x, item->y, item->text );
536                 else
537                         nm_string_inputbox( b, item->w, item->x, item->y, item->text, is_current );
538                 break;
539         case NM_TYPE_INPUT:
540                 nm_string_inputbox( b, item->w, item->x, item->y, item->text, is_current );
541                 break;
542         case NM_TYPE_CHECK:
543                 nm_string( b, item->w, item->x, item->y, item->text );
544                 if (item->value)
545                         nm_rstring( b, item->right_offset, item->x, item->y, CHECKED_CHECK_BOX );
546                 else
547                         nm_rstring( b, item->right_offset, item->x, item->y, NORMAL_CHECK_BOX );
548                 break;
549         case NM_TYPE_RADIO:
550                 nm_string( b, item->w, item->x, item->y, item->text );
551                 if (item->value)
552                         nm_rstring( b, item->right_offset, item->x, item->y, CHECKED_RADIO_BOX );
553                 else
554                         nm_rstring( b, item->right_offset, item->x, item->y, NORMAL_RADIO_BOX );
555                 break;
556         case NM_TYPE_NUMBER: {
557                 char text[10];
558
559                 if (item->value < item->min_value) item->value = item->min_value;
560                 if (item->value > item->max_value) item->value = item->max_value;
561                 nm_string( b, item->w, item->x, item->y, item->text );
562                 sprintf( text, "%d", item->value );
563                 nm_rstring( b,item->right_offset,item->x, item->y, text );
564                 }
565                 break;
566         }
567 }
568
569
570 const char *Newmenu_allowed_chars=NULL;
571
572
573 // returns true if char is allowed
574 int char_allowed(char c)
575 {
576         const char *p = Newmenu_allowed_chars;
577
578         if (!p)
579                 return 1;
580
581         while (*p) {
582                 Assert(p[1]);
583
584                 if (c >= p[0] && c <= p[1])
585                         return 1;
586
587                 p += 2;
588         }
589
590         return 0;
591 }
592
593
594 void strip_end_whitespace( char *text )
595 {
596         int i, l;
597
598         l = (int)strlen( text );
599         for ( i = l-1; i >= 0; i-- ) {
600                 if ( isspace(text[i]) )
601                         text[i] = 0;
602                 else
603                         return;
604         }
605 }
606
607
608 int newmenu_do( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem) )
609 {
610         return newmenu_do3( title, subtitle, nitems, item, subfunction, 0, NULL, -1, -1 );
611 }
612
613
614 int newmenu_dotiny( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem) )
615 {
616         return newmenu_do4( title, subtitle, nitems, item, subfunction, 0, NULL, LHX(310), -1, 1 );
617 }
618
619
620 int newmenu_dotiny2( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem) )
621 {
622         return newmenu_do4( title, subtitle, nitems, item, subfunction, 0, NULL, -1, -1, 1 );
623 }
624
625
626 int newmenu_do1( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem )
627 {
628         return newmenu_do3( title, subtitle, nitems, item, subfunction, citem, NULL, -1, -1 );
629 }
630
631
632 int newmenu_do2( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem, char *filename )
633 {
634         return newmenu_do3( title, subtitle, nitems, item, subfunction, citem, filename, -1, -1 );
635 }
636
637
638 int newmenu_do3( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem, char *filename, int width, int height )
639 {
640         return newmenu_do4( title, subtitle, nitems, item, subfunction, citem, filename, width, height, 0 );
641 }
642
643
644 int newmenu_do_fixedfont( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem, char *filename, int width, int height )
645 {
646         set_screen_mode(SCREEN_MENU); // hafta set the screen mode before calling or fonts might get changed/freed up if screen res changes
647 //      return newmenu_do3_real( title, subtitle, nitems, item, subfunction, citem, filename, width, height, GAME_FONT, GAME_FONT, GAME_FONT, GAME_FONT );
648         return newmenu_do4( title, subtitle, nitems, item, subfunction, citem, filename, width, height, 0);
649 }
650
651
652 // use menu with joystick
653 int newmenu_inkey(void)
654 {
655         int     key;
656
657         key = key_inkey();
658
659         if ( !Config_control_joystick.intval || key < KEY_JB1 || key > 255 ) // not using joystick, not a joystick button, or modifier pressed
660                 return key;
661
662         if ( strlen(key_text[key]) > 5 && key_text[key][0] == 'J' && key_text[key][2] == 'H' ) // joystick hat
663                 switch (key_text[key][4]) {
664                         case 'U': key = KEY_UP; break;
665                         case 'R': key = KEY_RIGHT; break;
666                         case 'D': key = KEY_DOWN; break;
667                         case 'L': key = KEY_LEFT; break;
668                 }
669         else if ( strlen(key_text[key]) > 3 && key_text[key][0] == 'J' && key_text[key][2] == 'B' ) // joystick button
670                 switch (key_text[key][3]) {
671                         case '1': key = KEY_SPACEBAR; break;
672                         case '2': key = KEY_ENTER; break;
673                         case '3': key = KEY_ESC; break;
674                 }
675         return key;
676 }
677
678
679 int newmenu_getch(void)
680 {
681         while (!key_checkch())
682                 timer_delay(1);
683
684         return newmenu_inkey();
685 }
686
687
688 extern int network_request_player_names(int);
689 extern int RestoringMenu;
690
691 #ifdef NEWMENU_MOUSE
692 ubyte Hack_DblClick_MenuMode = 0;
693 #endif
694
695 #ifdef MACINTOSH
696 extern ubyte joydefs_calibrating;
697 #else
698 # define joydefs_calibrating 0
699 #endif
700
701 #define CLOSE_X     (MenuHires?15:7)
702 #define CLOSE_Y     (MenuHires?15:7)
703 #define CLOSE_SIZE  (MenuHires?10:5)
704
705
706 void draw_close_box(int x, int y)
707 {
708         gr_setcolor( BM_XRGB(0, 0, 0) );
709         gr_rect(x + CLOSE_X, y + CLOSE_Y, x + CLOSE_X + CLOSE_SIZE, y + CLOSE_Y + CLOSE_SIZE);
710         gr_setcolor( BM_XRGB(21, 21, 21) );
711         gr_rect(x + CLOSE_X + LHX(1), y + CLOSE_Y + LHX(1), x + CLOSE_X + CLOSE_SIZE - LHX(1), y + CLOSE_Y + CLOSE_SIZE - LHX(1));
712 }
713
714
715 extern int Num_bitmap_files;
716
717
718 int newmenu_do4( char *title, char *subtitle, int nitems, newmenu_item *item, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int citem, char *filename, int width, int height, int TinyMode )
719 {
720         int old_keyd_repeat, done;
721         int choice, old_choice, i, j, x, y, w, h, aw, tw, th, twidth, fm, right_offset;
722         int k, nmenus, nothers, ScrollOffset = 0, LastScrollCheck = -1, MaxDisplayable, sx, sy;
723         grs_font *save_font;
724         int string_width, string_height = 0, average_width;
725         int ty;
726         bkg bg;
727         int all_text = 0; // set true if all text items
728         int sound_stopped = 0, time_stopped = 0;
729         int TopChoice, IsScrollBox = 0; // Is this a scrolling box? Set to false at init
730         char *Temp, TempVal;
731         int dont_restore = 0;
732         int MaxOnMenu = MAXDISPLAYABLEITEMS;
733         grs_canvas *save_canvas;
734 #ifdef NEWMENU_MOUSE
735         int mouse_state, omouse_state, dblclick_flag = 0;
736         int mx = 0, my = 0, x1 = 0, x2, y1, y2;
737         int close_box = 0;
738 #endif
739 #ifdef MACINTOSH
740         EventRecord event; // looking for disk inserted events for CD mounts
741 #endif
742
743         WIN(if (!_AppActive) return -1); // Don't draw message if minimized!
744         newmenu_hide_cursor();
745
746         if (nitems < 1 )
747                 return -1;
748
749         MaxDisplayable = nitems;
750
751         //set_screen_mode(SCREEN_MENU);
752         set_popup_screen();
753
754         if ( Function_mode == FMODE_GAME && !(Game_mode & GM_MULTI)) {
755                 digi_pause_digi_sounds();
756                 sound_stopped = 1;
757         }
758
759         if ( !((Game_mode & GM_MULTI) && (Function_mode == FMODE_GAME) && (!Endlevel_sequence)) ) {
760                 time_stopped = 1;
761                 stop_time();
762 #ifdef TACTILE
763                 if (TactileStick)
764                         DisableForces();
765 #endif
766         }
767
768         save_canvas = grd_curcanv;
769
770         gr_set_current_canvas(NULL);
771
772         save_font = grd_curcanv->cv_font;
773
774         tw = th = 0;
775
776         if ( title ) {
777                 grd_curcanv->cv_font = TITLE_FONT;
778                 gr_get_string_size( title, &string_width, &string_height, &average_width );
779                 tw = string_width;
780                 th = string_height;
781         }
782         if ( subtitle ) {
783                 grd_curcanv->cv_font = SUBTITLE_FONT;
784                 gr_get_string_size( subtitle, &string_width, &string_height, &average_width );
785                 if (string_width > tw )
786                         tw = string_width;
787                 th += string_height;
788         }
789
790         th += LHY(8); // put some space between titles & body
791
792         if (TinyMode)
793                 grd_curcanv->cv_font = SMALL_FONT;
794         else
795                 grd_curcanv->cv_font = NORMAL_FONT;
796
797         w = aw = 0;
798         h = th;
799         nmenus = nothers = 0;
800
801         // Find menu height & width (store in w,h)
802         for (i = 0; i < nitems; i++ ) {
803                 item[i].redraw = 1;
804                 item[i].y = h;
805                 gr_get_string_size( item[i].text, &string_width, &string_height, &average_width );
806                 item[i].right_offset = 0;
807
808                 if (SurfingNet)
809                         string_height += LHY(3);
810
811                 item[i].saved_text[0] = '\0';
812
813                 if ( item[i].type == NM_TYPE_SLIDER ) {
814                         int w1, h1, aw1;
815
816                         nothers++;
817                         sprintf( item[i].saved_text, "%s", SLIDER_LEFT );
818                         for (j = 0; j < (item[i].max_value-item[i].min_value+1); j++ )
819                                 strncat( item[i].saved_text, SLIDER_MIDDLE, sizeof(item[i].saved_text)-strlen(item[i].saved_text)-1 );
820                         strncat( item[i].saved_text, SLIDER_RIGHT, sizeof(item[i].saved_text)-strlen(item[i].saved_text)-1 );
821                         gr_get_string_size( item[i].saved_text, &w1, &h1, &aw1 );
822                         string_width += w1 + aw;
823                 }
824
825                 if ( item[i].type == NM_TYPE_MENU )
826                         nmenus++;
827
828                 if ( item[i].type == NM_TYPE_CHECK ) {
829                         int w1, h1, aw1;
830
831                         nothers++;
832                         gr_get_string_size( NORMAL_CHECK_BOX, &w1, &h1, &aw1 );
833                         item[i].right_offset = w1;
834                         gr_get_string_size( CHECKED_CHECK_BOX, &w1, &h1, &aw1 );
835                         if (w1 > item[i].right_offset)
836                                 item[i].right_offset = w1;
837                 }
838
839                 if (item[i].type == NM_TYPE_RADIO ) {
840                         int w1, h1, aw1;
841
842                         nothers++;
843                         gr_get_string_size( NORMAL_RADIO_BOX, &w1, &h1, &aw1 );
844                         item[i].right_offset = w1;
845                         gr_get_string_size( CHECKED_RADIO_BOX, &w1, &h1, &aw1 );
846                         if (w1 > item[i].right_offset)
847                                 item[i].right_offset = w1;
848                 }
849
850                 if (item[i].type == NM_TYPE_NUMBER ) {
851                         int w1, h1, aw1;
852                         char test_text[20];
853
854                         nothers++;
855                         sprintf( test_text, "%d", item[i].max_value );
856                         gr_get_string_size( test_text, &w1, &h1, &aw1 );
857                         item[i].right_offset = w1;
858                         sprintf( test_text, "%d", item[i].min_value );
859                         gr_get_string_size( test_text, &w1, &h1, &aw1 );
860                         if ( w1 > item[i].right_offset)
861                                 item[i].right_offset = w1;
862                 }
863
864                 if ( item[i].type == NM_TYPE_INPUT ) {
865                         Assert( strlen(item[i].text) < NM_MAX_TEXT_LEN );
866                         strcpy( item[i].saved_text, item[i].text );
867                         nothers++;
868                         string_width = item[i].text_len*grd_curcanv->cv_font->ft_w+((MenuHires?3:1)*item[i].text_len);
869                         if ( string_width > MAX_TEXT_WIDTH ) 
870                                 string_width = MAX_TEXT_WIDTH;
871                         item[i].value = -1;
872                 }
873
874                 if ( item[i].type == NM_TYPE_INPUT_MENU ) {
875                         Assert( strlen(item[i].text) < NM_MAX_TEXT_LEN );
876                         strcpy( item[i].saved_text, item[i].text );
877                         nmenus++;
878                         string_width = item[i].text_len*grd_curcanv->cv_font->ft_w+((MenuHires?3:1)*item[i].text_len);
879                         item[i].value = -1;
880                         item[i].group = 0;
881                 }
882
883                 item[i].w = string_width;
884                 item[i].h = string_height;
885
886                 if ( string_width > w )
887                         w = string_width; // Save maximum width
888                 if ( average_width > aw )
889                         aw = average_width;
890                 h += string_height+1; // Find the height of all strings
891         }
892
893         // Big hack for allowing the netgame options menu to spill over
894
895         MaxOnMenu = MAXDISPLAYABLEITEMS;
896         if (ExtGameStatus == GAMESTAT_NETGAME_OPTIONS || ExtGameStatus == GAMESTAT_MORE_NETGAME_OPTIONS)
897                 MaxOnMenu++;
898
899         if (!TinyMode && (h > ((MaxOnMenu+1)*(string_height+1))+(LHY(8))) ) {
900                 IsScrollBox = 1;
901                 h = (MaxOnMenu*(string_height+1)+LHY(8));
902                 MaxDisplayable = MaxOnMenu;
903                 mprintf((0, "Hey, this is a scroll box!\n"));
904         } else
905                 IsScrollBox = 0;
906
907         right_offset = 0;
908
909         if ( width > -1 )
910                 w = width;
911
912         if ( height > -1 )
913                 h = height;
914
915         for (i = 0; i < nitems; i++ ) {
916                 item[i].w = w;
917                 if (item[i].right_offset > right_offset )
918                         right_offset = item[i].right_offset;
919         }
920         if (right_offset > 0 )
921                 right_offset += 3;
922
923         //gr_get_string_size( "", &string_width, &string_height, &average_width );
924
925         w += right_offset;
926
927         twidth = 0;
928         if ( tw > w ) {
929                 twidth = ( tw - w )/2;
930                 w = tw;
931         }
932
933         if (RestoringMenu) {
934                 right_offset = 0;
935                 twidth = 0;
936         }
937
938         mprintf(( 0, "Right offset = %d\n", right_offset ));
939
940
941         // Find min point of menu border
942 //      x = (grd_curscreen->sc_w-w)/2;
943 //      y = (grd_curscreen->sc_h-h)/2;
944
945         w += MenuHires?60:30;
946         h += MenuHires?60:30;
947
948         if ( w > grd_curcanv->cv_bitmap.bm_w ) w = grd_curcanv->cv_bitmap.bm_w;
949         if ( h > grd_curcanv->cv_bitmap.bm_h ) h = grd_curcanv->cv_bitmap.bm_h;
950
951         x = (grd_curcanv->cv_bitmap.bm_w-w)/2;
952         y = (grd_curcanv->cv_bitmap.bm_h-h)/2;
953
954         if ( x < 0 ) x = 0;
955         if ( y < 0 ) y = 0;
956
957         if ( filename != NULL ) {
958                 nm_draw_background1( filename );
959                 gr_palette_load(gr_palette);
960         }
961
962         // Save the background of the display
963         bg.menu_canvas = gr_create_sub_canvas( &grd_curscreen->sc_canvas, x, y, w, h );
964         gr_set_current_canvas(bg.menu_canvas);
965
966         if ( filename == NULL ) {
967                 // Save the background under the menu...
968 #ifdef TACTILE
969                 if (TactileStick)
970                         DisableForces();
971 #endif
972
973                 bg.saved = gr_create_bitmap( w, h );
974                 Assert( bg.saved != NULL );
975
976                 gr_bm_bitblt( w, h, 0, 0, 0, 0, &grd_curcanv->cv_bitmap, bg.saved );
977
978                 gr_set_current_canvas( NULL );
979
980                 nm_draw_background(x, y, x+w-1, y+h-1);
981
982                 gr_set_current_canvas( bg.menu_canvas );
983
984                 bg.background = gr_create_sub_bitmap(&nm_background, 0, 0, w, h);
985
986         } else {
987                 bg.saved = NULL;
988                 bg.background = gr_create_bitmap( w, h );
989                 Assert( bg.background != NULL );
990
991                 gr_bm_bitblt(w, h, 0, 0, 0, 0, &grd_curcanv->cv_bitmap, bg.background );
992         }
993
994 //      ty = 15 + (yborder/4);
995
996         ty = MenuHires?30:15;
997
998         if ( title ) {
999                 grd_curcanv->cv_font = TITLE_FONT;
1000                 gr_set_fontcolor( GR_GETCOLOR(31,31,31), -1 );
1001                 gr_get_string_size(title, &string_width, &string_height, &average_width );
1002                 tw = string_width;
1003                 th = string_height;
1004                 gr_printf( 0x8000, ty, title );
1005                 ty += th;
1006         }
1007
1008         if ( subtitle ) {
1009                 grd_curcanv->cv_font = SUBTITLE_FONT;
1010                 gr_set_fontcolor( GR_GETCOLOR(21,21,21), -1 );
1011                 gr_get_string_size(subtitle, &string_width, &string_height, &average_width );
1012                 tw = string_width;
1013                 th = string_height;
1014                 gr_printf( 0x8000, ty, subtitle );
1015                 ty += th;
1016         }
1017
1018         if (TinyMode)
1019                 grd_curcanv->cv_font = SMALL_FONT;
1020         else
1021                 grd_curcanv->cv_font = NORMAL_FONT;
1022
1023         // Update all item's x & y values.
1024         for (i = 0; i < nitems; i++ ) {
1025                 item[i].x = (MenuHires?30:15) + twidth + right_offset;
1026                 item[i].y += (MenuHires?30:15);
1027                 if ( item[i].type == NM_TYPE_RADIO ) {
1028                         fm = -1; // find first marked one
1029                         for ( j = 0; j < nitems; j++ ) {
1030                                 if ( item[j].type == NM_TYPE_RADIO && item[j].group == item[i].group ) {
1031                                         if (fm == -1 && item[j].value)
1032                                                 fm = j;
1033                                         item[j].value = 0;
1034                                 }
1035                         }
1036                         if ( fm >= 0 )
1037                                 item[fm].value = 1;
1038                         else
1039                                 item[i].value = 1;
1040                 }
1041         }
1042
1043         old_keyd_repeat = keyd_repeat;
1044         keyd_repeat = 1;
1045
1046         if (citem == -1) {
1047                 choice = -1;
1048         } else {
1049                 if (citem < 0 ) citem = 0;
1050                 if (citem > nitems-1 ) citem = nitems-1;
1051                 choice = citem;
1052
1053 #ifdef NEWMENU_MOUSE
1054                 dblclick_flag = 1;
1055 #endif
1056
1057                 while ( item[choice].type == NM_TYPE_TEXT ) {
1058                         choice++;
1059                         if (choice >= nitems ) {
1060                                 choice = 0;
1061                         }
1062                         if (choice == citem ) {
1063                                 choice = 0;
1064                                 all_text = 1;
1065                                 break;
1066                         }
1067                 }
1068         }
1069         done = 0;
1070         TopChoice = choice;
1071
1072         vid_update();
1073         // Clear mouse, joystick to clear button presses.
1074         game_flush_inputs();
1075
1076 #ifdef NEWMENU_MOUSE
1077         mouse_state = omouse_state = 0;
1078         if (filename == NULL && !MenuReordering) {
1079                 //draw_close_box(0, 0);
1080                 close_box = 1;
1081         }
1082
1083         if (!MenuReordering && !joydefs_calibrating) {
1084                 newmenu_show_cursor();
1085 # ifdef WINDOWS
1086                 SetCursor(LoadCursor(NULL, IDC_ARROW));
1087 # endif
1088         }
1089 #endif
1090
1091         mprintf((0, "Set to true!\n"));
1092
1093         while (!done) {
1094 #ifdef NEWMENU_MOUSE
1095                 if (!joydefs_calibrating)
1096                         newmenu_show_cursor(); // possibly hidden
1097                 omouse_state = mouse_state;
1098                 if (!MenuReordering)
1099                         mouse_state = mouse_button_state(0);
1100 //@@            mprintf((0, "mouse state:%d\n", mouse_state));
1101 #endif
1102
1103                 // see if redbook song needs to be restarted
1104                 songs_check_redbook_repeat();
1105
1106                 //network_listen();
1107
1108                 k = newmenu_inkey();
1109
1110                 if (subfunction)
1111                         (*subfunction)(nitems, item, &k, choice);
1112
1113 #ifdef NETWORK
1114                 if (!time_stopped) {
1115                         // Save current menu box
1116                         if (multi_menu_poll() == -1)
1117                                 k = -2;
1118                 }
1119 #endif
1120
1121                 if ( k < -1 ) {
1122                         dont_restore = (k == -3); // -3 means don't restore
1123                         choice = k;
1124                         k = -1;
1125                         done = 1;
1126                 }
1127
1128 #ifndef NEWMENU_MOUSE // don't allow mouse to continue from menu
1129                 if (Config_control_mouse.intval)
1130                         for ( i = 0; i < 3; i++ )
1131                                 if (mouse_button_down_count(i) > 0)
1132                                         done = 1;
1133 #endif
1134
1135 //              if ( (nmenus < 2) && (k > 0) && (nothers == 0) )
1136 //                      done = 1;
1137
1138                 old_choice = choice;
1139
1140                 // More versatile menu navigation
1141                 switch (k) {
1142                 case KEY_RIGHT:
1143                         switch (item[choice].type) {
1144                         case NM_TYPE_MENU:
1145                         case NM_TYPE_INPUT_MENU:
1146                                 k = KEY_ENTER;
1147                                 break;
1148                         case NM_TYPE_CHECK:
1149                         case NM_TYPE_RADIO:
1150                                 k = KEY_SPACEBAR;
1151                                 break;
1152                         }
1153                         break;
1154                 case KEY_LEFT:
1155                         switch (item[choice].type) {
1156                         case NM_TYPE_MENU:
1157                         case NM_TYPE_CHECK:
1158                         case NM_TYPE_RADIO:
1159                         case NM_TYPE_INPUT_MENU:
1160                                 k = KEY_ESC;
1161                                 break;
1162                         }
1163                         break;
1164                 }
1165
1166                 switch( k ) {
1167
1168 #ifdef NETWORK
1169                 case KEY_I:
1170                         if (SurfingNet && !already_showing_info)
1171                                 show_extra_netgame_info(choice-2);
1172                         if (SurfingNet && already_showing_info) {
1173                                 done = 1;
1174                                 choice = -1;
1175                         }
1176                         break;
1177                 case KEY_U:
1178                         if (SurfingNet && !already_showing_info)
1179                                 network_request_player_names(choice-2);
1180                         if (SurfingNet && already_showing_info) {
1181                                 done = 1;
1182                                 choice = -1;
1183                         }
1184                         break;
1185 #endif
1186                 case KEY_PAUSE:
1187                         if (Pauseable_menu) {
1188                                 Pauseable_menu = 0;
1189                                 done = 1;
1190                                 choice = -1;
1191                         }
1192                         break;
1193                 case KEY_TAB + KEY_SHIFTED:
1194                 case KEY_UP:
1195                 case KEY_PAD8:
1196                         if (all_text)
1197                                 break;
1198                         do {
1199                                 choice--;
1200
1201                                 if (IsScrollBox) {
1202                                         LastScrollCheck = -1;
1203                                         mprintf((0, "Scrolling! Choice=%d\n", choice));
1204
1205                                         if (choice < TopChoice) {
1206                                                 choice = TopChoice;
1207                                                 break;
1208                                         }
1209
1210                                         if (choice < ScrollOffset) {
1211                                                 for (i = 0; i < nitems; i++)
1212                                                         item[i].redraw = 1;
1213                                                 ScrollOffset--;
1214                                                 mprintf((0, "ScrollOffset=%d\n", ScrollOffset));
1215                                         }
1216                                 } else {
1217                                         if (choice >= nitems )
1218                                                 choice = 0;
1219                                         if (choice < 0 )
1220                                                 choice = nitems-1;
1221                                 }
1222                         } while ( item[choice].type == NM_TYPE_TEXT );
1223                         if ((item[choice].type == NM_TYPE_INPUT) && (choice != old_choice))
1224                                 item[choice].value = -1;
1225                         if ((old_choice > -1) && (item[old_choice].type == NM_TYPE_INPUT_MENU) && (old_choice != choice)) {
1226                                 item[old_choice].group = 0;
1227                                 strcpy(item[old_choice].text, item[old_choice].saved_text );
1228                                 item[old_choice].value = -1;
1229                         }
1230                         if (old_choice > -1)
1231                                 item[old_choice].redraw = 1;
1232                         item[choice].redraw = 1;
1233                         break;
1234                 case KEY_TAB:
1235                 case KEY_DOWN:
1236                 case KEY_PAD2:
1237                         // ((0,"Pressing down! IsScrollBox=%d",IsScrollBox));
1238                         if (all_text)
1239                                 break;
1240                         do {
1241                                 choice++;
1242
1243                                 if (IsScrollBox) {
1244                                         LastScrollCheck = -1;
1245                                         mprintf((0, "Scrolling! Choice=%d\n", choice));
1246
1247                                         if (choice == nitems) {
1248                                                 choice--;
1249                                                 break;
1250                                         }
1251
1252                                         if (choice >= MaxOnMenu+ScrollOffset) {
1253                                                 for (i = 0; i < nitems; i++)
1254                                                         item[i].redraw = 1;
1255                                                 ScrollOffset++;
1256                                                 mprintf((0, "ScrollOffset=%d\n", ScrollOffset));
1257                                         }
1258                                 } else {
1259                                         if (choice < 0 )
1260                                                 choice = nitems-1;
1261                                         if (choice >= nitems )
1262                                                 choice = 0;
1263                                 }
1264
1265                         } while ( item[choice].type == NM_TYPE_TEXT );
1266
1267                         if ((item[choice].type == NM_TYPE_INPUT) && (choice != old_choice))
1268                                 item[choice].value = -1;
1269                         if ( (old_choice > -1) && (item[old_choice].type == NM_TYPE_INPUT_MENU) && (old_choice != choice) ) {
1270                                 item[old_choice].group = 0;
1271                                 strcpy( item[old_choice].text, item[old_choice].saved_text );
1272                                 item[old_choice].value = -1;
1273                         }
1274                         if (old_choice > -1)
1275                                 item[old_choice].redraw = 1;
1276                         item[choice].redraw = 1;
1277                         break;
1278                 case KEY_SPACEBAR:
1279                         if ( choice > -1 ) {
1280                                 switch( item[choice].type ) {
1281                                 case NM_TYPE_MENU:
1282                                 case NM_TYPE_INPUT:
1283                                 case NM_TYPE_INPUT_MENU:
1284                                         break;
1285                                 case NM_TYPE_CHECK:
1286                                         if ( item[choice].value )
1287                                                 item[choice].value = 0;
1288                                         else
1289                                                 item[choice].value = 1;
1290                                         mprintf((0, "ISB=%d MDI=%d SO=%d choice=%d\n", IsScrollBox, MAXDISPLAYABLEITEMS, ScrollOffset, choice));
1291                                         if (IsScrollBox) {
1292                                                 if (choice == (MaxOnMenu+ScrollOffset-1) || choice == ScrollOffset) {
1293                                                         mprintf((0, "Special redraw!\n"));
1294                                                         LastScrollCheck = -1;
1295                                                 }
1296                                         }
1297
1298                                         item[choice].redraw = 1;
1299                                         break;
1300                                 case NM_TYPE_RADIO:
1301                                         for ( i = 0; i < nitems; i++ ) {
1302                                                 if ((i!=choice) && (item[i].type==NM_TYPE_RADIO) && (item[i].group==item[choice].group) && (item[i].value) ) {
1303                                                         item[i].value = 0;
1304                                                         item[i].redraw = 1;
1305                                                 }
1306                                         }
1307                                         item[choice].value = 1;
1308                                         item[choice].redraw = 1;
1309                                         break;
1310                                 }
1311                         }
1312                         break;
1313
1314                 case KEY_SHIFTED+KEY_UP:
1315                         if (MenuReordering && choice != TopChoice) {
1316                                 Temp = item[choice].text;
1317                                 TempVal = item[choice].value;
1318                                 item[choice].text = item[choice-1].text;
1319                                 item[choice].value = item[choice-1].value;
1320                                 item[choice-1].text = Temp;
1321                                 item[choice-1].value = TempVal;
1322                                 item[choice].redraw = 1;
1323                                 item[choice-1].redraw = 1;
1324                                 choice--;
1325                         }
1326                         break;
1327                 case KEY_SHIFTED+KEY_DOWN:
1328                         if (MenuReordering && choice != (nitems-1)) {
1329                                 Temp = item[choice].text;
1330                                 TempVal = item[choice].value;
1331                                 item[choice].text = item[choice+1].text;
1332                                 item[choice].value = item[choice+1].value;
1333                                 item[choice+1].text = Temp;
1334                                 item[choice+1].value = TempVal;
1335                                 item[choice].redraw = 1;
1336                                 item[choice+1].redraw = 1;
1337                                 choice++;
1338                         }
1339                         break;
1340                 case KEY_ENTER:
1341                 case KEY_PADENTER:
1342                         if ( (choice > -1) && (item[choice].type == NM_TYPE_INPUT_MENU) && (item[choice].group == 0)) {
1343                                 item[choice].group = 1;
1344                                 item[choice].redraw = 1;
1345                                 if ( !strnicmp( item[choice].saved_text, TXT_EMPTY, strlen(TXT_EMPTY) ) ) {
1346                                         item[choice].text[0] = 0;
1347                                         item[choice].value = -1;
1348                                 } else
1349                                         strip_end_whitespace(item[choice].text);
1350                         } else
1351                                 done = 1;
1352                         break;
1353
1354                 case KEY_ESC:
1355                         if ( (choice > -1) && (item[choice].type == NM_TYPE_INPUT_MENU) && (item[choice].group == 1)) {
1356                                 item[choice].group = 0;
1357                                 strcpy( item[choice].text, item[choice].saved_text );
1358                                 item[choice].redraw = 1;
1359                                 item[choice].value = -1;
1360                         } else {
1361                                 done = 1;
1362                                 choice = -1;
1363                         }
1364                         break;
1365
1366                 case KEY_COMMAND+KEY_SHIFTED+KEY_3:
1367                 case KEY_PRINT_SCREEN:
1368                         MAC(newmenu_hide_cursor());
1369                         save_screen_shot(0);
1370                         for (i = 0; i < nitems; i++)
1371                                 item[i].redraw = 1;
1372
1373                         MAC(newmenu_show_cursor());
1374                         MAC(key_flush());
1375                         break;
1376
1377 #ifdef MACINTOSH
1378                 case KEY_COMMAND+KEY_RIGHT:
1379                         songs_goto_next_song();
1380                         break;
1381                 case KEY_COMMAND+KEY_LEFT:
1382                         songs_goto_prev_song();
1383                         break;
1384                 case KEY_COMMAND+KEY_UP:
1385                         songs_play_level_song(1);
1386                         break;
1387                 case KEY_COMMAND+KEY_DOWN:
1388                         songs_stop_redbook();
1389                         break;
1390
1391                 case KEY_COMMAND+KEY_M:
1392                         k = -1;
1393                         if ( (Game_mode & GM_MULTI) ) // don't process in multiplayer games
1394                                 break;
1395
1396                         key_close(); // no processing of keys with keyboard handler.. jeez
1397                         stop_time();
1398                         newmenu_hide_cursor();
1399                         show_boxed_message("Mounting CD\nESC to quit");
1400                         RBAMountDisk(); // OS has totaly control of the CD.
1401                         if (Function_mode == FMODE_MENU)
1402                                 songs_play_song(SONG_TITLE, 1);
1403                         else if (Function_mode == FMODE_GAME)
1404                                 songs_play_level_song( Current_level_num );
1405                         clear_boxed_message();
1406                         newmenu_show_cursor();
1407                         key_init();
1408                         key_flush();
1409                         start_time();
1410                         break;
1411
1412                 case KEY_COMMAND+KEY_E:
1413                         songs_stop_redbook();
1414                         RBAEjectDisk();
1415                         k = -1; // force key not to register
1416                         break;
1417 #endif
1418
1419                 case KEY_COMMAND+KEY_Q:
1420                         if ( !(Game_mode & GM_MULTI) )
1421                                 quit_request();
1422                         if (!joydefs_calibrating)
1423                                 newmenu_show_cursor();
1424                         k = -1; // force key not to register
1425                         break;
1426
1427 #ifndef NDEBUG
1428                 case KEY_BACKSP:
1429                         if ( (choice > -1) && (item[choice].type != NM_TYPE_INPUT) && (item[choice].type != NM_TYPE_INPUT_MENU))
1430                                 Int3();
1431                         break;
1432
1433                 case KEY_B:
1434                 case KEY_SHIFTED + KEY_B:
1435                         {
1436                                 static int n = 0;
1437                                 grs_canvas *canv_save = grd_curcanv;
1438                                 grs_canvas *temp_canv;
1439                                 grs_bitmap *bm;
1440                                 ubyte bm_pal[768];
1441                                 ubyte pal_save[768];
1442
1443                                 memcpy(pal_save, gr_palette, 768);
1444                                 gr_use_palette_table(DEFAULT_LEVEL_PALETTE);
1445                                 memcpy(bm_pal, gr_palette, 768);
1446                                 gr_copy_palette(gr_palette, pal_save, 768);
1447
1448                                 if (k & KEY_SHIFTED)
1449                                         n--;
1450                                 else
1451                                         n++;
1452
1453                                 if (n < 0)
1454                                         n = Num_bitmap_files - 1;
1455                                 n %= Num_bitmap_files;
1456                                 bm = &GameBitmaps[n];
1457                                 PIGGY_PAGE_IN( *(bitmap_index *)&n );
1458                                 con_printf(CON_DEBUG, "showing bitmap %d of %d: %s\n", n, Num_bitmap_files, piggy_game_bitmap_name(bm));
1459
1460                                 temp_canv = gr_create_canvas(bm->bm_w, bm->bm_h);
1461                                 gr_set_current_canvas(temp_canv);
1462                                 gr_bitmap(0, 0, bm);
1463                                 gr_set_current_canvas(&grd_curscreen->sc_canvas);
1464                                 gr_remap_bitmap_good(&temp_canv->cv_bitmap, bm_pal, -1, -1);
1465                                 gr_bitmap(0, 0, &temp_canv->cv_bitmap);
1466                                 gr_free_canvas(temp_canv);
1467                                 gr_set_current_canvas(canv_save);
1468                                 break;
1469                         }
1470
1471                 case KEY_P:
1472                 case KEY_SHIFTED + KEY_P:
1473                         {
1474                                 char *palettes[] = {
1475                                         "default.256",
1476                                         "groupa.256",
1477                                         "alien1.256",
1478                                         "alien2.256",
1479                                         "credits.256",
1480                                         "fire.256",
1481                                         "ice.256",
1482                                         "water.256",
1483                                 };
1484                                 static unsigned int palnum = 0;
1485
1486                                 if (k & KEY_SHIFTED)
1487                                         palnum--;
1488                                 else
1489                                         palnum++;
1490
1491                                 palnum %= sizeof(palettes)/sizeof(char *);
1492                                 load_palette(palettes[palnum], 0, 0);
1493
1494                                 break;
1495                         }
1496 #endif
1497                 }
1498
1499 #ifdef NEWMENU_MOUSE // for mouse selection of menu's etc.
1500                 if ( !done && mouse_state && !omouse_state && !all_text ) {
1501                         mouse_get_pos(&mx, &my);
1502                         for (i = 0; i < nitems; i++ ) {
1503                                 x1 = grd_curcanv->cv_bitmap.bm_x + item[i].x - item[i].right_offset - 6;
1504                                 x2 = x1 + item[i].w;
1505                                 y1 = grd_curcanv->cv_bitmap.bm_y + item[i].y;
1506                                 y2 = y1 + item[i].h;
1507                                 if (((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2))) {
1508                                         if (i+ScrollOffset != choice) {
1509                                                 if (Hack_DblClick_MenuMode)
1510                                                         dblclick_flag = 0;
1511                                         }
1512                                         
1513                                         choice = i + ScrollOffset;
1514
1515                                         switch( item[choice].type ) {
1516                                         case NM_TYPE_CHECK:
1517                                                 if ( item[choice].value )
1518                                                         item[choice].value = 0;
1519                                                 else
1520                                                         item[choice].value = 1;
1521                                                 item[choice].redraw = 1;
1522
1523                                                 if (IsScrollBox)
1524                                                         LastScrollCheck = -1;
1525 #if 0
1526                                                 if (IsScrollBox) {
1527                                                         if (choice == (MaxOnMenu+ScrollOffset-1) || choice == ScrollOffset) {
1528                                                                 mprintf((0, "Special redraw!\n"));
1529                                                                 LastScrollCheck = -1;
1530                                                         }
1531                                                 }
1532 #endif
1533                                                 break;
1534                                         case NM_TYPE_RADIO:
1535                                                 for ( i = 0; i < nitems; i++ ) {
1536                                                         if ((i != choice) && (item[i].type == NM_TYPE_RADIO) && (item[i].group == item[choice].group) && (item[i].value) ) {
1537                                                                 item[i].value = 0;
1538                                                                 item[i].redraw = 1;
1539                                                         }
1540                                                 }
1541                                                 item[choice].value = 1;
1542                                                 item[choice].redraw = 1;
1543                                                 break;
1544                                         }
1545                                         item[old_choice].redraw = 1;
1546                                         break;
1547                                 }
1548                         }
1549                 }
1550
1551                 if (mouse_state && all_text)
1552                         done = 1;
1553
1554                 if ( !done && mouse_state && !all_text ) {
1555                         mouse_get_pos(&mx, &my);
1556
1557                         // check possible scrollbar stuff first
1558                         if (IsScrollBox) {
1559                                 int arrow_width, arrow_height, aw;
1560
1561                                 if (ScrollOffset != 0) {
1562                                         gr_get_string_size(UP_ARROW_MARKER, &arrow_width, &arrow_height, &aw);
1563                                         x2 = grd_curcanv->cv_bitmap.bm_x + item[ScrollOffset].x-(MenuHires?24:12);
1564                                         y1 = grd_curcanv->cv_bitmap.bm_y + item[ScrollOffset].y-((string_height+1)*ScrollOffset);
1565                                         x1 = x1 - arrow_width;
1566                                         y2 = y1 + arrow_height;
1567                                         if (((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
1568                                                 choice--;
1569                                                 LastScrollCheck = -1;
1570                                                 mprintf((0, "Scrolling! Choice=%d\n", choice));
1571
1572                                                 if (choice < ScrollOffset) {
1573                                                         for (i = 0; i < nitems; i++)
1574                                                                 item[i].redraw = 1;
1575                                                         ScrollOffset--;
1576                                                         mprintf((0, "ScrollOffset=%d\n", ScrollOffset));
1577                                                 }
1578                                         }
1579                                 }
1580                                 if (ScrollOffset+MaxDisplayable < nitems) {
1581                                         gr_get_string_size(DOWN_ARROW_MARKER, &arrow_width, &arrow_height, &aw);
1582                                         x2 = grd_curcanv->cv_bitmap.bm_x + item[ScrollOffset+MaxDisplayable-1].x-(MenuHires?24:12);
1583                                         y1 = grd_curcanv->cv_bitmap.bm_y + item[ScrollOffset+MaxDisplayable-1].y-((string_height+1)*ScrollOffset);
1584                                         x1 = x1 - arrow_width;
1585                                         y2 = y1 + arrow_height;
1586                                         if (((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
1587                                                 choice++;
1588                                                 LastScrollCheck = -1;
1589                                                 mprintf((0, "Scrolling! Choice=%d\n", choice));
1590
1591                                                 if (choice >= MaxOnMenu+ScrollOffset) {
1592                                                         for (i = 0; i < nitems; i++)
1593                                                                 item[i].redraw = 1;
1594                                                         ScrollOffset++;
1595                                                         mprintf((0, "ScrollOffset=%d\n", ScrollOffset));
1596                                                 }
1597                                         }
1598                                 }
1599                         }
1600
1601                         for ( i = 0; i < nitems; i++ ) {
1602                                 x1 = grd_curcanv->cv_bitmap.bm_x + item[i].x - item[i].right_offset - 6;
1603                                 x2 = x1 + item[i].w;
1604                                 y1 = grd_curcanv->cv_bitmap.bm_y + item[i].y;
1605                                 y2 = y1 + item[i].h;
1606                                 if (((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) && (item[i].type != NM_TYPE_TEXT) ) {
1607                                         if (i+ScrollOffset != choice) {
1608                                                 if (Hack_DblClick_MenuMode)
1609                                                         dblclick_flag = 0;
1610                                         }
1611
1612                                         choice = i + ScrollOffset;
1613
1614                                         if ( item[choice].type == NM_TYPE_SLIDER ) {
1615                                                 char slider_text[NM_MAX_TEXT_LEN+1], *p, *s1 = NULL;
1616                                                 int slider_width, height, aw, sleft_width, sright_width, smiddle_width;
1617
1618                                                 strcpy(slider_text, item[choice].saved_text);
1619                                                 p = strchr(slider_text, '\t');
1620                                                 if (p) {
1621                                                         *p = '\0';
1622                                                         s1 = p+1;
1623                                                 }
1624                                                 if (p) {
1625                                                         gr_get_string_size(s1, &slider_width, &height, &aw);
1626                                                         gr_get_string_size(SLIDER_LEFT, &sleft_width, &height, &aw);
1627                                                         gr_get_string_size(SLIDER_RIGHT, &sright_width, &height, &aw);
1628                                                         gr_get_string_size(SLIDER_MIDDLE, &smiddle_width, &height, &aw);
1629
1630                                                         x1 = grd_curcanv->cv_bitmap.bm_x + item[choice].x + item[choice].w - slider_width;
1631                                                         x2 = x1 + slider_width + sright_width;
1632                                                         if ( (mx > x1) && (mx < (x1 + sleft_width)) && (item[choice].value != item[choice].min_value) ) {
1633                                                                 item[choice].value = item[choice].min_value;
1634                                                                 item[choice].redraw = 2;
1635                                                         } else if ( (mx < x2) && (mx > (x2 - sright_width)) && (item[choice].value != item[choice].max_value) ) {
1636                                                                 item[choice].value = item[choice].max_value;
1637                                                                 item[choice].redraw = 2;
1638                                                         } else if ( (mx > (x1 + sleft_width)) && (mx < (x2 - sright_width)) ) {
1639                                                                 int num_values, value_width, new_value;
1640                                                                 
1641                                                                 num_values = item[choice].max_value - item[choice].min_value + 1;
1642                                                                 value_width = (slider_width - sleft_width - sright_width) / num_values;
1643                                                                 new_value = (mx - x1 - sleft_width) / value_width;
1644                                                                 if ( item[choice].value != new_value ) {
1645                                                                         item[choice].value = new_value;
1646                                                                         item[choice].redraw = 2;
1647                                                                 }
1648                                                         }
1649                                                         *p = '\t';
1650                                                 }
1651                                         }
1652                                         if (choice == old_choice)
1653                                                 break;
1654                                         if ((item[choice].type == NM_TYPE_INPUT) && (choice != old_choice))
1655                                                 item[choice].value = -1;
1656                                         if ((old_choice > -1) && (item[old_choice].type == NM_TYPE_INPUT_MENU) && (old_choice != choice)) {
1657                                                 item[old_choice].group = 0;
1658                                                 strcpy( item[old_choice].text, item[old_choice].saved_text );
1659                                                 item[old_choice].value = -1;
1660                                         }
1661                                         if (old_choice > -1)
1662                                                 item[old_choice].redraw = 1;
1663                                         item[choice].redraw = 1;
1664                                         break;
1665                                 }
1666                         }
1667                 }
1668
1669                 if ( !done && !mouse_state && omouse_state && !all_text && (choice != -1) && (item[choice].type == NM_TYPE_MENU) ) {
1670                         mouse_get_pos(&mx, &my);
1671                         x1 = grd_curcanv->cv_bitmap.bm_x + item[choice].x;
1672                         x2 = x1 + item[choice].w;
1673                         y1 = grd_curcanv->cv_bitmap.bm_y + item[choice].y;
1674                         y2 = y1 + item[choice].h;
1675                         if (((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2))) {
1676                                 if (Hack_DblClick_MenuMode) {
1677                                         if (dblclick_flag)
1678                                                 done = 1;
1679                                         else
1680                                                 dblclick_flag = 1;
1681                                 }
1682                                 else
1683                                         done = 1;
1684                         }
1685                 }
1686
1687                 if ( !done && !mouse_state && omouse_state && (choice > -1) && (item[choice].type == NM_TYPE_INPUT_MENU) && (item[choice].group == 0)) {
1688                         item[choice].group = 1;
1689                         item[choice].redraw = 1;
1690                         if ( !strnicmp( item[choice].saved_text, TXT_EMPTY, strlen(TXT_EMPTY) ) ) {
1691                                 item[choice].text[0] = 0;
1692                                 item[choice].value = -1;
1693                         } else
1694                                 strip_end_whitespace(item[choice].text);
1695                 }
1696
1697                 if ( !done && !mouse_state && omouse_state && close_box ) {
1698                         mouse_get_pos(&mx, &my);
1699                         x1 = grd_curcanv->cv_bitmap.bm_x + CLOSE_X;
1700                         x2 = x1 + CLOSE_SIZE;
1701                         y1 = grd_curcanv->cv_bitmap.bm_y + CLOSE_Y;
1702                         y2 = y1 + CLOSE_SIZE;
1703                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
1704                                 choice = -1;
1705                                 done = 1;
1706                         }
1707                 }
1708
1709                 // HACK! Don't redraw loadgame preview
1710                 if (RestoringMenu)
1711                         item[0].redraw = 0;
1712 #endif // NEWMENU_MOUSE
1713
1714                 if ( choice > -1 ) {
1715                         int ascii;
1716
1717                         if ( ((item[choice].type == NM_TYPE_INPUT) || ((item[choice].type == NM_TYPE_INPUT_MENU) && (item[choice].group == 1)) ) && (old_choice == choice) ) {
1718                                 if ( k == KEY_LEFT || k == KEY_BACKSP || k == KEY_PAD4 ) {
1719                                         if (item[choice].value == -1)
1720                                                 item[choice].value = (int)strlen(item[choice].text);
1721                                         if (item[choice].value > 0)
1722                                                 item[choice].value--;
1723                                         item[choice].text[item[choice].value] = 0;
1724                                         item[choice].redraw = 1;
1725                                 } else {
1726                                         ascii = key_to_ascii(k);
1727                                         if ((ascii < 255 ) && (item[choice].value < item[choice].text_len )) {
1728                                                 int allowed;
1729
1730                                                 if (item[choice].value == -1)
1731                                                         item[choice].value = 0;
1732
1733                                                 allowed = char_allowed(ascii);
1734
1735                                                 if (!allowed && (ascii == ' ') && char_allowed('_')) {
1736                                                         ascii = '_';
1737                                                         allowed = 1;
1738                                                 }
1739
1740                                                 if (allowed) {
1741                                                         item[choice].text[item[choice].value++] = ascii;
1742                                                         item[choice].text[item[choice].value] = 0;
1743                                                         item[choice].redraw = 1;
1744                                                 }
1745                                         }
1746                                 }
1747                         } else if ( (item[choice].type != NM_TYPE_INPUT) && (item[choice].type != NM_TYPE_INPUT_MENU) ) {
1748                                 ascii = key_to_ascii(k);
1749                                 if (ascii < 255 ) {
1750                                         int choice1 = choice;
1751
1752                                         ascii = toupper(ascii);
1753                                         do {
1754                                                 int i,ch;
1755
1756                                                 choice1++;
1757                                                 if (choice1 >= nitems )
1758                                                         choice1 = 0;
1759                                                 for (i = 0; (ch = item[choice1].text[i]) != 0 && ch == ' '; i++);
1760                                                 if ( ( (item[choice1].type==NM_TYPE_MENU) ||
1761                                                        (item[choice1].type==NM_TYPE_CHECK) ||
1762                                                        (item[choice1].type==NM_TYPE_RADIO) ||
1763                                                        (item[choice1].type==NM_TYPE_NUMBER) ||
1764                                                        (item[choice1].type==NM_TYPE_SLIDER) ) &&
1765                                                      (ascii == toupper(ch)) ) {
1766                                                         k = 0;
1767                                                         choice = choice1;
1768                                                         if (old_choice > -1)
1769                                                                 item[old_choice].redraw = 1;
1770                                                         item[choice].redraw = 1;
1771                                                 }
1772                                         } while (choice1 != choice );
1773                                 }
1774                         }
1775
1776                         if ( (item[choice].type == NM_TYPE_NUMBER) || (item[choice].type == NM_TYPE_SLIDER)) {
1777                                 int ov = item[choice].value;
1778
1779                                 switch( k ) {
1780                                 case KEY_PAD4:
1781                                 case KEY_LEFT:
1782                                 case KEY_MINUS:
1783                                 case KEY_MINUS+KEY_SHIFTED:
1784                                 case KEY_PADMINUS:
1785                                         item[choice].value -= 1;
1786                                         break;
1787                                 case KEY_RIGHT:
1788                                 case KEY_PAD6:
1789                                 case KEY_EQUAL:
1790                                 case KEY_EQUAL+KEY_SHIFTED:
1791                                 case KEY_PADPLUS:
1792                                         item[choice].value++;
1793                                         break;
1794                                 case KEY_PAGEUP:
1795                                 case KEY_PAD9:
1796                                 case KEY_SPACEBAR:
1797                                         item[choice].value += 10;
1798                                         break;
1799                                 case KEY_PAGEDOWN:
1800                                 case KEY_BACKSP:
1801                                 case KEY_PAD3:
1802                                         item[choice].value -= 10;
1803                                         break;
1804                                 }
1805                                 if (ov != item[choice].value)
1806                                         item[choice].redraw = 1;
1807                         }
1808                 }
1809
1810                 gr_set_current_canvas(bg.menu_canvas);
1811
1812                 // Redraw everything...
1813                 for (i = ScrollOffset; i < MaxDisplayable+ScrollOffset; i++ ) {
1814                         if (item[i].redraw) { // warning! ugly hack below
1815                                 item[i].y -= ((string_height+1)*ScrollOffset);
1816                                 newmenu_hide_cursor();
1817                                 draw_item( &bg, &item[i], (i == choice && !all_text), TinyMode );
1818                                 item[i].redraw = 0;
1819 #ifdef NEWMENU_MOUSE
1820                                 if (!MenuReordering && !joydefs_calibrating)
1821                                         newmenu_show_cursor();
1822 #endif
1823                                 item[i].y += ((string_height+1)*ScrollOffset);
1824                         }
1825                         if ((i == choice) && (item[i].type == NM_TYPE_INPUT || (item[i].type == NM_TYPE_INPUT_MENU && item[i].group)))
1826                                 update_cursor( &item[i] );
1827                 }
1828                 vid_update();
1829
1830                 if (IsScrollBox) {
1831                         //grd_curcanv->cv_font = NORMAL_FONT;
1832
1833                         if (LastScrollCheck != ScrollOffset) {
1834                                 LastScrollCheck = ScrollOffset;
1835                                 grd_curcanv->cv_font = SELECTED_FONT;
1836
1837                                 sy = item[ScrollOffset].y-((string_height+1)*ScrollOffset);
1838                                 sx = item[ScrollOffset].x-(MenuHires?24:12);
1839
1840                                 if (ScrollOffset != 0)
1841                                         nm_rstring( &bg, (MenuHires?20:10), sx, sy, UP_ARROW_MARKER );
1842                                 else
1843                                         nm_rstring( &bg, (MenuHires?20:10), sx, sy, "  " );
1844
1845                                 sy = item[ScrollOffset+MaxDisplayable-1].y-((string_height+1)*ScrollOffset);
1846                                 sx = item[ScrollOffset+MaxDisplayable-1].x-(MenuHires?24:12);
1847
1848                                 if (ScrollOffset+MaxDisplayable < nitems)
1849                                         nm_rstring( &bg, (MenuHires?20:10), sx, sy, DOWN_ARROW_MARKER );
1850                                 else
1851                                         nm_rstring( &bg, (MenuHires?20:10), sx, sy, "  " );
1852
1853                         }
1854                 }
1855
1856                 if ( !dont_restore && gr_palette_faded_out )
1857                         gr_palette_fade_in( gr_palette, 32, 0 );
1858         }
1859
1860         newmenu_hide_cursor();
1861
1862         // Restore everything...
1863
1864         gr_set_current_canvas(bg.menu_canvas);
1865
1866         if ( filename == NULL ) {
1867                 // Save the background under the menu...
1868                 gr_bitmap(0, 0, bg.saved);
1869                 gr_free_bitmap(bg.saved);
1870                 d_free( bg.background );
1871         } else {
1872                 if (!dont_restore) // info passed back from subfunction
1873                         gr_bitmap(0, 0, bg.background);
1874                 gr_free_bitmap(bg.background);
1875         }
1876
1877         gr_free_sub_canvas( bg.menu_canvas );
1878
1879         gr_set_current_canvas(save_canvas);
1880         grd_curcanv->cv_font = save_font;
1881         keyd_repeat = old_keyd_repeat;
1882
1883         game_flush_inputs();
1884
1885         if (time_stopped) {
1886                 start_time();
1887 #ifdef TACTILE
1888                 if (TactileStick)
1889                         EnableForces();
1890 #endif
1891         }
1892
1893         if ( sound_stopped )
1894                 digi_resume_digi_sounds();
1895
1896         return choice;
1897 }
1898
1899
1900 int nm_messagebox1( char *title, void (*subfunction)(int nitems, newmenu_item *items, int *last_key, int citem), int nchoices, ... )
1901 {
1902         int i;
1903         char *format;
1904         va_list args;
1905         char *s;
1906         char nm_text[MESSAGEBOX_TEXT_SIZE];
1907         newmenu_item nm_message_items[5];
1908
1909         va_start( args, nchoices );
1910
1911         Assert( nchoices <= 5 );
1912
1913         for ( i = 0; i < nchoices; i++ ) {
1914                 s = va_arg( args, char * );
1915                 nm_message_items[i].type = NM_TYPE_MENU; nm_message_items[i].text = s;
1916         }
1917         format = va_arg( args, char * );
1918         strcpy( nm_text, "" );
1919         vsprintf(nm_text,format,args);
1920         va_end(args);
1921
1922         Assert(strlen(nm_text) < MESSAGEBOX_TEXT_SIZE);
1923
1924         return newmenu_do( title, nm_text, nchoices, nm_message_items, subfunction );
1925 }
1926
1927
1928 int nm_messagebox( char *title, int nchoices, ... )
1929 {
1930         int i;
1931         char * format;
1932         va_list args;
1933         char *s;
1934         char nm_text[MESSAGEBOX_TEXT_SIZE];
1935         newmenu_item nm_message_items[5];
1936
1937         va_start(args, nchoices );
1938
1939         Assert( nchoices <= 5 );
1940
1941         for ( i = 0; i < nchoices; i++ ) {
1942                 s = va_arg( args, char * );
1943                 nm_message_items[i].type = NM_TYPE_MENU; nm_message_items[i].text = s;
1944         }
1945         format = va_arg( args, char * );
1946         strcpy( nm_text, "" );
1947         vsprintf(nm_text, format, args);
1948         va_end(args);
1949
1950         Assert(strlen(nm_text) < MESSAGEBOX_TEXT_SIZE );
1951
1952         return newmenu_do( title, nm_text, nchoices, nm_message_items, NULL );
1953 }
1954
1955
1956 void newmenu_file_sort( int n, char *list )
1957 {
1958         int i, j, incr;
1959         char t[14];
1960
1961         incr = n / 2;
1962         while( incr > 0 ) {
1963                 for (i = incr; i < n; i++ ) {
1964                         j = i - incr;
1965                         while (j >= 0 ) {
1966                                 if (strncmp(&list[j*14], &list[(j+incr)*14], 12) > 0) {
1967                                         memcpy( t, &list[j*14], FILENAME_LEN );
1968                                         memcpy( &list[j*14], &list[(j+incr)*14], FILENAME_LEN );
1969                                         memcpy( &list[(j+incr)*14], t, FILENAME_LEN );
1970                                         j -= incr;
1971                                 } else
1972                                         break;
1973                         }
1974                 }
1975                 incr = incr / 2;
1976         }
1977 }
1978
1979 void delete_player_saved_games(char *name)
1980 {
1981         int i;
1982         char filename[16];
1983
1984         for (i = 0; i < 10; i++) {
1985                 sprintf( filename, PLAYER_DIR "%s.sg%d", name, i );
1986                 PHYSFS_delete(filename);
1987         }
1988 }
1989
1990
1991 #define MAX_FILES 300
1992
1993
1994 // FIXME: should maybe put globbing ability back?
1995 int newmenu_get_filename(char *title, char *type, char *filename, int allow_abort_flag)
1996 {
1997         int i;
1998         char **find;
1999         char **f;
2000         char *ext;
2001         int NumFiles=0, key,done, citem, ocitem;
2002         char *filenames = NULL;
2003         int NumFiles_displayed = 8;
2004         int first_item = -1, ofirst_item;
2005         int old_keyd_repeat = keyd_repeat;
2006         int player_mode = 0;
2007         int demo_mode = 0;
2008         int demos_deleted = 0;
2009         int initialized = 0;
2010         int exit_value = 0;
2011         int w_x, w_y, w_w, w_h, title_height;
2012         int box_x, box_y, box_w, box_h;
2013         bkg bg = { NULL, NULL, NULL}; // background under listbox
2014 #ifdef NEWMENU_MOUSE
2015         int mx, my, x1, x2, y1, y2, mouse_state, omouse_state;
2016         int mouse2_state, omouse2_state;
2017         int dblclick_flag = 0;
2018 # ifdef WINDOWS
2019         int simukey = 0;
2020         int show_up_arrow = 0, show_down_arrow = 0;
2021 # endif
2022 #endif
2023
2024         w_x = w_y = w_w = w_h = title_height = 0;
2025         box_x = box_y = box_w = box_h = 0;
2026
2027         filenames = d_malloc( MAX_FILES * 14 );
2028         if (filenames == NULL)
2029                 return 0;
2030
2031         citem = 0;
2032         keyd_repeat = 1;
2033
2034         if (!stricmp(type, "plr"))
2035                 player_mode = 1;
2036         else if (!stricmp(type, "dem"))
2037                 demo_mode = 1;
2038
2039 ReadFileNames:
2040         done = 0;
2041         NumFiles = 0;
2042
2043 #if !defined(APPLE_DEMO) // no new pilots for special apple oem version
2044         if (player_mode) {
2045                 strncpy( &filenames[NumFiles*14], TXT_CREATE_NEW, FILENAME_LEN );
2046                 NumFiles++;
2047         }
2048 #endif
2049
2050         find = PHYSFS_enumerateFiles(demo_mode?DEMO_DIR:"");
2051         for (f = find; *f != NULL; f++) {
2052                 if (player_mode) {
2053                         ext = strrchr(*f, '.');
2054                         if (!ext || strnicmp(ext, ".plr", 4))
2055                                 continue;
2056                 }
2057                 if (NumFiles < MAX_FILES) {
2058                         strncpy(&filenames[NumFiles*14], *f, FILENAME_LEN);
2059                         if (player_mode) {
2060                                 char *p;
2061
2062                                 p = strchr(&filenames[NumFiles*14], '.');
2063                                 if (p)
2064                                         *p = '\0';
2065                         }
2066                         NumFiles++;
2067                 } else
2068                         break;
2069         }
2070
2071         PHYSFS_freeList(find);
2072
2073         if ( (NumFiles < 1) && demos_deleted ) {
2074                 exit_value = 0;
2075                 goto ExitFileMenu;
2076         }
2077         if ( (NumFiles < 1) && demo_mode ) {
2078                 nm_messagebox( NULL, 1, TXT_OK, "%s %s\n%s", TXT_NO_DEMO_FILES, TXT_USE_F5, TXT_TO_CREATE_ONE );
2079                 exit_value = 0;
2080                 goto ExitFileMenu;
2081         }
2082
2083 #ifndef APPLE_DEMO
2084         if ( (NumFiles < 2) && player_mode ) {
2085                 citem = 0;
2086                 goto ExitFileMenuEarly;
2087         }
2088 #endif
2089
2090         if ( NumFiles < 1 ) {
2091 #ifndef APPLE_DEMO
2092                 nm_messagebox( NULL, 1, "Ok", "%s\n '%s' %s", TXT_NO_FILES_MATCHING, type, TXT_WERE_FOUND );
2093 #endif
2094                 exit_value = 0;
2095                 goto ExitFileMenu;
2096         }
2097
2098         if (!initialized) {
2099 //              set_screen_mode(SCREEN_MENU);
2100                 set_popup_screen();
2101
2102                 gr_set_current_canvas(NULL);
2103
2104                 grd_curcanv->cv_font = SUBTITLE_FONT;
2105
2106                 w_w = 0;
2107                 w_h = 0;
2108
2109                 for ( i = 0; i < NumFiles; i++ ) {
2110                         int w, h, aw;
2111
2112                         gr_get_string_size( &filenames[i*14], &w, &h, &aw );
2113                         if ( w > w_w )
2114                                 w_w = w;
2115                 }
2116                 if ( title ) {
2117                         int w, h, aw;
2118
2119                         gr_get_string_size( title, &w, &h, &aw );
2120                         if ( w > w_w )
2121                                 w_w = w;
2122                         title_height = h + (grd_curfont->ft_h*2); // add a little space at the bottom of the title
2123                 }
2124
2125                 box_w = w_w;
2126                 box_h = ((grd_curfont->ft_h + 2) * NumFiles_displayed);
2127
2128                 w_w += (grd_curfont->ft_w * 4);
2129                 w_h = title_height + box_h + (grd_curfont->ft_h * 2); // more space at bottom
2130
2131                 if ( w_w > grd_curcanv->cv_w ) w_w = grd_curcanv->cv_w;
2132                 if ( w_h > grd_curcanv->cv_h ) w_h = grd_curcanv->cv_h;
2133
2134                 w_x = (grd_curcanv->cv_w-w_w)/2;
2135                 w_y = (grd_curcanv->cv_h-w_h)/2;
2136
2137                 if ( w_x < 0 ) w_x = 0;
2138                 if ( w_y < 0 ) w_y = 0;
2139
2140                 box_x = w_x + (grd_curfont->ft_w*2); // must be in sync with w_w!!!
2141                 box_y = w_y + title_height;
2142
2143                 // save the screen behind the menu.
2144
2145                 bg.saved = NULL;
2146
2147                 if ( (VR_offscreen_buffer->cv_w >= w_w) && (VR_offscreen_buffer->cv_h >= w_h) ) 
2148                         bg.background = &VR_offscreen_buffer->cv_bitmap;
2149                 else
2150                         bg.background = gr_create_bitmap( w_w, w_h );
2151
2152                 Assert( bg.background != NULL );
2153
2154                 gr_bm_bitblt( w_w, w_h, 0, 0, w_x, w_y, &grd_curcanv->cv_bitmap, bg.background );
2155
2156 #if 0
2157                 gr_bm_bitblt( grd_curcanv->cv_w, grd_curcanv->cv_h, 0, 0, 0, 0, &(grd_curcanv->cv_bitmap), &(VR_offscreen_buffer->cv_bitmap) );
2158 #endif
2159
2160                 nm_draw_background( w_x, w_y, w_x+w_w-1, w_y+w_h-1 );
2161
2162                 gr_string( 0x8000, w_y+10, title );
2163
2164                 initialized = 1;
2165         }
2166
2167         if ( !player_mode )
2168                 newmenu_file_sort( NumFiles, filenames );
2169         else {
2170 #if defined(MACINTOSH) && defined(APPLE_DEMO)
2171                 newmenu_file_sort( NumFiles, filenames );
2172 #else
2173                 newmenu_file_sort( NumFiles-1, &filenames[14] ); // Don't sort first one!
2174 #endif
2175                 for ( i = 0; i < NumFiles; i++ ) {
2176                         if ( !stricmp(Players[Player_num].callsign, &filenames[i*14]) ) {
2177 #ifdef NEWMENU_MOUSE
2178                                 dblclick_flag = 1;
2179 #endif
2180                                 citem = i;
2181                         }
2182                 }
2183         }
2184
2185 #ifdef NEWMENU_MOUSE
2186         mouse_state = omouse_state = 0;
2187         mouse2_state = omouse2_state = 0;
2188         //draw_close_box(w_x, w_y);
2189         newmenu_show_cursor();
2190 #endif
2191
2192         while(!done) {
2193                 ocitem = citem;
2194                 ofirst_item = first_item;
2195                 vid_update();
2196
2197 #ifdef NEWMENU_MOUSE
2198                 omouse_state = mouse_state;
2199                 omouse2_state = mouse2_state;
2200                 mouse_state = mouse_button_state(0);
2201                 mouse2_state = mouse_button_state(1);
2202 #endif
2203
2204                 // see if redbook song needs to be restarted
2205                 songs_check_redbook_repeat();
2206
2207                 key = newmenu_inkey();
2208
2209                 switch(key) {
2210                 case KEY_COMMAND+KEY_SHIFTED+KEY_3:
2211                 case KEY_PRINT_SCREEN:
2212                         MAC(newmenu_hide_cursor());
2213                         save_screen_shot(0);
2214
2215                         MAC(newmenu_show_cursor());
2216                         MAC(key_flush());
2217                         break;
2218
2219                 case KEY_CTRLED+KEY_D:
2220 #if defined(MACINTOSH) && defined(APPLE_DEMO)
2221                         break;
2222 #endif
2223
2224                         if ( (player_mode && (citem > 0)) || (demo_mode && (citem >= 0)) ) {
2225                                 int x = 1;
2226
2227                                 newmenu_hide_cursor();
2228                                 if (player_mode)
2229                                         x = nm_messagebox( NULL, 2, TXT_YES, TXT_NO, "%s %s?", TXT_DELETE_PILOT, &filenames[citem*14]+((player_mode && filenames[citem*14]=='$')?1:0) );
2230                                 else if (demo_mode)
2231                                         x = nm_messagebox( NULL, 2, TXT_YES, TXT_NO, "%s %s?", TXT_DELETE_DEMO, &filenames[citem*14]+((demo_mode && filenames[citem*14]=='$')?1:0) );
2232                                 newmenu_show_cursor();
2233                                 if (x == 0) {
2234                                         char * p;
2235                                         int ret;
2236                                         char name[PATH_MAX];
2237
2238                                         p = &filenames[(citem*14)+strlen(&filenames[citem*14])];
2239                                         if (player_mode)
2240                                                 *p = '.';
2241
2242                                         strcpy(name, demo_mode?DEMO_DIR:"");
2243                                         strcat(name, &filenames[citem*14]);
2244
2245 #ifdef MACINTOSH
2246                                         {
2247                                                 int i;
2248                                                 char *p;
2249
2250                                                 if ( !strncmp(name, ".\\", 2) )
2251                                                         for (i = 0; i < strlen(name); i++) // don't subtract 1 from strlen to get the EOS marker
2252                                                                 name[i] = name[i+1];
2253                                                 while ( (p = strchr(name, '\\')) )
2254                                                         *p = ':';
2255                                         }
2256 #endif
2257
2258                                         ret = !PHYSFS_delete(name);
2259                                         if (player_mode)
2260                                                 *p = 0;
2261
2262                                         if ((!ret) && player_mode)
2263                                                 delete_player_saved_games( &filenames[citem*14] );
2264
2265                                         if (ret) {
2266                                                 if (player_mode)
2267                                                         nm_messagebox( NULL, 1, TXT_OK, "%s %s %s", TXT_COULDNT, TXT_DELETE_PILOT, &filenames[citem*14]+((player_mode && filenames[citem*14]=='$')?1:0) );
2268                                                 else if (demo_mode)
2269                                                         nm_messagebox( NULL, 1, TXT_OK, "%s %s %s", TXT_COULDNT, TXT_DELETE_DEMO, &filenames[citem*14]+((demo_mode && filenames[citem*14]=='$')?1:0) );
2270                                         } else if (demo_mode)
2271                                                 demos_deleted = 1;
2272                                         first_item = -1;
2273                                         goto ReadFileNames;
2274                                 }
2275                         }
2276                         break;
2277                 case KEY_HOME:
2278                 case KEY_PAD7:
2279                         citem = 0;
2280                         break;
2281                 case KEY_END:
2282                 case KEY_PAD1:
2283                         citem = NumFiles-1;
2284                         break;
2285                 case KEY_UP:
2286                 case KEY_PAD8:
2287                         citem--;
2288                         break;
2289                 case KEY_DOWN:
2290                 case KEY_PAD2:
2291                         citem++;
2292                         break;
2293                 case KEY_PAGEDOWN:
2294                 case KEY_PAD3:
2295                         citem += NumFiles_displayed;
2296                         break;
2297                 case KEY_PAGEUP:
2298                 case KEY_PAD9:
2299                         citem -= NumFiles_displayed;
2300                         break;
2301                 case KEY_ESC:
2302                 case KEY_LEFT:
2303                         if (allow_abort_flag) {
2304                                 citem = -1;
2305                                 done = 1;
2306                         }
2307                         break;
2308                 case KEY_ENTER:
2309                 case KEY_PADENTER:
2310                 case KEY_RIGHT:
2311                         done = 1;
2312                         break;
2313                 case KEY_COMMAND+KEY_Q:
2314                         if ( !(Game_mode & GM_MULTI) ) {
2315                                 d_free(filenames);
2316                                 quit_request();
2317                         }
2318                         newmenu_show_cursor();
2319                         key_flush();
2320                         break;
2321                 default:
2322                         {
2323                                 int ascii = key_to_ascii(key);
2324
2325                                 if ( ascii < 255 ) {
2326                                         int cc, cc1;
2327                                         cc = cc1 = citem+1;
2328                                         if (cc1 < 0 ) cc1 = 0;
2329                                         if (cc1 >= NumFiles ) cc1 = 0;
2330                                         while(1) {
2331                                                 if ( cc < 0 ) cc = 0;
2332                                                 if ( cc >= NumFiles ) cc = 0;
2333                                                 if ( citem == cc ) break;
2334
2335                                                 if ( toupper(filenames[cc*14]) == toupper(ascii) ) {
2336                                                         citem = cc;
2337                                                         break;
2338                                                 }
2339                                                 cc++;
2340                                         }
2341                                 }
2342                         }
2343                 }
2344                 if ( done )
2345                         break;
2346
2347                 if (citem < 0)
2348                         citem = 0;
2349
2350                 if (citem >= NumFiles)
2351                         citem = NumFiles-1;
2352
2353                 if (citem < first_item)
2354                         first_item = citem;
2355
2356                 if (citem >= (first_item+NumFiles_displayed))
2357                         first_item = citem-NumFiles_displayed+1;
2358
2359 #ifdef WINDOWS
2360                 if (NumFiles > first_item+NumFiles_displayed)
2361                         show_down_arrow = 1;
2362                 else 
2363                         show_down_arrow = 0;
2364
2365                 if (first_item > 0)
2366                         show_up_arrow = 1;
2367                 else
2368                         show_up_arrow = 0;
2369 #endif
2370
2371                 if (NumFiles <= NumFiles_displayed)
2372                          first_item = 0;
2373
2374                 if (first_item > NumFiles-NumFiles_displayed)
2375                         first_item = NumFiles-NumFiles_displayed;
2376
2377                 if (first_item < 0 )
2378                         first_item = 0;
2379
2380 #ifdef NEWMENU_MOUSE
2381                 if (mouse_state || mouse2_state) {
2382                         int w, h, aw;
2383
2384                         mouse_get_pos(&mx, &my);
2385                         for ( i = first_item; i < first_item+NumFiles_displayed; i++ ) {
2386                                 gr_get_string_size( &filenames[i*14], &w, &h, &aw );
2387                                 x1 = box_x;
2388                                 x2 = box_x + box_w - 1;
2389                                 y1 = (i-first_item)*(grd_curfont->ft_h + 2) + box_y;
2390                                 y2 = y1+h+1;
2391                                 if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2392                                         if (i == citem && !mouse2_state)
2393                                                 break;
2394                                         citem = i;
2395                                         dblclick_flag = 0;
2396                                         break;
2397                                 }
2398                         }
2399                 }
2400
2401                 if (!mouse_state && omouse_state) {
2402                         int w, h, aw;
2403
2404                         gr_get_string_size( &filenames[citem*14], &w, &h, &aw );
2405                         mouse_get_pos(&mx, &my);
2406                         x1 = box_x;
2407                         x2 = box_x + box_w - 1;
2408                         y1 = (citem-first_item)*(grd_curfont->ft_h + 2) + box_y;
2409                         y2 = y1+h+1;
2410                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2411                                 if (dblclick_flag)
2412                                         done = 1;
2413                                 else
2414                                         dblclick_flag = 1;
2415                         }
2416                 }
2417
2418                 if ( !mouse_state && omouse_state ) {
2419                         mouse_get_pos(&mx, &my);
2420                         x1 = w_x + CLOSE_X + 2;
2421                         x2 = x1 + CLOSE_SIZE - 2;
2422                         y1 = w_y + CLOSE_Y + 2;
2423                         y2 = y1 + CLOSE_SIZE - 2;
2424                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2425                                 citem = -1;
2426                                 done = 1;
2427                         }
2428 #ifdef WINDOWS
2429                         x1 = box_x-LHX(10);
2430                         x2 = x1 + LHX(10);
2431                         y1 = box_y;
2432                         y2 = box_y+LHY(7);
2433                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) && show_up_arrow ) 
2434                                 simukey = -1;
2435                         y1 = box_y+box_h-LHY(7);
2436                         y2 = box_y+box_h;
2437                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) && show_down_arrow) 
2438                                 simukey = 1;
2439 #endif
2440                 }
2441 #endif
2442
2443                 gr_setcolor( BM_XRGB(2,2,2) );
2444                 //gr_rect( box_x - 1, box_y-2, box_x + box_w, box_y-2 );
2445                 gr_setcolor( BM_XRGB( 0,0,0) );
2446
2447                 if (ofirst_item != first_item) {
2448                         newmenu_hide_cursor();
2449                         gr_setcolor( BM_XRGB( 0,0,0) );
2450                         for (i = first_item; i < first_item+NumFiles_displayed; i++ ) {
2451                                 int w, h, aw, y;
2452
2453                                 y = (i-first_item)*(grd_curfont->ft_h + 2) + box_y;
2454
2455                                 if ( i >= NumFiles ) {
2456                                         gr_setcolor( BM_XRGB(5,5,5) );
2457                                         gr_rect( box_x + box_w, y-1, box_x + box_w, y + grd_curfont->ft_h + 1 );
2458                                         //gr_rect( box_x, y + grd_curfont->ft_h + 2, box_x + box_w, y + grd_curfont->ft_h + 2 );
2459
2460                                         gr_setcolor( BM_XRGB(2,2,2) );
2461                                         gr_rect( box_x - 1, y - 1, box_x - 1, y + grd_curfont->ft_h + 2 );
2462
2463                                         gr_setcolor( BM_XRGB(0,0,0) );
2464                                         gr_rect( box_x, y - 1, box_x + box_w - 1, y + grd_curfont->ft_h + 1 );
2465
2466                                 } else {
2467                                         if ( i == citem )
2468                                                 grd_curcanv->cv_font = SELECTED_FONT;
2469                                         else
2470                                                 grd_curcanv->cv_font = NORMAL_FONT;
2471                                         gr_get_string_size( &filenames[i*14], &w, &h, &aw );
2472
2473                                         gr_setcolor( BM_XRGB(5,5,5) );
2474                                         //gr_rect( box_x, y + h + 2, box_x + box_w, y + h + 2 );
2475                                         gr_rect( box_x + box_w, y - 1, box_x + box_w, y + h + 1 );
2476
2477                                         gr_setcolor( BM_XRGB(2,2,2) );
2478                                         gr_rect( box_x - 1, y - 1, box_x - 1, y + h + 1 );
2479                                         gr_setcolor( BM_XRGB(0,0,0) );
2480
2481                                         gr_rect( box_x, y-1, box_x + box_w - 1, y + h + 1 );
2482                                         gr_string( box_x + 5, y, (&filenames[i*14])+((player_mode && filenames[i*14]=='$')?1:0) );
2483                                 }
2484                         }
2485                         newmenu_show_cursor();
2486                 } else if ( citem != ocitem ) {
2487                         int w, h, aw, y;
2488
2489                         newmenu_hide_cursor();
2490                         i = ocitem;
2491                         if ( (i >= 0) && (i < NumFiles) ) {
2492                                 y = (i-first_item)*(grd_curfont->ft_h+2)+box_y;
2493                                 if ( i == citem )
2494                                         grd_curcanv->cv_font = SELECTED_FONT;
2495                                 else
2496                                         grd_curcanv->cv_font = NORMAL_FONT;
2497                                 gr_get_string_size( &filenames[i*14], &w, &h, &aw );
2498                                 gr_rect( box_x, y-1, box_x + box_w - 1, y + h + 1 );
2499                                 gr_string( box_x + 5, y, (&filenames[i*14])+((player_mode && filenames[i*14]=='$')?1:0) );
2500                         }
2501                         i = citem;
2502                         if ( (i>=0) && (i<NumFiles) ) {
2503                                 y = (i-first_item)*(grd_curfont->ft_h+2)+box_y;
2504                                 if ( i == citem )
2505                                         grd_curcanv->cv_font = SELECTED_FONT;
2506                                 else
2507                                         grd_curcanv->cv_font = NORMAL_FONT;
2508                                 gr_get_string_size( &filenames[i*14], &w, &h, &aw );
2509                                 gr_rect( box_x, y-1, box_x + box_x - 1, y + h + 1 );
2510                                 gr_string( box_x + 5, y, (&filenames[i*14])+((player_mode && filenames[i*14]=='$')?1:0) );
2511                         }
2512                         newmenu_show_cursor();
2513                 }
2514
2515 #ifdef WINDOWS
2516                 grd_curcanv->cv_font = NORMAL_FONT;
2517                 if (show_up_arrow)
2518                         gr_string( box_x-LHX(10), box_y, UP_ARROW_MARKER );
2519                 else {
2520                         No_darkening = 1;
2521                         nm_draw_background(box_x-LHX(10), box_y, box_x-2, box_y+LHY(7));
2522                         No_darkening = 0;
2523                 }
2524
2525                 if (show_down_arrow)
2526                         gr_string( box_x-LHX(10), box_y+box_h-LHY(7), DOWN_ARROW_MARKER );
2527                 else {
2528                         No_darkening = 1;
2529                         nm_draw_background(box_x-LHX(10), box_y+box_h-LHY(7), box_x-2, box_y+box_h);
2530                         No_darkening = 0;
2531                 }
2532 #endif
2533         }
2534
2535 ExitFileMenuEarly:
2536         MAC(newmenu_hide_cursor());
2537         if ( citem > -1 ) {
2538                 strncpy( filename, (&filenames[citem*14])+((player_mode && filenames[citem*14]=='$')?1:0), FILENAME_LEN );
2539                 exit_value = 1;
2540         } else
2541                 exit_value = 0;
2542
2543 ExitFileMenu:
2544         keyd_repeat = old_keyd_repeat;
2545
2546         if ( initialized ) {
2547                 if (Newdemo_state != ND_STATE_PLAYBACK) // horrible hack to prevent restore when screen has been cleared
2548                         gr_bm_bitblt(w_w, w_h, w_x, w_y, 0, 0, bg.background, &grd_curcanv->cv_bitmap );
2549                 if ( bg.background != &VR_offscreen_buffer->cv_bitmap )
2550                         gr_free_bitmap(bg.background);
2551 #if 0
2552                 gr_bm_bitblt(grd_curcanv->cv_w, grd_curcanv->cv_h, 0, 0, 0, 0, &(VR_offscreen_buffer->cv_bitmap), &(grd_curcanv->cv_bitmap) )
2553 #endif
2554         }
2555
2556         if ( filenames )
2557                 d_free(filenames);
2558
2559         return exit_value;
2560 }
2561
2562
2563 // Example listbox callback function...
2564 // int lb_callback( int * citem, int *nitems, char * items[], int *keypress )
2565 // {
2566 //      int i;
2567 // 
2568 //      if ( *keypress = KEY_CTRLED+KEY_D ) {
2569 //              if ( *nitems > 1 ) {
2570 //                      PHYSFS_delete(items[*citem]); // Delete the file
2571 //                      for ( i = *citem; i < *nitems-1; i++ ) {
2572 //                              items[i] = items[i+1];
2573 //                      }
2574 //                      *nitems = *nitems - 1;
2575 //                      d_free( items[*nitems] );
2576 //                      items[*nitems] = NULL;
2577 //                      return 1; // redraw;
2578 //              }
2579 //              *keypress = 0;
2580 //      }
2581 //      return 0;
2582 // }
2583
2584
2585 #define LB_ITEMS_ON_SCREEN 8
2586
2587
2588 int newmenu_listbox( char *title, int nitems, char *items[], int allow_abort_flag, int (*listbox_callback)( int *citem, int *nitems, char *items[], int *keypress ) )
2589 {
2590         return newmenu_listbox1( title, nitems, items, allow_abort_flag, 0, listbox_callback );
2591 }
2592
2593
2594 int newmenu_listbox1( char *title, int nitems, char *items[], int allow_abort_flag, int default_item, int (*listbox_callback)( int *citem, int *nitems, char *items[], int *keypress ) )
2595 {
2596         int i;
2597         int done, ocitem,citem, ofirst_item, first_item, key, redraw;
2598         int old_keyd_repeat = keyd_repeat;
2599         int width, height, wx, wy, title_height, border_size;
2600         int total_width, total_height;
2601         bkg bg;
2602 #ifdef NEWMENU_MOUSE
2603         int mx, my, x1, x2, y1, y2, mouse_state, omouse_state; //, dblclick_flag;
2604         int close_x, close_y;
2605 # ifdef WINDOWS
2606         int simukey = 0, show_up_arrow = 0, show_down_arrow = 0;
2607 # endif
2608 #endif
2609
2610         keyd_repeat = 1;
2611
2612 //      set_screen_mode(SCREEN_MENU);
2613         set_popup_screen();
2614
2615         gr_set_current_canvas(NULL);
2616
2617         grd_curcanv->cv_font = SUBTITLE_FONT;
2618
2619         width = 0;
2620         for ( i = 0; i < nitems; i++ ) {
2621                 int w, h, aw;
2622
2623                 gr_get_string_size( items[i], &w, &h, &aw );
2624                 if ( w > width )
2625                         width = w;
2626         }
2627         height = (grd_curfont->ft_h + 2) * LB_ITEMS_ON_SCREEN;
2628         {
2629                 int w, h, aw;
2630
2631                 gr_get_string_size( title, &w, &h, &aw );
2632                 if ( w > width )
2633                         width = w;
2634                 title_height = h + 5;
2635         }
2636
2637         border_size = grd_curfont->ft_w;
2638         WIN(border_size = grd_curfont->ft_w*2);
2639
2640         width += (grd_curfont->ft_w);
2641         if ( width > grd_curcanv->cv_w - (grd_curfont->ft_w * 3) )
2642                 width = grd_curcanv->cv_w - (grd_curfont->ft_w * 3);
2643
2644         wx = (grd_curcanv->cv_bitmap.bm_w-width)/2;
2645         wy = (grd_curcanv->cv_bitmap.bm_h-(height+title_height))/2 + title_height;
2646         if ( wy < title_height )
2647                 wy = title_height;
2648
2649         total_width = width+2*border_size;
2650         total_height = height+2*border_size+title_height;
2651
2652         bg.saved = NULL;
2653
2654         if ( (VR_offscreen_buffer->cv_w >= total_width) && (VR_offscreen_buffer->cv_h >= total_height) )
2655                 bg.background = &VR_offscreen_buffer->cv_bitmap;
2656         else
2657                 //bg.background = gr_create_bitmap( width, (height + title_height) );
2658                 bg.background = gr_create_bitmap(total_width, total_height);
2659         Assert( bg.background != NULL );
2660
2661         //gr_bm_bitblt( wx+width+border_size, wy+height+border_size, 0, 0, wx-border_size, wy-title_height-border_size, &grd_curcanv->cv_bitmap, bg.background );
2662         gr_bm_bitblt(total_width,total_height, 0, 0, wx-border_size, wy-title_height-border_size, &grd_curcanv->cv_bitmap, bg.background );
2663
2664 #if 0
2665         gr_bm_bitblt( grd_curcanv->cv_w, grd_curcanv->cv_h, 0, 0, 0, 0, &(grd_curcanv->cv_bitmap), &(VR_offscreen_buffer->cv_bitmap) );
2666 #endif
2667
2668         nm_draw_background( wx-border_size, wy-title_height-border_size, wx+width+border_size-1, wy+height+border_size-1 );
2669
2670         gr_string( 0x8000, wy - title_height, title );
2671
2672         done = 0;
2673         citem = default_item;
2674         if ( citem < 0 ) citem = 0;
2675         if ( citem >= nitems ) citem = 0;
2676
2677         first_item = -1;
2678
2679 #ifdef NEWMENU_MOUSE
2680         mouse_state = omouse_state = 0; //dblclick_flag = 0;
2681         close_x = wx-border_size;
2682         close_y = wy-title_height-border_size;
2683         //draw_close_box(close_x, close_y);
2684         newmenu_show_cursor();
2685 #endif
2686
2687         while (!done) {
2688                 ocitem = citem;
2689                 ofirst_item = first_item;
2690 #ifdef NEWMENU_MOUSE
2691                 omouse_state = mouse_state;
2692                 mouse_state = mouse_button_state(0);
2693 #endif
2694                 // see if redbook song needs to be restarted
2695                 songs_check_redbook_repeat();
2696
2697                 key = newmenu_inkey();
2698
2699                 if ( listbox_callback )
2700                         redraw = (*listbox_callback)( &citem, &nitems, items, &key );
2701                 else
2702                         redraw = 0;
2703
2704                 if ( key < -1 ) {
2705                         citem = key;
2706                         key = -1;
2707                         done = 1;
2708                 }
2709
2710 #ifdef WINDOWS
2711                 if (simukey == -1)
2712                         key = KEY_UP;
2713                 else if (simukey == 1)
2714                         key = KEY_DOWN;
2715                 simukey = 0;
2716 #endif
2717
2718                 switch(key) {
2719                 case KEY_COMMAND+KEY_SHIFTED+KEY_3:
2720                 case KEY_PRINT_SCREEN:
2721                         MAC(newmenu_hide_cursor());
2722                         save_screen_shot(0); 
2723                         MAC(newmenu_show_cursor());
2724                         MAC(key_flush());
2725                         break;
2726                 case KEY_HOME:
2727                 case KEY_PAD7:
2728                         citem = 0;
2729                         break;
2730                 case KEY_END:
2731                 case KEY_PAD1:
2732                         citem = nitems-1;
2733                         break;
2734                 case KEY_UP:
2735                 case KEY_PAD8:
2736                         citem--;
2737                         break;
2738                 case KEY_DOWN:
2739                 case KEY_PAD2:
2740                         citem++;
2741                         break;
2742                 case KEY_PAGEDOWN:
2743                 case KEY_PAD3:
2744                         citem += LB_ITEMS_ON_SCREEN;
2745                         break;
2746                 case KEY_PAGEUP:
2747                 case KEY_PAD9:
2748                         citem -= LB_ITEMS_ON_SCREEN;
2749                         break;
2750                 case KEY_ESC:
2751                 case KEY_LEFT:
2752                         if (allow_abort_flag) {
2753                                 citem = -1;
2754                                 done = 1;
2755                         }
2756                         break;
2757                 case KEY_ENTER:
2758                 case KEY_PADENTER:
2759                 case KEY_RIGHT:
2760                         done = 1;
2761                         break;
2762                 case KEY_COMMAND+KEY_Q:
2763                         if ( !(Game_mode & GM_MULTI) )
2764                                 quit_request();
2765                         newmenu_show_cursor();
2766                         key_flush();
2767                         break;
2768
2769                 default:
2770                         if ( key > 0 ) {
2771                                 int ascii = key_to_ascii(key);
2772
2773                                 if ( ascii < 255 ) {
2774                                         int cc, cc1;
2775                                         cc = cc1 = citem+1;
2776                                         if (cc1 < 0 ) cc1 = 0;
2777                                         if (cc1 >= nitems ) cc1 = 0;
2778                                         while (1) {
2779                                                 if ( cc < 0 ) cc = 0;
2780                                                 if ( cc >= nitems ) cc = 0;
2781                                                 if ( citem == cc ) break;
2782
2783                                                 if ( toupper( items[cc][0] ) == toupper(ascii) ) {
2784                                                         citem = cc;
2785                                                         break;
2786                                                 }
2787                                                 cc++;
2788                                         }
2789                                 }
2790                         }
2791                 }
2792                 if ( done )
2793                         break;
2794
2795                 if (citem < 0)
2796                         citem = 0;
2797
2798                 if (citem >= nitems)
2799                         citem = nitems-1;
2800
2801                 if (citem < first_item)
2802                         first_item = citem;
2803
2804                 if (citem >= (first_item+LB_ITEMS_ON_SCREEN))
2805                         first_item = citem-LB_ITEMS_ON_SCREEN+1;
2806
2807                 if (nitems <= LB_ITEMS_ON_SCREEN)
2808                         first_item = 0;
2809
2810                 if (first_item > nitems-LB_ITEMS_ON_SCREEN)
2811                         first_item = nitems-LB_ITEMS_ON_SCREEN;
2812                 if (first_item < 0 )
2813                         first_item = 0;
2814
2815 #ifdef WINDOWS
2816                 if (nitems > first_item+LB_ITEMS_ON_SCREEN)
2817                         show_down_arrow = 1;
2818                 else 
2819                         show_down_arrow = 0;
2820                 if (first_item > 0)
2821                         show_up_arrow = 1;
2822                 else
2823                         show_up_arrow = 0;
2824 #endif
2825
2826 #ifdef NEWMENU_MOUSE
2827                 if (mouse_state) {
2828                         int w, h, aw;
2829
2830                         mouse_get_pos(&mx, &my);
2831                         for (i = first_item; i < first_item+LB_ITEMS_ON_SCREEN; i++ ) {
2832                                 if (i > nitems)
2833                                         break;
2834                                 gr_get_string_size( items[i], &w, &h, &aw );
2835                                 x1 = wx;
2836                                 x2 = wx + width;
2837                                 y1 = (i-first_item)*(grd_curfont->ft_h+2)+wy;
2838                                 y2 = y1+h+1;
2839                                 if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2840                                         //if (i == citem) {
2841                                         //      break;
2842                                         //}
2843                                         //dblclick_flag= 0;
2844                                         citem = i;
2845                                         done = 1;
2846                                         break;
2847                                 }
2848                         }
2849                 }
2850
2851                 // no double-click stuff for listbox
2852                 //@@if (!mouse_state && omouse_state) {
2853                 //@@    int w, h, aw;
2854                 //@@
2855                 //@@    gr_get_string_size(items[citem], &w, &h, &aw  );
2856                 //@@    mouse_get_pos(&mx, &my);
2857                 //@@    x1 = wx;
2858                 //@@    x2 = wx + width;
2859                 //@@    y1 = (citem-first_item)*(grd_curfont->ft_h+2)+wy;
2860                 //@@    y2 = y1+h+1;
2861                 //@@    if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2862                 //@@            if (dblclick_flag) done = 1;
2863                 //@@    }
2864                 //@@}
2865
2866                 // check for close box clicked
2867                 if ( !mouse_state && omouse_state ) {
2868                         mouse_get_pos(&mx, &my);
2869                         x1 = close_x + CLOSE_X + 2;
2870                         x2 = x1 + CLOSE_SIZE - 2;
2871                         y1 = close_y + CLOSE_Y + 2;
2872                         y2 = y1 + CLOSE_SIZE - 2;
2873                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) ) {
2874                                 citem = -1;
2875                                 done = 1;
2876                         }
2877
2878 #ifdef WINDOWS
2879                         x1 = wx-LHX(10);
2880                         x2 = x1 + LHX(10);
2881                         y1 = wy;
2882                         y2 = wy+LHY(7);
2883                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) && show_up_arrow )
2884                                 simukey = -1;
2885                         y1 = total_height-LHY(7);
2886                         y2 = total_height;
2887                         if ( ((mx > x1) && (mx < x2)) && ((my > y1) && (my < y2)) && show_down_arrow ) 
2888                                 simukey = 1;
2889 #endif
2890                 }
2891 #endif
2892
2893                 if ( (ofirst_item != first_item) || redraw ) {
2894                         newmenu_hide_cursor();
2895
2896                         gr_setcolor( BM_XRGB(0,0,0) );
2897                         for ( i = first_item; i < first_item+LB_ITEMS_ON_SCREEN; i++ ) {
2898                                 int w, h, aw, y;
2899                                 y = (i-first_item)*(grd_curfont->ft_h+2)+wy;
2900                                 if ( i >= nitems ) {
2901                                         gr_setcolor( BM_XRGB(0,0,0) );
2902                                         gr_rect( wx, y-1, wx+width-1, y+grd_curfont->ft_h + 1 );
2903                                 } else {
2904                                         if ( i == citem )
2905                                                 grd_curcanv->cv_font = SELECTED_FONT;
2906                                         else
2907                                                 grd_curcanv->cv_font = NORMAL_FONT;
2908                                         gr_get_string_size( items[i], &w, &h, &aw );
2909                                         gr_rect( wx, y-1, wx+width-1, y+h+1 );
2910                                         gr_string( wx+5, y, items[i] );
2911                                 }
2912                         }
2913
2914                         // If Win95 port, draw up/down arrows on left side of menu
2915 #ifdef WINDOWS
2916                         grd_curcanv->cv_font = NORMAL_FONT;
2917                         if (show_up_arrow)
2918                                 gr_string( wx-LHX(10), wy ,UP_ARROW_MARKER );
2919                         else {
2920                                 No_darkening = 1;
2921                                 nm_draw_background(wx-LHX(10), wy, wx-2, wy+LHY(7));
2922                                 No_darkening = 0;
2923                         }
2924
2925                         if (show_down_arrow)
2926                                 gr_string( wx-LHX(10), wy+total_height-LHY(7), DOWN_ARROW_MARKER );
2927                         else {
2928                                 No_darkening = 1;
2929                                 nm_draw_background(wx-LHX(10), wy+total_height-LHY(7), wx-2, wy+total_height);
2930                                 No_darkening = 0;
2931                         }
2932 #endif
2933
2934                         newmenu_show_cursor();
2935                         vid_update();
2936                 } else if ( citem != ocitem ) {
2937                         int w, h, aw, y;
2938
2939                         newmenu_hide_cursor();
2940
2941                         i = ocitem;
2942                         if ( (i >= 0) && (i < nitems) ) {
2943                                 y = (i-first_item)*(grd_curfont->ft_h+2)+wy;
2944                                 if ( i == citem )
2945                                         grd_curcanv->cv_font = SELECTED_FONT;
2946                                 else
2947                                         grd_curcanv->cv_font = NORMAL_FONT;
2948                                 gr_get_string_size( items[i], &w, &h, &aw );
2949                                 gr_rect( wx, y-1, wx+width-1, y+h+1 );
2950                                 gr_string( wx+5, y, items[i] );
2951                         }
2952                         i = citem;
2953                         if ( (i >= 0) && (i < nitems) ) {
2954                                 y = (i-first_item)*(grd_curfont->ft_h+2)+wy;
2955                                 if ( i == citem )
2956                                         grd_curcanv->cv_font = SELECTED_FONT;
2957                                 else
2958                                         grd_curcanv->cv_font = NORMAL_FONT;
2959                                 gr_get_string_size( items[i], &w, &h, &aw );
2960                                 gr_rect( wx, y-1, wx+width-1, y+h );
2961                                 gr_string( wx+5, y, items[i] );
2962                         }
2963
2964                         newmenu_show_cursor();
2965                         vid_update();
2966                 }
2967         }
2968         newmenu_hide_cursor();
2969
2970         keyd_repeat = old_keyd_repeat;
2971
2972         gr_bm_bitblt( total_width, total_height, wx-border_size, wy-title_height-border_size, 0, 0, bg.background, &grd_curcanv->cv_bitmap );
2973
2974         if ( bg.background != &VR_offscreen_buffer->cv_bitmap )
2975                 gr_free_bitmap(bg.background);
2976
2977 #if 0
2978         gr_bm_bitblt( grd_curcanv->cv_w, grd_curcanv->cv_h, 0, 0, 0, 0, &(VR_offscreen_buffer->cv_bitmap), &(grd_curcanv->cv_bitmap) );
2979 #endif
2980
2981         return citem;
2982 }
2983
2984
2985 #if 0
2986 int newmenu_filelist( char *title, char *filespec, char *filename )
2987 {
2988         int i, NumFiles;
2989         char *Filenames[MAX_FILES];
2990         char FilenameText[MAX_FILES][14];
2991         FILEFINDSTRUCT find;
2992
2993         NumFiles = 0;
2994         if ( !FileFindFirst( filespec, &find ) ) {
2995                 do {
2996                         if (NumFiles<MAX_FILES) {
2997                                 strncpy( FilenameText[NumFiles], find.name, FILENAME_LEN );
2998                                 Filenames[NumFiles] = FilenameText[NumFiles];
2999                                 NumFiles++;
3000                         } else
3001                                 break;
3002                 } while( !FileFindNext( &find ) );
3003                 FileFindClose();
3004         }
3005
3006         i = newmenu_listbox( title, NumFiles, Filenames, 1, NULL );
3007         if ( i > -1 ) {
3008                 strcpy( filename, Filenames[i] );
3009                 return 1;
3010         }
3011         return 0;
3012 }
3013 #endif
3014
3015
3016 // added on 10/14/98 by Victor Rachels to attempt a fixedwidth font messagebox
3017 int nm_messagebox_fixedfont( char *title, int nchoices, ... )
3018 {
3019         int i;
3020         char * format;
3021         va_list args;
3022         char *s;
3023         char nm_text[MESSAGEBOX_TEXT_SIZE];
3024         newmenu_item nm_message_items[5];
3025
3026         va_start( args, nchoices );
3027
3028         Assert( nchoices <= 5 );
3029
3030         for ( i = 0; i < nchoices; i++ ) {
3031                 s = va_arg( args, char * );
3032                 nm_message_items[i].type = NM_TYPE_MENU; nm_message_items[i].text = s;
3033         }
3034         format = va_arg( args, char * );
3035         //sprintf( nm_text, "" ); // adb: ?
3036         vsprintf(nm_text, format, args);
3037         va_end(args);
3038
3039         Assert( strlen(nm_text) < MESSAGEBOX_TEXT_SIZE );
3040
3041         return newmenu_do_fixedfont( title, nm_text, nchoices, nm_message_items, NULL, 0, NULL, -1, -1 );
3042 }
3043
3044
3045 #ifdef NETWORK
3046 extern netgame_info Active_games[];
3047 extern int NumActiveNetgames;
3048
3049
3050 void show_extra_netgame_info(int choice)
3051 {
3052         newmenu_item m[5];
3053         char mtext[5][50];
3054         int i, num=0;
3055
3056         if (choice >= NumActiveNetgames)
3057                 return;
3058
3059         for (i = 0; i < 5; i++) {
3060                 m[i].text = (char *)&mtext[i];
3061                 m[i].type = NM_TYPE_TEXT;
3062         }
3063
3064         sprintf(mtext[num], "Game: %s", Active_games[choice].game_name); num++;
3065         sprintf(mtext[num], "Mission: %s", Active_games[choice].mission_title); num++;
3066         sprintf(mtext[num], "Current Level: %d", Active_games[choice].levelnum); num++;
3067         sprintf(mtext[num], "Difficulty: %s", MENU_DIFFICULTY_TEXT(Active_games[choice].difficulty)); num++;
3068
3069         already_showing_info = 1;
3070         newmenu_dotiny2( NULL, "Netgame Information", num, m, NULL );
3071         already_showing_info = 0;
3072 }
3073 #endif // NETWORK
3074
3075
3076 /* Spiffy word wrap string formatting function */
3077 void nm_wrap_text(char *dbuf, char *sbuf, int line_length)
3078 {
3079         int col;
3080         char *wordptr;
3081         char *tbuf;
3082
3083         tbuf = (char *)d_malloc((unsigned int)strlen(sbuf) + 1);
3084         strcpy(tbuf, sbuf);
3085
3086         wordptr = strtok(tbuf, " ");
3087         if (!wordptr) return;
3088         col = 0;
3089         dbuf[0] = 0;
3090
3091         while (wordptr) {
3092                 col = col + (int)strlen(wordptr) + 1;
3093                 if (col >=line_length) {
3094                         col = 0;
3095                         strncat(dbuf, "\n", sizeof(dbuf)-strlen(dbuf)-1);
3096                 }
3097                 strncat(dbuf, wordptr, sizeof(dbuf)-strlen(dbuf)-1);
3098                 wordptr = strtok(NULL, " ");
3099         }
3100
3101         d_free(tbuf);
3102 }