OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / win / gnome / gnbind.c
1 /*      SCCS Id: @(#)gnbind.c   3.4     2000/07/16      */
2 /* Copyright (C) 1998 by Erik Andersen <andersee@debian.org> */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /*
6  * This file implements the interface between the window port specific
7  * code in the Gnome port and the rest of the nethack game engine. 
8 */
9
10 #include "gnbind.h"
11 #include "gnmain.h"
12 #include "gnmenu.h"
13 #include "gnaskstr.h"
14 #include "gnyesno.h"
15
16 GNHWinData gnome_windowlist[MAXWINDOWS];
17 winid WIN_WORN = WIN_ERR;
18
19 extern void tty_raw_print(const char *);
20 extern void tty_raw_print_bold(const char *);
21
22
23 /* Interface definition, for windows.c */
24 struct window_procs Gnome_procs = {
25     "Gnome",
26     WC_COLOR|WC_HILITE_PET|WC_INVERSE,
27     0L,
28     gnome_init_nhwindows,
29     gnome_player_selection,
30     gnome_askname,
31     gnome_get_nh_event,
32     gnome_exit_nhwindows,
33     gnome_suspend_nhwindows,
34     gnome_resume_nhwindows,
35     gnome_create_nhwindow,
36     gnome_clear_nhwindow,
37     gnome_display_nhwindow,
38     gnome_destroy_nhwindow,
39     gnome_curs,
40     gnome_putstr,
41     gnome_display_file,
42     gnome_start_menu,
43     gnome_add_menu,
44     gnome_end_menu,
45     gnome_select_menu,
46     genl_message_menu,          /* no need for X-specific handling */
47     gnome_update_inventory,
48     gnome_mark_synch,
49     gnome_wait_synch,
50 #ifdef CLIPPING
51     gnome_cliparound,
52 #endif
53 #ifdef POSITIONBAR
54     donull,
55 #endif
56     gnome_print_glyph,
57     gnome_raw_print,
58     gnome_raw_print_bold,
59     gnome_nhgetch,
60     gnome_nh_poskey,
61     gnome_nhbell,
62     gnome_doprev_message,
63     gnome_yn_function,
64     gnome_getlin,
65     gnome_get_ext_cmd,
66     gnome_number_pad,
67     gnome_delay_output,
68 #ifdef CHANGE_COLOR     /* only a Mac option currently */
69     donull,
70     donull,
71 #endif
72     /* other defs that really should go away (they're tty specific) */
73     gnome_start_screen,
74     gnome_end_screen,
75     gnome_outrip,
76     genl_preference_update,
77 };
78
79 /*  
80 init_nhwindows(int* argcp, char** argv)
81                 -- Initialize the windows used by NetHack.  This can also
82                    create the standard windows listed at the top, but does
83                    not display them.
84                 -- Any commandline arguments relevant to the windowport
85                    should be interpreted, and *argcp and *argv should
86                    be changed to remove those arguments.
87                 -- When the message window is created, the variable
88                    iflags.window_inited needs to be set to TRUE.  Otherwise
89                    all plines() will be done via raw_print().
90                 ** Why not have init_nhwindows() create all of the "standard"
91                 ** windows?  Or at least all but WIN_INFO?      -dean
92 */
93 void gnome_init_nhwindows(int* argc, char** argv)
94 {
95     /* Main window */
96     ghack_init_main_window( *argc, argv);
97     ghack_init_signals( );
98
99 #ifdef HACKDIR
100     //if (ghack_init_glyphs(HACKDIR "/t32-1024.xpm"))
101     if (ghack_init_glyphs(HACKDIR "/x11tiles"))
102       g_error ("ERROR:  Could not initialize glyphs.\n");
103 #else
104 #   error HACKDIR is not defined!
105 #endif
106   
107     // gnome/gtk is not reentrant
108     set_option_mod_status("ignintr", DISP_IN_GAME);
109     flags.ignintr = TRUE;
110
111     iflags.window_inited = TRUE;
112
113     /* gnome-specific window creation */
114     WIN_WORN = gnome_create_nhwindow(NHW_WORN);
115 }
116
117
118 /* Do a window-port specific player type selection. If player_selection()
119    offers a Quit option, it is its responsibility to clean up and terminate
120    the process. You need to fill in pl_character[0].
121 */
122 void
123 gnome_player_selection()
124 {
125     int n, i, sel;
126     const char** choices;
127     int* pickmap;
128
129     /* prevent an unnecessary prompt */
130     rigid_role_checks();
131
132     if (!flags.randomall && flags.initrole < 0) {
133
134         /* select a role */
135         for (n = 0; roles[n].name.m; n++) continue;
136         choices = (const char **)alloc(sizeof(char *) * (n+1));
137         pickmap = (int*)alloc(sizeof(int) * (n+1));
138         for (;;) {
139             for (n = 0, i = 0; roles[i].name.m; i++) {
140                 if (ok_role(i, flags.initrace,
141                             flags.initgend, flags.initalign)) {
142                     if (flags.initgend >= 0 && flags.female && roles[i].name.f)
143                         choices[n] = roles[i].name.f;
144                     else
145                         choices[n] = roles[i].name.m;
146                     pickmap[n++] = i;
147                 }
148             }
149             if (n > 0) break;
150             else if (flags.initalign >= 0) flags.initalign = -1;    /* reset */
151             else if (flags.initgend >= 0) flags.initgend = -1;
152             else if (flags.initrace >= 0) flags.initrace = -1;
153             else panic("no available ROLE+race+gender+alignment combinations");
154         }
155         choices[n] = (const char *) 0;
156         if (n > 1)
157             sel = ghack_player_sel_dialog(choices,
158                 _("Player selection"), _("Choose one of the following roles:"));
159         else sel = 0;
160         if (sel >= 0) sel = pickmap[sel];
161         else if (sel == ROLE_NONE) {            /* Quit */
162             clearlocks();
163             gnome_exit_nhwindows(0);
164         }
165         free(choices);
166         free(pickmap);
167     } else if (flags.initrole < 0) sel = ROLE_RANDOM;
168     else sel = flags.initrole;
169   
170     if (sel == ROLE_RANDOM) {   /* Random role */
171         sel = pick_role(flags.initrace, flags.initgend,
172                           flags.initalign, PICK_RANDOM);
173         if (sel < 0) sel = randrole();
174     }
175
176     flags.initrole = sel;
177
178     /* Select a race, if necessary */
179     /* force compatibility with role, try for compatibility with
180      * pre-selected gender/alignment */
181     if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
182         if (flags.initrace == ROLE_RANDOM || flags.randomall) {
183             flags.initrace = pick_race(flags.initrole, flags.initgend,
184                                        flags.initalign, PICK_RANDOM);
185             if (flags.initrace < 0) flags.initrace = randrace(flags.initrole);
186         } else {
187             /* Count the number of valid races */
188             n = 0;      /* number valid */
189             for (i = 0; races[i].noun; i++) {
190                 if (ok_race(flags.initrole, i, flags.initgend, flags.initalign))
191                     n++;
192             }
193             if (n == 0) {
194                 for (i = 0; races[i].noun; i++) {
195                     if (validrace(flags.initrole, i)) n++;
196                 }
197             }
198
199             choices = (const char **)alloc(sizeof(char *) * (n+1));
200             pickmap = (int*)alloc(sizeof(int) * (n + 1));
201             for (n = 0, i = 0; races[i].noun; i++) {
202                 if (ok_race(flags.initrole, i, flags.initgend,
203                             flags.initalign)) {
204                     choices[n] = races[i].noun;
205                     pickmap[n++] = i;
206                 }
207             }
208             choices[n] = (const char *) 0;
209             /* Permit the user to pick, if there is more than one */
210             if (n > 1)
211                 sel = ghack_player_sel_dialog(choices, _("Race selection"),
212                         _("Choose one of the following races:"));
213             else sel = 0;
214             if (sel >= 0) sel = pickmap[sel];
215             else if (sel == ROLE_NONE) { /* Quit */
216                 clearlocks();
217                 gnome_exit_nhwindows(0);
218             }
219             flags.initrace = sel;
220             free(choices);
221             free(pickmap);
222         }
223         if (flags.initrace == ROLE_RANDOM) {    /* Random role */
224             sel = pick_race(flags.initrole, flags.initgend,
225                             flags.initalign, PICK_RANDOM);
226             if (sel < 0) sel = randrace(flags.initrole);
227             flags.initrace = sel;
228         }
229     }
230
231     /* Select a gender, if necessary */
232     /* force compatibility with role/race, try for compatibility with
233      * pre-selected alignment */
234     if (flags.initgend < 0 ||
235         !validgend(flags.initrole, flags.initrace, flags.initgend)) {
236         if (flags.initgend == ROLE_RANDOM || flags.randomall) {
237             flags.initgend = pick_gend(flags.initrole, flags.initrace,
238                                        flags.initalign, PICK_RANDOM);
239             if (flags.initgend < 0)
240                 flags.initgend = randgend(flags.initrole, flags.initrace);
241         } else {
242             /* Count the number of valid genders */
243             n = 0;      /* number valid */
244             for (i = 0; i < ROLE_GENDERS; i++) {
245                 if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign))
246                     n++;
247             }
248             if (n == 0) {
249                 for (i = 0; i < ROLE_GENDERS; i++) {
250                     if (validgend(flags.initrole, flags.initrace, i)) n++;
251                 }
252             }
253
254             choices = (const char **)alloc(sizeof(char *) * (n+1));
255             pickmap = (int*)alloc(sizeof(int) * (n + 1));
256             for (n = 0, i = 0; i < ROLE_GENDERS; i++) {
257                 if (ok_gend(flags.initrole, flags.initrace, i,
258                                 flags.initalign)) {
259                     choices[n] = genders[i].adj;
260                     pickmap[n++] = i;
261                 }
262             }
263             choices[n] = (const char *) 0;
264             /* Permit the user to pick, if there is more than one */
265             if (n > 1)
266                 sel = ghack_player_sel_dialog(choices, _("Gender selection"),
267                         _("Choose one of the following genders:"));
268             else sel = 0;
269             if (sel >= 0) sel = pickmap[sel];
270             else if (sel == ROLE_NONE) { /* Quit */
271                 clearlocks();
272                 gnome_exit_nhwindows(0);
273             }
274             flags.initgend = sel;
275             free(choices);
276             free(pickmap);
277         }
278         if (flags.initgend == ROLE_RANDOM) {    /* Random gender */
279             sel = pick_gend(flags.initrole, flags.initrace,
280                             flags.initalign, PICK_RANDOM);
281             if (sel < 0) sel = randgend(flags.initrole, flags.initrace);
282             flags.initgend = sel;
283         }
284     }
285
286     /* Select an alignment, if necessary */
287     /* force compatibility with role/race/gender */
288     if (flags.initalign < 0 ||
289         !validalign(flags.initrole, flags.initrace, flags.initalign)) {
290         if (flags.initalign == ROLE_RANDOM || flags.randomall) {
291             flags.initalign = pick_align(flags.initrole, flags.initrace,
292                                          flags.initgend, PICK_RANDOM);
293             if (flags.initalign < 0)
294                 flags.initalign = randalign(flags.initrole, flags.initrace);
295         } else {
296             /* Count the number of valid alignments */
297             n = 0;      /* number valid */
298             for (i = 0; i < ROLE_ALIGNS; i++) {
299                 if (ok_align(flags.initrole, flags.initrace, flags.initgend, i))
300                     n++;
301             }
302             if (n == 0) {
303                 for (i = 0; i < ROLE_ALIGNS; i++)
304                     if (validalign(flags.initrole, flags.initrace, i)) n++;
305             }
306
307             choices = (const char **)alloc(sizeof(char *) * (n+1));
308             pickmap = (int*)alloc(sizeof(int) * (n + 1));
309             for (n = 0, i = 0; i < ROLE_ALIGNS; i++) {
310                 if (ok_align(flags.initrole,
311                              flags.initrace, flags.initgend, i)) {
312                     choices[n] = aligns[i].adj;
313                     pickmap[n++] = i;
314                 }
315             }
316             choices[n] = (const char *) 0;
317             /* Permit the user to pick, if there is more than one */
318             if (n > 1)
319                 sel = ghack_player_sel_dialog(choices, _("Alignment selection"),
320                         _("Choose one of the following alignments:"));
321             else sel = 0;
322             if (sel >= 0) sel = pickmap[sel];
323             else if (sel == ROLE_NONE) { /* Quit */
324                 clearlocks();
325                 gnome_exit_nhwindows(0);
326             }
327             flags.initalign = sel;
328             free(choices);
329             free(pickmap);
330         }
331         if (flags.initalign == ROLE_RANDOM) {
332             sel = pick_align(flags.initrole, flags.initrace,
333                              flags.initgend, PICK_RANDOM);
334             if (sel < 0) sel = randalign(flags.initrole, flags.initrace);
335             flags.initalign = sel;
336         }
337     }
338 }
339
340
341 /* Ask the user for a player name. */
342 void gnome_askname()
343 {
344     int ret;
345
346     g_message("Asking name....");
347
348     /* Ask for a name and stuff the response into plname, a nethack global */
349     ret = ghack_ask_string_dialog("What is your name?", "gandalf", 
350             "GnomeHack", plname);
351
352     /* Quit if they want to quit... */
353     if (ret==-1)
354       {
355         gnome_exit_nhwindows(0);
356       }
357 }
358
359
360 /* Does window event processing (e.g. exposure events).
361    A noop for the tty and X window-ports.
362 */
363 void gnome_get_nh_event()
364 {
365         /* We handle our own events. */
366         return;
367 }
368
369 /* Exits the window system.  This should dismiss all windows,
370    except the "window" used for raw_print().  str is printed if possible.
371 */
372 void gnome_exit_nhwindows(const char *str)
373 {
374         gtk_exit (0);
375         terminate(EXIT_SUCCESS);
376 }
377
378 /* Prepare the window to be suspended. */
379 void gnome_suspend_nhwindows(const char *str)
380 {
381         /* I don't think we need to do anything here... */
382         return;
383 }
384
385
386 /* Restore the windows after being suspended. */
387 void gnome_resume_nhwindows()
388 {
389         /* Do Nothing.  Un-necessary since the GUI will refresh itself. */
390         return;
391 }
392
393 /*  Create a window of type "type" which can be 
394         NHW_MESSAGE     (top line)
395         NHW_STATUS      (bottom lines)
396         NHW_MAP         (main dungeon)
397         NHW_MENU        (inventory or other "corner" windows)
398         NHW_TEXT        (help/text, full screen paged window)
399 */
400 winid 
401 gnome_create_nhwindow(int type)
402 {
403
404   winid i = 0;
405
406 /* Return the next available winid
407  */
408
409   for (i=0; i<MAXWINDOWS; i++)
410       if (gnome_windowlist[i].win == NULL)
411           break;
412   if (i == MAXWINDOWS)
413       g_error ("ERROR:  No windows available...\n");
414   gnome_create_nhwindow_by_id( type, i);
415   return i;
416 }
417
418 void
419 gnome_create_nhwindow_by_id( int type, winid i)
420 {
421     switch (type)
422       {
423       case NHW_MAP:
424         {
425           gnome_windowlist[i].win = ghack_init_map_window( );
426           gnome_windowlist[i].type = NHW_MAP;
427           ghack_main_window_add_map_window( gnome_windowlist[i].win);
428           break;
429         }
430       case NHW_MESSAGE:
431         {
432           gnome_windowlist[i].win = ghack_init_message_window( );
433           gnome_windowlist[i].type = NHW_MESSAGE;
434           ghack_main_window_add_message_window( gnome_windowlist[i].win);
435           break; 
436         }
437       case NHW_STATUS:
438         {
439           gnome_windowlist[i].win = ghack_init_status_window( );
440           gnome_windowlist[i].type = NHW_STATUS;
441           ghack_main_window_add_status_window( gnome_windowlist[i].win);
442           break;
443         }    
444       case NHW_WORN:
445         {
446           gnome_windowlist[i].win = ghack_init_worn_window( );
447           gnome_windowlist[i].type = NHW_WORN;
448           ghack_main_window_add_worn_window(gnome_windowlist[i].win);
449           break;
450         }
451       case NHW_MENU:
452         {
453           gnome_windowlist[i].type = NHW_MENU;
454           gnome_windowlist[i].win = ghack_init_menu_window( );
455           break;
456         } 
457       case NHW_TEXT:
458         {
459           gnome_windowlist[i].win = ghack_init_text_window( );
460           gnome_windowlist[i].type = NHW_TEXT;
461           break;
462         }
463       }
464 }
465
466 /* This widget is being destroyed before its time--
467  * clear its entry from the windowlist.
468 */
469 void gnome_delete_nhwindow_by_reference( GtkWidget *menuWin)
470 {
471   int i;
472
473   for (i = 0; i < MAXWINDOWS; i++) {
474     if (gnome_windowlist[i].win == menuWin) {
475       gnome_windowlist[i].win = NULL;
476       gnome_windowlist[i].type = 0;
477       break;
478     }
479   }
480 }
481
482 /* Clear the given window, when asked to. */
483 void gnome_clear_nhwindow(winid wid)
484 {
485   if (gnome_windowlist[wid].win != NULL)
486     {
487       gtk_signal_emit (GTK_OBJECT (gnome_windowlist[wid].win),
488                        ghack_signals[GHSIG_CLEAR]);
489     }
490 }
491
492 /* -- Display the window on the screen.  If there is data
493                    pending for output in that window, it should be sent.
494                    If blocking is TRUE, display_nhwindow() will not
495                    return until the data has been displayed on the screen,
496                    and acknowledged by the user where appropriate.
497                 -- All calls are blocking in the tty window-port.
498                 -- Calling display_nhwindow(WIN_MESSAGE,???) will do a
499                    --more--, if necessary, in the tty window-port.
500 */
501 void gnome_display_nhwindow(winid wid, BOOLEAN_P block)
502 {
503   if (gnome_windowlist[wid].win != NULL)
504     {
505       gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
506                        ghack_signals[GHSIG_DISPLAY],
507                        block);
508       if (block && (gnome_windowlist[wid].type == NHW_MAP))
509         (void) gnome_nhgetch();
510     }
511 }
512
513
514 /* Destroy will dismiss the window if the window has not 
515  * already been dismissed.
516 */
517 void gnome_destroy_nhwindow(winid wid)
518 {
519     if ((wid == WIN_MAP) || 
520         (wid == WIN_MESSAGE) || 
521         (wid == WIN_STATUS)) {
522         /* no thanks, I'll do these myself */
523         return;
524     }
525     if (wid != -1 && gnome_windowlist[wid].win != NULL)
526       {
527         gtk_widget_destroy(gnome_windowlist[wid].win);
528         gnome_windowlist[wid].win = NULL;
529         gnome_windowlist[wid].type = 0;
530       }
531 }
532
533 /* Next output to window will start at (x,y), also moves
534  displayable cursor to (x,y).  For backward compatibility,
535  1 <= x < cols, 0 <= y < rows, where cols and rows are
536  the size of window.
537 */
538 void gnome_curs(winid wid, int x, int y)
539 {
540   if (wid != -1 && gnome_windowlist[wid].win != NULL)
541     {
542       gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win), 
543                        ghack_signals[GHSIG_CURS], x, y);
544     }
545 }
546
547 /*
548 putstr(window, attr, str)
549                 -- Print str on the window with the given attribute.  Only
550                    printable ASCII characters (040-0126) must be supported.
551                    Multiple putstr()s are output on separate lines.
552 Attributes
553                    can be one of
554                         ATR_NONE (or 0)
555                         ATR_ULINE
556                         ATR_BOLD
557                         ATR_BLINK
558                         ATR_INVERSE
559                    If a window-port does not support all of these, it may map
560                    unsupported attributes to a supported one (e.g. map them
561                    all to ATR_INVERSE).  putstr() may compress spaces out of
562                    str, break str, or truncate str, if necessary for the
563                    display.  Where putstr() breaks a line, it has to clear
564                    to end-of-line.
565                 -- putstr should be implemented such that if two putstr()s
566                    are done consecutively the user will see the first and
567                    then the second.  In the tty port, pline() achieves this
568                    by calling more() or displaying both on the same line.
569 */
570 void gnome_putstr(winid wid, int attr, const char *text)
571 {
572     if ((wid >= 0) && 
573         (wid < MAXWINDOWS) &&
574         (gnome_windowlist[wid].win != NULL))
575     {
576       gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
577                        ghack_signals[GHSIG_PUTSTR],
578                        (guint) attr,
579                        text);
580     }
581 }
582
583 /* Display the file named str.  Complain about missing files
584                    iff complain is TRUE.
585 */
586 void gnome_display_file(const char *filename,BOOLEAN_P must_exist)
587 {
588         /* Strange -- for some reason it makes us create a new text window
589          * instead of reusing any existing ones -- perhaps we can work out
590          * some way to reuse stuff -- but for now just make and destroy new
591          * ones each time */
592         
593         dlb *f;
594        
595         f = dlb_fopen(filename, "r");
596         if (!f) {
597           if (must_exist) {
598             GtkWidget *box;
599             char message[90];
600             sprintf(message, "Warning! Could not find file: %s\n",filename);
601
602             box = gnome_message_box_new (_(message),
603                     GNOME_MESSAGE_BOX_ERROR,
604                     GNOME_STOCK_BUTTON_OK,
605                     NULL);
606             gnome_dialog_set_default( GNOME_DIALOG(box), 0);
607             gnome_dialog_set_parent (GNOME_DIALOG (box), 
608                     GTK_WINDOW (ghack_get_main_window ()) );
609             gtk_window_set_modal( GTK_WINDOW(box), TRUE);
610             gtk_widget_show (box);
611           }
612         }
613         else {
614           GtkWidget *txtwin, *gless, *frametxt;
615 #define LLEN 128
616           char line[LLEN], *textlines;
617           int num_lines, charcount;
618
619           txtwin = gnome_dialog_new("Text Window", GNOME_STOCK_BUTTON_OK,
620                                     NULL);
621           gtk_widget_set_usize(GTK_WIDGET(txtwin), 500, 400);
622           gtk_window_set_policy(GTK_WINDOW(txtwin), TRUE, TRUE, FALSE);
623           gtk_window_set_title(GTK_WINDOW(txtwin), "Text Window");
624           gnome_dialog_set_default( GNOME_DIALOG(txtwin), 0);
625           gtk_window_set_modal( GTK_WINDOW(txtwin), TRUE);
626           frametxt = gtk_frame_new ("");
627           gtk_widget_show (frametxt);
628
629           /*
630            * Count the number of lines and characters in the file.
631            */
632           num_lines = 0;
633           charcount = 1;
634           while (dlb_fgets(line, LLEN, f)) {
635             num_lines++;
636             charcount += strlen(line);
637           }
638           (void) dlb_fclose(f);
639           
640           /* Ignore empty files */
641           if (num_lines == 0) return;
642
643           /*
644            * Re-open the file and read the data into a buffer.  
645            */
646           textlines = (char *) alloc((unsigned int) charcount);
647           textlines[0] = '\0';
648           f = dlb_fopen( filename, RDTMODE);
649
650           while (dlb_fgets(line, LLEN, f)) {
651             (void) strcat(textlines, line);
652           }
653           (void) dlb_fclose(f);
654
655           gless = gnome_less_new ();
656           gnome_less_show_string (GNOME_LESS (gless), textlines);
657           gtk_container_add (GTK_CONTAINER (frametxt), gless);
658           gtk_box_pack_start(GTK_BOX (GNOME_DIALOG (txtwin)->vbox), frametxt,
659                              TRUE, TRUE, 0);
660           gtk_widget_show_all( txtwin);
661           gtk_window_set_modal( GTK_WINDOW(txtwin), TRUE);
662           gnome_dialog_set_parent (GNOME_DIALOG (txtwin), 
663                   GTK_WINDOW (ghack_get_main_window ()) );
664           gnome_dialog_run_and_close (GNOME_DIALOG (txtwin));
665           free(textlines);
666         }
667 }
668
669 /* Start using window as a menu.  You must call start_menu()
670    before add_menu().  After calling start_menu() you may not
671    putstr() to the window.  Only windows of type NHW_MENU may
672    be used for menus.
673 */
674 void gnome_start_menu(winid wid)
675 {
676   if (wid != -1)
677     {
678       if (gnome_windowlist[wid].win == NULL && gnome_windowlist[wid].type != 0)
679         {
680           gnome_create_nhwindow_by_id(gnome_windowlist[wid].type, wid);
681         }
682         gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
683                        ghack_signals[GHSIG_START_MENU]);
684     }
685 }
686
687 /*
688 add_menu(windid window, int glyph, const anything identifier,
689                                 char accelerator, char groupacc,
690                                 int attr, char *str, boolean preselected)
691                 -- Add a text line str to the given menu window.  If identifier
692                    is 0, then the line cannot be selected (e.g. a title).
693                    Otherwise, identifier is the value returned if the line is
694                    selected.  Accelerator is a keyboard key that can be used
695                    to select the line.  If the accelerator of a selectable
696                    item is 0, the window system is free to select its own
697                    accelerator.  It is up to the window-port to make the
698                    accelerator visible to the user (e.g. put "a - " in front
699                    of str).  The value attr is the same as in putstr().
700                    Glyph is an optional glyph to accompany the line.  If
701                    window port cannot or does not want to display it, this
702                    is OK.  If there is no glyph applicable, then this
703                    value will be NO_GLYPH.
704                 -- All accelerators should be in the range [A-Za-z].
705                 -- It is expected that callers do not mix accelerator
706                    choices.  Either all selectable items have an accelerator
707                    or let the window system pick them.  Don't do both.
708                 -- Groupacc is a group accelerator.  It may be any character
709                    outside of the standard accelerator (see above) or a
710                    number.  If 0, the item is unaffected by any group
711                    accelerator.  If this accelerator conflicts with
712                    the menu command (or their user defined alises), it loses.
713                    The menu commands and aliases take care not to interfere
714                    with the default object class symbols.
715                 -- If you want this choice to be preselected when the
716                    menu is displayed, set preselected to TRUE.
717 */
718 void gnome_add_menu(winid wid, int glyph, const ANY_P * identifier,
719                 CHAR_P accelerator, CHAR_P group_accel, int attr, 
720                 const char *str, BOOLEAN_P presel)
721 {
722   GHackMenuItem item;
723   item.glyph =  glyph;
724   item.identifier = identifier;
725   item.accelerator = accelerator;
726   item.group_accel = group_accel;
727   item.attr = attr;
728   item.str = str;
729   item.presel = presel;
730
731   if (wid != -1 && gnome_windowlist[wid].win != NULL)
732     {
733       gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
734                        ghack_signals[GHSIG_ADD_MENU],
735                        &item);
736     }
737 }
738
739 /*
740 end_menu(window, prompt)
741                 -- Stop adding entries to the menu and flushes the window
742                    to the screen (brings to front?).  Prompt is a prompt
743                    to give the user.  If prompt is NULL, no prompt will
744                    be printed.
745                 ** This probably shouldn't flush the window any more (if
746                 ** it ever did).  That should be select_menu's job.  -dean
747 */
748 void gnome_end_menu(winid wid, const char *prompt)
749 {
750     if (wid != -1 && gnome_windowlist[wid].win != NULL)
751       {
752         gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
753                          ghack_signals[GHSIG_END_MENU],
754                          prompt);
755       }
756 }
757
758 /*
759 int select_menu(windid window, int how, menu_item **selected)
760                 -- Return the number of items selected; 0 if none were chosen,
761                    -1 when explicitly cancelled.  If items were selected, then
762                    selected is filled in with an allocated array of menu_item
763                    structures, one for each selected line.  The caller must
764                    free this array when done with it.  The "count" field
765                    of selected is a user supplied count.  If the user did
766                    not supply a count, then the count field is filled with
767                    -1 (meaning all).  A count of zero is equivalent to not
768                    being selected and should not be in the list.  If no items
769                    were selected, then selected is NULL'ed out.  How is the
770                    mode of the menu.  Three valid values are PICK_NONE,
771                    PICK_ONE, and PICK_N, meaning: nothing is selectable,
772                    only one thing is selectable, and any number valid items
773                    may selected.  If how is PICK_NONE, this function should
774                    never return anything but 0 or -1.
775                 -- You may call select_menu() on a window multiple times --
776                    the menu is saved until start_menu() or destroy_nhwindow()
777                    is called on the window.
778                 -- Note that NHW_MENU windows need not have select_menu()
779                    called for them. There is no way of knowing whether
780                    select_menu() will be called for the window at
781                    create_nhwindow() time.
782 */
783 int gnome_select_menu(winid wid, int how, MENU_ITEM_P **selected)
784 {
785     int nReturned = -1;
786
787     if (wid != -1 && gnome_windowlist[wid].win != NULL &&
788         gnome_windowlist[wid].type == NHW_MENU)
789       {
790         nReturned=ghack_menu_window_select_menu (gnome_windowlist[wid].win,
791                                        selected, how);
792       }
793
794     return nReturned;
795 }
796
797 /*
798     -- Indicate to the window port that the inventory has been changed.
799     -- Merely calls display_inventory() for window-ports that leave the 
800         window up, otherwise empty.
801 */
802 void gnome_update_inventory()
803 {
804     ghack_main_window_update_inventory();
805 }
806
807 /*
808 mark_synch()    -- Don't go beyond this point in I/O on any channel until
809                    all channels are caught up to here.  Can be an empty call
810                    for the moment
811 */
812 void gnome_mark_synch()
813 {
814         /* Do nothing */
815 }
816
817 /*
818 wait_synch()    -- Wait until all pending output is complete (*flush*() for
819                    streams goes here).
820                 -- May also deal with exposure events etc. so that the
821                    display is OK when return from wait_synch().
822 */
823 void gnome_wait_synch()
824 {
825         /* Do nothing */
826 }
827
828 /*
829 cliparound(x, y)-- Make sure that the user is more-or-less centered on the
830                    screen if the playing area is larger than the screen.
831                 -- This function is only defined if CLIPPING is defined.
832 */
833 void gnome_cliparound(int x, int y)
834 {
835   /* FIXME!!!  winid should be a parameter!!!
836    * Call a function that Does The Right Thing(tm).
837   */
838     gnome_cliparound_proper(WIN_MAP,x,y);
839 }
840
841 void gnome_cliparound_proper(winid wid, int x, int y)
842 {
843     if (wid != -1 && gnome_windowlist[wid].win != NULL)
844       {
845         gtk_signal_emit( GTK_OBJECT (gnome_windowlist[wid].win),
846                          ghack_signals[GHSIG_CLIPAROUND],
847                          (guint) x, 
848                          (guint) y);
849       }
850 }
851
852 /*
853 print_glyph(window, x, y, glyph)
854                 -- Print the glyph at (x,y) on the given window.  Glyphs are
855                    integers at the interface, mapped to whatever the window-
856                    port wants (symbol, font, color, attributes, ...there's
857                    a 1-1 map between glyphs and distinct things on the map).
858 */
859 void gnome_print_glyph(winid wid,XCHAR_P x,XCHAR_P y,int glyph)
860 {
861     if (wid != -1 && gnome_windowlist[wid].win != NULL)
862       {
863         GdkImlibImage *im;
864
865         im = ghack_image_from_glyph( glyph, FALSE);
866
867         gtk_signal_emit (GTK_OBJECT (gnome_windowlist[wid].win),
868                          ghack_signals[GHSIG_PRINT_GLYPH],
869                          (guint) x,
870                          (guint) y,
871                          im,
872                          NULL);
873     }
874 }
875
876 /*
877 raw_print(str)  -- Print directly to a screen, or otherwise guarantee that
878                    the user sees str.  raw_print() appends a newline to str.
879                    It need not recognize ASCII control characters.  This is
880                    used during startup (before windowing system initialization
881                    -- maybe this means only error startup messages are raw),
882                    for error messages, and maybe other "msg" uses.  E.g.
883                    updating status for micros (i.e, "saving").
884 */
885 void gnome_raw_print(const char *str)
886 {
887     tty_raw_print(str);
888 }
889
890 /*
891 raw_print_bold(str)
892                 -- Like raw_print(), but prints in bold/standout (if
893 possible).
894 */
895 void gnome_raw_print_bold(const char *str)
896 {
897     tty_raw_print_bold(str);
898 }
899
900 /*
901 int nhgetch()   -- Returns a single character input from the user.
902                 -- In the tty window-port, nhgetch() assumes that tgetch()
903                    will be the routine the OS provides to read a character.
904                    Returned character _must_ be non-zero.
905 */
906 int gnome_nhgetch()
907 {
908     int key;
909     GList *theFirst;
910     gtk_signal_emit (GTK_OBJECT (gnome_windowlist[WIN_STATUS].win),
911                        ghack_signals[GHSIG_FADE_HIGHLIGHT]);
912
913     g_askingQuestion = 1;
914     /* Process events until a key press event arrives. */
915     while ( g_numKeys == 0 ) 
916         gtk_main_iteration();
917     
918     theFirst = g_list_first( g_keyBuffer);
919     g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst);
920     key = GPOINTER_TO_INT( theFirst->data);
921     g_list_free_1( theFirst);
922     g_numKeys--;
923     g_askingQuestion = 0;
924     return ( key);
925 }
926
927 /*
928 int nh_poskey(int *x, int *y, int *mod)
929                 -- Returns a single character input from the user or a
930                    a positioning event (perhaps from a mouse).  If the
931                    return value is non-zero, a character was typed, else,
932                    a position in the MAP window is returned in x, y and mod.
933                    mod may be one of
934
935                         CLICK_1         -- mouse click type 1 
936                         CLICK_2         -- mouse click type 2 
937
938                    The different click types can map to whatever the
939                    hardware supports.  If no mouse is supported, this
940                    routine always returns a non-zero character.
941 */
942 int gnome_nh_poskey(int *x, int *y, int *mod)
943 {
944     gtk_signal_emit (GTK_OBJECT (gnome_windowlist[WIN_STATUS].win),
945                        ghack_signals[GHSIG_FADE_HIGHLIGHT]);
946     
947     g_askingQuestion = 0;
948     /* Process events until a key or map-click arrives. */
949     while ( g_numKeys == 0 && g_numClicks == 0 )
950         gtk_main_iteration();
951     
952     if (g_numKeys > 0) {
953         int key;
954         GList *theFirst;
955         
956         theFirst = g_list_first( g_keyBuffer);
957         g_keyBuffer = g_list_remove_link(g_keyBuffer, theFirst);
958         key = GPOINTER_TO_INT( theFirst->data);
959         g_list_free_1( theFirst);
960         g_numKeys--;
961         return ( key);
962     }
963     else {
964         GHClick *click;
965         GList *theFirst;
966         
967         theFirst = g_list_first( g_clickBuffer);
968         g_clickBuffer = g_list_remove_link(g_clickBuffer, theFirst);
969         click = (GHClick*) theFirst->data;
970         *x=click->x;
971         *y=click->y;
972         *mod=click->mod;
973         g_free( click);
974         g_list_free_1( theFirst);
975         g_numClicks--;
976         return ( 0);
977     }
978 }
979
980 /*
981 nhbell()        -- Beep at user.  [This will exist at least until sounds are
982                    redone, since sounds aren't attributable to windows anyway.]
983 */
984 void gnome_nhbell()
985 {
986     /* FIXME!!! Play a cool GNOME sound instead */
987     gdk_beep();
988 }
989
990 /*
991 doprev_message()
992                 -- Display previous messages.  Used by the ^P command.
993                 -- On the tty-port this scrolls WIN_MESSAGE back one line.
994 */
995 int gnome_doprev_message()
996 {
997     /* Do Nothing.  They can read old messages using the scrollbar. */
998     return 0;
999 }
1000
1001 /*
1002 char yn_function(const char *ques, const char *choices, char default)
1003                 -- Print a prompt made up of ques, choices and default.
1004                    Read a single character response that is contained in
1005                    choices or default.  If choices is NULL, all possible
1006                    inputs are accepted and returned.  This overrides
1007                    everything else.  The choices are expected to be in
1008                    lower case.  Entering ESC always maps to 'q', or 'n',
1009                    in that order, if present in choices, otherwise it maps
1010                    to default.  Entering any other quit character (SPACE,
1011                    RETURN, NEWLINE) maps to default.
1012                 -- If the choices string contains ESC, then anything after
1013                    it is an acceptable response, but the ESC and whatever
1014                    follows is not included in the prompt.
1015                 -- If the choices string contains a '#' then accept a count.
1016                    Place this value in the global "yn_number" and return '#'.
1017                 -- This uses the top line in the tty window-port, other
1018                    ports might use a popup.
1019 */
1020 char gnome_yn_function(const char *question, const char *choices,
1021                 CHAR_P def)
1022 {
1023     int ch;
1024     int result=-1;
1025     char message[BUFSZ];
1026     char yn_esc_map='\033';
1027     GtkWidget *mainWnd = ghack_get_main_window();
1028     
1029     
1030     if (choices) {
1031         char *cb, choicebuf[QBUFSZ];
1032         Strcpy(choicebuf, choices);
1033         if ((cb = index(choicebuf, '\033')) != 0) {
1034             /* anything beyond <esc> is hidden */
1035             *cb = '\0';
1036         }
1037         sprintf(message, "%s [%s] ", question, choicebuf);
1038         if (def) sprintf(eos(message), "(%c) ", def);
1039         /* escape maps to 'q' or 'n' or default, in that order */
1040         yn_esc_map = (index(choices, 'q') ? 'q' :
1041                  (index(choices, 'n') ? 'n' : def));
1042     } else {
1043         Strcpy(message, question);
1044     }
1045     
1046     
1047     gnome_putstr(WIN_MESSAGE, ATR_BOLD, message);
1048     if (mainWnd != NULL && choices && !index(choices,ch)) {
1049         return(ghack_yes_no_dialog( question, choices, def));
1050     }
1051
1052     /* Only here if main window is not present */
1053     while (result<0) {
1054         ch=gnome_nhgetch();
1055         if (ch=='\033') {
1056             result=yn_esc_map;
1057         } else if (choices && !index(choices,ch)) {
1058             /* FYI: ch==-115 is for KP_ENTER */
1059             if (def && (ch==' ' || ch=='\r' || ch=='\n' || ch==-115)) {
1060                 result=def;
1061             } else {
1062                 gnome_nhbell();
1063                 /* and try again... */
1064             }
1065         } else {
1066             result=ch;
1067         }
1068     }
1069     return result;
1070 }
1071
1072 /*
1073 getlin(const char *ques, char *input)
1074             -- Prints ques as a prompt and reads a single line of text,
1075                up to a newline.  The string entered is returned without the
1076                newline.  ESC is used to cancel, in which case the string
1077                "\033\000" is returned.
1078             -- getlin() must call flush_screen(1) before doing anything.
1079             -- This uses the top line in the tty window-port, other
1080                ports might use a popup.
1081 */
1082 void gnome_getlin(const char *question, char *input)
1083 {
1084     int ret;
1085
1086     ret = ghack_ask_string_dialog(question, "", "nethack", input);
1087
1088     if (ret == -1)
1089         input[0] = 0;
1090 }
1091
1092 /*
1093 int get_ext_cmd(void)
1094             -- Get an extended command in a window-port specific way.
1095                An index into extcmdlist[] is returned on a successful
1096                selection, -1 otherwise.
1097 */
1098 int gnome_get_ext_cmd()
1099 {
1100     return ghack_menu_ext_cmd();
1101 }
1102
1103
1104 /*
1105 number_pad(state)
1106             -- Initialize the number pad to the given state.
1107 */
1108 void gnome_number_pad(int state)
1109 {
1110     /* Do Nothing */
1111 }
1112
1113 /*
1114 delay_output()  -- Causes a visible delay of 50ms in the output.
1115                Conceptually, this is similar to wait_synch() followed
1116                by a nap(50ms), but allows asynchronous operation.
1117 */
1118 void gnome_delay_output()
1119 {
1120     if (gnome_windowlist[WIN_MESSAGE].win != NULL) {
1121         gtk_signal_emit( GTK_OBJECT (gnome_windowlist[WIN_MESSAGE].win),
1122         ghack_signals[GHSIG_DELAY],
1123         (guint) 50);
1124     }
1125 }
1126
1127 /*
1128 start_screen()  -- Only used on Unix tty ports, but must be declared for
1129                completeness.  Sets up the tty to work in full-screen
1130                graphics mode.  Look at win/tty/termcap.c for an
1131                example.  If your window-port does not need this function
1132                just declare an empty function.
1133 */
1134 void gnome_start_screen()
1135 {
1136     /* Do Nothing */
1137 }
1138
1139 /*
1140 end_screen()    -- Only used on Unix tty ports, but must be declared for
1141                completeness.  The complement of start_screen().
1142 */
1143 void gnome_end_screen()
1144 {
1145     /* Do Nothing */
1146 }
1147
1148 /*
1149 outrip(winid, int)
1150             -- The tombstone code.  If you want the traditional code use
1151                genl_outrip for the value and check the #if in rip.c.
1152 */
1153 void gnome_outrip(winid wid, int how)
1154 {
1155     /* Follows roughly the same algorithm as genl_outrip() */
1156     char buf[BUFSZ];
1157     char ripString[BUFSZ]="\0";
1158     extern const char *killed_by_prefix[];
1159     
1160     /* Put name on stone */
1161     Sprintf(buf, "%s\n", plname);
1162     Strcat(ripString, buf);
1163     
1164     /* Put $ on stone */
1165     Sprintf(buf, "%ld Au\n",
1166 #ifndef GOLDOBJ
1167                 u.ugold);
1168 #else
1169                 done_money);
1170 #endif
1171     Strcat(ripString, buf);
1172
1173     /* Put together death description */
1174     switch (killer_format) {
1175             default: impossible("bad killer format?");
1176             case KILLED_BY_AN:
1177                     Strcpy(buf, killed_by_prefix[how]);
1178                     Strcat(buf, an(killer));
1179                     break;
1180             case KILLED_BY:
1181                     Strcpy(buf, killed_by_prefix[how]);
1182                     Strcat(buf, killer);
1183                     break;
1184             case NO_KILLER_PREFIX:
1185                     Strcpy(buf, killer);
1186                     break;
1187     }
1188     /* Put death type on stone */
1189     Strcat(ripString, buf);
1190     Strcat(ripString, "\n");
1191
1192     /* Put year on stone */
1193     Sprintf(buf, "%4d\n", getyear());
1194     Strcat(ripString, buf);
1195
1196     ghack_text_window_rip_string( ripString);
1197 }