OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / sys / mac / macmenu.c
1 /*      SCCS Id: @(#)macmenu.c  3.4     1999/11/24      */
2 /*      Copyright (c) Macintosh NetHack Port Team, 1993.          */
3 /* NetHack may be freely redistributed.  See license for details. */
4
5 /****************************************\
6  * Extended Macintosh menu support
7  *
8  * provides access to all keyboard commands from cmd.c
9  * provides control key functionality for classic keyboards
10  * provides key equivalent references and logical menu groups
11  * supports various menu highlighting modes
12 \****************************************/
13
14 /****************************************\
15  * Edit History:
16  *
17  * 930512       - More bug fixes and getting tty to work again, Jon W{tte
18  * 930508       - Bug fixes in-flight, Jon W{tte
19  * 04/29/93 - 1st Release Draft, David Hairston
20  * 04/11/93 - 1st Draft, David Hairston
21 \****************************************/
22
23 /******** Application Defines ********/
24 #include "hack.h"
25 #include "mactty.h"
26 #include "macwin.h"
27 #include "macpopup.h"
28 #include "patchlevel.h"
29
30 /******** Toolbox Defines ********/
31 #if !TARGET_API_MAC_CARBON
32 #include <Menus.h>
33 #include <Devices.h>
34 #include <Resources.h>
35 #include <TextUtils.h>
36 #include <ToolUtils.h>
37 #include <Sound.h>
38 #endif
39
40 /* Borrowed from the Mac tty port */
41 extern WindowPtr _mt_window;
42
43 /******** Local Defines ********/
44
45 /* 'MNU#' (menu list record) */
46 typedef union menuRefUnn
47 {
48         short           mresID;         /* MENU resource ID (before GetMenu) */
49         MenuHandle      mhnd;           /* MENU handle (after GetMenu) */
50 } menuRefUnn;
51
52 typedef struct menuListRec
53 {
54         short           firstMenuID;
55         short           numMenus;
56         menuRefUnn      mref[];
57 } menuListRec, *menuListPtr, **menuListHandle;
58
59 /* indices and resource IDs of the menu list data */
60 enum
61 {
62         listMenubar,
63         listSubmenu,
64
65         menuBarListID = 128,
66         subMenuListID
67 };
68
69 /* the following mref[] indices are reserved */
70 enum
71 {
72         /* menu bar */
73         menuApple,
74         menuFile,
75         menuEdit,
76
77         /* submenu */
78         menuWizard = 0
79 };
80
81 /* the following menu items are reserved */
82 enum
83 {
84         /* apple */
85         menuAppleAboutBox = 1,
86         ____Apple__1,
87
88         /* File */
89         menuFileRedraw = 1,
90         menuFilePrevMsg,
91         menuFileCleanup,
92         ____File___1,
93         menuFilePlayMode,
94         menuFileEnterExplore,
95         ____File___2,
96         menuFileSave,
97         ____File___3,
98         menuFileQuit,
99
100         /* standard minimum Edit menu items */
101
102         /* Wizard */
103         menuWizardAttributes = 1
104 };
105
106
107 /*
108  * menuListRec data (preloaded and locked) specifies the number of menus in
109  * the menu bar, the number of hierarchal or submenus and the menu IDs of
110  * all of those menus.  menus that go into in the menu bar are specified by
111  * 'MNU#' 128 and submenus are specified by 'MNU#' 129.  the fields of the
112  * menuListRec are:
113  * firstMenuID - the menu ID (not resource ID) of the 1st menu.  subsequent
114  *     menus in the list are _forced_ to have consecutively incremented IDs.
115  * numMenus - the total count of menus in a given list (and the extent of
116  *     valid menu IDs).
117  * mref[] - initially the MENU resource ID is stored in the placeholder for
118  *     the resource handle.  after loading (GetResource), the menu handle
119  *     is stored and the menu ID, in memory, is set as noted above.
120  *
121  * NOTE: a ResEdit template editor is supplied to edit the 'MNU#' resources.
122  *
123  * NOTE: the resource IDs do not need to match the menu IDs in a menu list
124  * record although they have been originally set that way.
125  *
126  * NOTE: the menu ID's of menus in the submenu list record may be reset, as
127  * noted above.  it is the programmers responsibility to make sure that
128  * submenu references/IDs are valid.
129  *
130  * WARNING: the existence of the submenu list record is assumed even if the
131  * number of submenus is zero.  also, no error checking is done on the
132  * extents of the menu IDs.  this must be correctly setup by the programmer.
133  */
134
135 #define ID1_MBAR        pMenuList[listMenubar]->firstMenuID
136 #define ID1_SUBM        pMenuList[listSubmenu]->firstMenuID
137
138 #define NUM_MBAR        pMenuList[listMenubar]->numMenus
139 #define NUM_SUBM        pMenuList[listSubmenu]->numMenus
140
141 #define MHND_APPLE      pMenuList[listMenubar]->mref[menuApple].mhnd
142 #define MHND_FILE       pMenuList[listMenubar]->mref[menuFile].mhnd
143 #define MHND_EDIT       pMenuList[listMenubar]->mref[menuEdit].mhnd
144
145 #define MBARHND(x)      pMenuList[listMenubar]->mref[(x)].mhnd
146
147 #define MHND_WIZ        pMenuList[listSubmenu]->mref[menuWizard].mhnd
148
149
150 /* mutually exclusive (and prioritized) menu bar states */
151 enum
152 {
153         mbarDim,
154         mbarNoWindows,
155         mbarDA,
156         mbarNoMap,
157         mbarRegular,
158         mbarSpecial                                     /* explore or debug mode */
159 };
160
161 #define WKND_MAP                (WIN_BASE_KIND + NHW_MAP)
162
163
164 /* menu routine error numbers */
165 enum
166 {
167         errGetMenuList,
168         errGetMenu,
169         errGetANDlogTemplate,
170         errGetANDlogItems,
171         errGetANDialog,
172         errANNewMenu,
173         err_Menu_total
174 };
175
176
177 /* menu 'STR#' comment char */
178 #define mstrEndChar             0xA5            /* '\245' or option-* or "bullet" */
179
180 /* 'ALRT' */
181 enum
182 {
183         alrt_Menu_start = 5000,
184         alrtMenuNote = alrt_Menu_start,
185         alrtMenu_NY,
186         alrt_Menu_limit
187 };
188
189 #define beepMenuAlertErr        1               /* # of SysBeep()'s before exitting */
190 enum
191 {
192         bttnMenuAlertNo = 1,
193         bttnMenuAlertYes
194 };
195
196
197 /******** Globals ********/
198 static  unsigned char *menuErrStr[err_Menu_total] = 
199         {
200                 "\pAbort: Bad \'MNU#\' resource!",              /* errGetMenuList */
201                 "\pAbort: Bad \'MENU\' resource!",              /* errGetMenu */
202                 "\pAbort: Bad \'DLOG\' resource!",              /* errGetANDlogTemplate */
203                 "\pAbort: Bad \'DITL\' resource!",              /* errGetANDlogItems */
204                 "\pAbort: Bad Dialog Allocation!",              /* errGetANDialog */
205                 "\pAbort: Bad Menu Allocation!",                /* errANNewMenu */
206         };
207 static  menuListPtr     pMenuList[2];
208 static  short           theMenubar = mbarDA;    /* force initial update */
209 static  short           kAdjustWizardMenu = 1;
210
211
212 /******** Prototypes ********/
213 #if !TARGET_API_MAC_CARBON
214 static  void alignAD(Rect *, short);
215 #endif
216 static  void mustGetMenuAlerts(void);
217 static  void menuError(short);
218 static  void aboutNetHack(void);
219 static  void askSave(void);
220 static  void askQuit(void);
221
222
223 /*** Askname dialog box ***/
224
225 #define RSRC_ASK                        6000    /* Askname dialog and item list */
226 #define RSRC_ASK_PLAY                   1       /*      Play button */
227 #define RSRC_ASK_QUIT                   2       /*      Quit button */
228 #define RSRC_ASK_DEFAULT                3       /*      Default ring */
229 #define RSRC_ASK_ROLE                   4       /*      Role popup menu */
230 #define RSRC_ASK_RACE                   5       /*      Race popup menu */
231 #define RSRC_ASK_GEND                   6       /*      Gender popup menu */
232 #define RSRC_ASK_ALIGN                  7       /*      Alignment popup menu */
233 #define RSRC_ASK_MODE                   8       /*      Mode popup menu */
234 #define RSRC_ASK_NAME                   9       /*      Name text field */
235 #define RSRC_ASK_MAX                    10      /*      Maximum enabled item */
236
237 #define KEY_MASK        0xff00
238 #define KEY_RETURN      0x2400
239 #define KEY_ENTER       0x4c00
240 #define KEY_ESCAPE      0x3500
241 #define CH_MASK         0x00ff
242 #define CH_RETURN       0x000d
243 #define CH_ENTER        0x0003
244 #define CH_ESCAPE       0x001b
245
246 static void ask_restring(const char *cstr, unsigned char *pstr);
247 static void ask_enable(DialogRef wind, short item, int enable);
248 static pascal void ask_redraw(DialogRef wind, DialogItemIndex item);
249 static pascal Boolean ask_filter(DialogRef wind, EventRecord *event, DialogItemIndex *item);
250 #define noresource(t,n) {SysBeep(3); ExitToShell();}
251 #define fatal(s)        {SysBeep(3); ExitToShell();}
252
253 static MenuHandle askmenu[RSRC_ASK_MAX];
254 static int askselect[RSRC_ASK_MAX];
255 #define currrole        askselect[RSRC_ASK_ROLE]
256 #define currrace        askselect[RSRC_ASK_RACE]
257 #define currgend        askselect[RSRC_ASK_GEND]
258 #define curralign       askselect[RSRC_ASK_ALIGN]
259 #define currmode        askselect[RSRC_ASK_MODE]
260
261 static RGBColor
262         blackcolor = {0x0000, 0x0000, 0x0000},
263 //      indentcolor = {0x4000, 0x4000, 0x4000},
264         darkcolor = {0x8000, 0x8000, 0x8000},
265         backcolor = {0xdddd, 0xdddd, 0xdddd},
266         lightcolor = {0xffff, 0xffff, 0xffff},
267         whitecolor = {0xffff, 0xffff, 0xffff};
268
269
270 /* Convert a mixed-case C string to a Capitalized Pascal string */
271 static void
272 ask_restring (const char *cstr, unsigned char *pstr)
273 {
274         int i;
275
276
277         for (i = 0; *cstr && (i < 255); i++)
278             pstr[i+1] = *cstr++;
279         pstr[0] = i;
280         if ((pstr[1] >= 'a') && (pstr[1] <= 'z'))
281             pstr[1] += 'A' - 'a';
282         return;
283 }
284
285
286 /* Enable the dialog item with the given index */
287 static void
288 ask_enable (DialogRef wind, short item, int enable)
289 {
290         short type;
291         Handle handle;
292         Rect rect;
293
294
295         /* Enable or disable the appropriate item */
296         GetDialogItem(wind, item, &type, &handle, &rect);
297         if (enable)     type &= ~itemDisable;
298         else            type |= itemDisable;
299         HiliteControl((ControlHandle)handle, enable ? 0 : 255);
300         SetDialogItem(wind, item, type, handle, &rect);
301         return;
302 }
303
304
305 static pascal void
306 ask_redraw (DialogRef wind, DialogItemIndex item)
307 {
308         short type;
309         Handle handle;
310         Rect rect;
311         static char     *modechar = "NED";
312
313
314         /* Which item shall we redraw? */
315         GetDialogItem(wind, item, &type, &handle, &rect);
316         switch (item) {
317                 case RSRC_ASK_DEFAULT:
318                         PenSize(3, 3);
319                         FrameRoundRect(&rect, 16, 16);
320                         break;
321
322                 case RSRC_ASK_ROLE:
323                 case RSRC_ASK_RACE:
324                 case RSRC_ASK_GEND:
325                 case RSRC_ASK_ALIGN:
326                 case RSRC_ASK_MODE:
327                         if (macFlags.color) {
328                                 RGBForeColor(&blackcolor);
329                                 RGBBackColor(&backcolor);
330                         }
331                         PenNormal();
332                         TextMode(srcOr);
333                         EraseRect(&rect);
334
335                         /* Draw the frame and drop shadow */
336                         rect.right--;
337                         rect.bottom--;
338                         FrameRect(&rect);
339                         MoveTo(rect.right, rect.top+1);
340                         LineTo(rect.right, rect.bottom);
341                         LineTo(rect.left+1, rect.bottom);
342
343                         /* Draw the menu character */
344                         MoveTo(rect.left+4, rect.top+12);
345                         switch (item) {
346                         case RSRC_ASK_ROLE:
347                                 DrawText(roles[askselect[item]].filecode, 0, 3);
348                                 break;
349                         case RSRC_ASK_RACE:
350                                 DrawText(races[askselect[item]].filecode, 0, 3);
351                                 break;
352                         case RSRC_ASK_GEND:
353                                 DrawText(genders[askselect[item]].filecode, 0, 3);
354                                 break;
355                         case RSRC_ASK_ALIGN:
356                                 DrawText(aligns[askselect[item]].filecode, 0, 3);
357                                 break;
358                         case RSRC_ASK_MODE:
359                                 DrawChar(modechar[askselect[item]]);
360                                 break;
361                         }
362
363                         /* Draw the popup symbol */
364                         MoveTo(rect.right - 16, rect.top + 5);
365                         LineTo(rect.right -  6, rect.top + 5);
366                         LineTo(rect.right - 11, rect.top + 10);
367                         LineTo(rect.right - 15, rect.top + 6);
368                         LineTo(rect.right -  8, rect.top + 6);
369                         LineTo(rect.right - 11, rect.top + 9);
370                         LineTo(rect.right - 13, rect.top + 7);
371                         LineTo(rect.right - 10, rect.top + 7);
372                         LineTo(rect.right - 11, rect.top + 8);
373
374                         /* Draw the shadow */
375                         InsetRect(&rect, 1, 1);
376                         if (macFlags.color) {
377                                 RGBColor color;
378
379
380                                 /* Save the foreground color */
381                                 GetForeColor(&color);
382
383                                 /* Draw the top and left */
384                                 RGBForeColor(&lightcolor);
385                                 MoveTo(rect.left, rect.bottom-1);
386                                 LineTo(rect.left, rect.top);
387                                 LineTo(rect.right-1, rect.top);
388
389                                 /* Draw the bottom and right */
390                                 RGBForeColor(&darkcolor);
391                                 MoveTo(rect.right-1, rect.top+1);
392                                 LineTo(rect.right-1, rect.bottom-1);
393                                 LineTo(rect.left+1, rect.bottom-1);
394
395                                 /* Restore the foreground color */
396                                 RGBForeColor(&color);
397                         }
398                         break;
399
400                 case RSRC_ASK_NAME:
401                         PenNormal();
402                         if (macFlags.color) {
403                                 RGBForeColor(&whitecolor);
404                                 RGBBackColor(&whitecolor);
405                                 TextMode(srcOr);
406                         } else {
407                                 PenMode(notPatCopy);
408                                 TextMode(srcBic);
409                         }
410                         InsetRect(&rect, -1, -1);
411                         FrameRect(&rect);
412                         InsetRect(&rect, -1, -1);
413                         FrameRect(&rect);
414                         InsetRect(&rect, -2, -2);
415                         if (macFlags.color) {
416                                 /* Draw the top and left */
417                                 RGBForeColor(&darkcolor);
418                                 MoveTo(rect.left, rect.bottom-1);
419                                 LineTo(rect.left, rect.top);
420                                 LineTo(rect.right-1, rect.top);
421
422                                 /* Draw the bottom and right */
423                                 RGBForeColor(&lightcolor);
424                                 MoveTo(rect.right-1, rect.top+1);
425                                 LineTo(rect.right-1, rect.bottom-1);
426                                 LineTo(rect.left+1, rect.bottom-1);
427
428                                 /* Restore the colors */
429                                 RGBForeColor(&blackcolor);
430                                 RGBBackColor(&backcolor);
431                         }
432                         break;
433         }
434         return;
435 }
436
437
438 static pascal Boolean
439 ask_filter (DialogRef wind, EventRecord *event, DialogItemIndex *item)
440 {
441         short ch, key;
442
443
444         switch (event->what) {
445                 case keyDown:
446                 case autoKey:
447                         ch = event->message & CH_MASK;
448                         key = event->message & KEY_MASK;
449                         /* Handle equivalents for OK */
450                         if ((ch == CH_RETURN) || (key == KEY_RETURN) ||
451                                 (ch == CH_ENTER) || (key == KEY_ENTER)) {
452                                 if (GetDialogTextEditHandle(wind)[0]->teLength) {
453                                         FlashButton(wind, RSRC_ASK_PLAY);
454                                         *item = RSRC_ASK_PLAY;
455                                 } else
456                                         *item = 0;
457                                 return (TRUE);
458                         }
459                         /* Handle equivalents for Normal/Explore/Debug */
460                         if ((event->modifiers & cmdKey) && (ch == 'n')) {
461                                 currmode = 0;
462                                 ask_redraw(wind, RSRC_ASK_MODE);
463                                 *item = RSRC_ASK_MODE;
464                                 return (TRUE);
465                         }
466                         if ((event->modifiers & cmdKey) && (ch == 'e')) {
467                                 currmode = 1;
468                                 ask_redraw(wind, RSRC_ASK_MODE);
469                                 *item = RSRC_ASK_MODE;
470                                 return (TRUE);
471                         }
472                         if ((event->modifiers & cmdKey) && (ch == 'd')) {
473                                 currmode = 2;
474                                 ask_redraw(wind, RSRC_ASK_MODE);
475                                 *item = RSRC_ASK_MODE;
476                                 return (TRUE);
477                         }
478                         /* Handle equivalents for Cancel and Quit */
479                         if ((ch == CH_ESCAPE) || (key == KEY_ESCAPE) ||
480                                 ((event->modifiers & cmdKey) && (ch == 'q')) ||
481                                 ((event->modifiers & cmdKey) && (ch == '.'))) {
482                                 FlashButton(wind, RSRC_ASK_QUIT);
483                                 *item = RSRC_ASK_QUIT;
484                                 return (TRUE);
485                         }
486                         return (FALSE);
487                 case updateEvt:
488                         ask_redraw(wind, RSRC_ASK_NAME);
489                         return (FALSE);
490                 default:
491                         return (FALSE);
492         }
493 }
494
495
496 void mac_askname ()
497 {
498         GrafPtr oldport;
499         DialogRef askdialog;
500         short i, j, item, type;
501         Handle handle;
502         Rect rect;
503         Str255 str;
504         Point pt;
505         UserItemUPP redraw = NewUserItemUPP(ask_redraw);
506         ModalFilterUPP filter = NewModalFilterUPP(ask_filter);
507
508
509         /* Create the dialog */
510         if (!(askdialog = GetNewDialog(RSRC_ASK, NULL, (WindowRef)-1)))
511             noresource('DLOG', RSRC_ASK);
512         GetPort(&oldport);
513         SetPortDialogPort(askdialog);
514
515         /* Initialize the name text item */
516         ask_restring(plname, str);
517         if (plname[0]) {
518             GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
519             SetDialogItemText(handle, str);
520         }
521 #if 0
522         {
523         Str32 pName;
524                 pName [0] = 0;
525                 if (plname && plname [0]) {
526                         strcpy ((char *) pName, plname);
527                         c2pstr ((char *) pName);
528                 } else {
529                         Handle h;
530                         h = GetResource ('STR ', -16096);
531                         if (((Handle) 0 != h) && (GetHandleSize (h) > 0)) {
532                                 DetachResource (h);
533                                 HLock (h);
534                                 if (**h > 31) {
535                                         **h = 31;
536                                 }
537                                 BlockMove (*h, pName, **h + 1);
538                                 DisposeHandle (h);
539                         }
540                 }
541                 if (pName [0]) {
542                         GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
543                         SetDialogItemText(handle, pName);
544                         if (pName [0] > 2 && pName [pName [0] - 1] == '-') {
545                             short role = (*pANR).anMenu[anRole];
546                             char suffix = (char) pName[pName[0]],
547                                 *sfxindx = strchr(pl_classes, suffix);
548
549                             if (sfxindx)
550                                 role = (short) (sfxindx - pl_classes);
551                             else if (suffix == '@')
552                                 role = (short) rn2((int) strlen(pl_classes));
553                             (*pANR).anMenu[anRole] = role;
554                         }
555                 }
556         }
557 #endif
558         SelectDialogItemText(askdialog, RSRC_ASK_NAME, 0, 32767);
559
560         /* Initialize the role popup menu */
561         if (!(askmenu[RSRC_ASK_ROLE] = NewMenu(RSRC_ASK_ROLE, "\p")))
562             fatal("\pCannot create role menu");
563         for (i = 0; roles[i].name.m; i++) {
564             ask_restring(roles[i].name.m, str);
565             AppendMenu(askmenu[RSRC_ASK_ROLE], str);
566         }
567         InsertMenu(askmenu[RSRC_ASK_ROLE], hierMenu);
568         if (flags.initrole >= 0)
569             currrole = flags.initrole;
570         /* Check for backward compatibility */
571         else if ((currrole = str2role(pl_character)) < 0)
572             currrole = randrole();
573
574         /* Initialize the race popup menu */
575         if (!(askmenu[RSRC_ASK_RACE] = NewMenu(RSRC_ASK_RACE, "\p")))
576             fatal("\pCannot create race menu");
577         for (i = 0; races[i].noun; i++) {
578             ask_restring(races[i].noun, str);
579             AppendMenu(askmenu[RSRC_ASK_RACE], str);
580         }
581         InsertMenu(askmenu[RSRC_ASK_RACE], hierMenu);
582         if (flags.initrace >= 0)
583             currrace = flags.initrace;
584         else
585             currrace = randrace(currrole);
586
587         /* Initialize the gender popup menu */
588         if (!(askmenu[RSRC_ASK_GEND] = NewMenu(RSRC_ASK_GEND, "\p")))
589             fatal("\pCannot create gender menu");
590         for (i = 0; i < ROLE_GENDERS; i++) {
591             ask_restring(genders[i].adj, str);
592             AppendMenu(askmenu[RSRC_ASK_GEND], str);
593         }
594         InsertMenu(askmenu[RSRC_ASK_GEND], hierMenu);
595         if (flags.initgend >= 0)
596             currgend = flags.initgend;
597         else if (flags.female)
598             currgend = 1;
599         else
600             currgend = randgend(currrole, currrace);
601
602         /* Initialize the alignment popup menu */
603         if (!(askmenu[RSRC_ASK_ALIGN] = NewMenu(RSRC_ASK_ALIGN, "\p")))
604             fatal("\pCannot create alignment menu");
605         for (i = 0; i < ROLE_ALIGNS; i++) {
606             ask_restring(aligns[i].adj, str);
607             AppendMenu(askmenu[RSRC_ASK_ALIGN], str);
608         }
609         InsertMenu(askmenu[RSRC_ASK_ALIGN], hierMenu);
610         if (flags.initalign >= 0)
611             curralign = flags.initalign;
612         else
613             curralign = randalign(currrole, currrace);
614
615         /* Initialize the mode popup menu */
616         if (!(askmenu[RSRC_ASK_MODE] = NewMenu(RSRC_ASK_MODE, "\p")))
617             fatal("\pCannot create mode menu");
618         AppendMenu(askmenu[RSRC_ASK_MODE], "\pNormal");
619         AppendMenu(askmenu[RSRC_ASK_MODE], "\pExplore");
620 #ifdef WIZARD
621         AppendMenu(askmenu[RSRC_ASK_MODE], "\pDebug");
622 #endif
623         InsertMenu(askmenu[RSRC_ASK_MODE], hierMenu);
624         currmode = 0;
625
626         /* Set the redraw procedures */
627         for (item = RSRC_ASK_DEFAULT; item <= RSRC_ASK_MODE; item++) {
628             GetDialogItem(askdialog, item, &type, &handle, &rect);
629             SetDialogItem(askdialog, item, type, (Handle)redraw, &rect);
630         }
631
632         /* Handle dialog events */
633         do {
634             /* Adjust the Play button */
635             ask_enable(askdialog, RSRC_ASK_PLAY,
636                                 GetDialogTextEditHandle(askdialog)[0]->teLength);
637
638             /* Adjust the race popup menu */
639             i = j = currrace;
640             do {
641                 if (validrace(currrole, j)) {
642                         EnableMenuItem(askmenu[RSRC_ASK_RACE], j+1);
643                         CheckMenuItem(askmenu[RSRC_ASK_RACE], j+1,
644                                         currrace == j);
645                 } else {
646                         DisableMenuItem(askmenu[RSRC_ASK_RACE], j+1);
647                         CheckMenuItem(askmenu[RSRC_ASK_RACE], j+1, FALSE);
648                         if ((currrace == j) && !races[++currrace].noun)
649                                 currrace = 0;
650                 }
651                 if (!races[++j].noun) j = 0;
652             } while (i != j);
653             if (currrace != i) {
654                 GetDialogItem(askdialog, RSRC_ASK_RACE, &type, &handle, &rect);
655                 InvalWindowRect(GetDialogWindow(askdialog), &rect);
656             }
657
658             /* Adjust the gender popup menu */
659             i = j = currgend;
660             do {
661                 if (validgend(currrole, currrace, j)) {
662                         EnableMenuItem(askmenu[RSRC_ASK_GEND], j+1);
663                         CheckMenuItem(askmenu[RSRC_ASK_GEND], j+1,
664                                         currgend == j);
665                 } else {
666                         DisableMenuItem(askmenu[RSRC_ASK_GEND], j+1);
667                         CheckMenuItem(askmenu[RSRC_ASK_GEND], j+1, FALSE);
668                         if ((currgend == j) && (++currgend >= ROLE_GENDERS))
669                                 currgend = 0;
670                 }
671                 if (++j >= ROLE_GENDERS) j = 0;
672             } while (i != j);
673             if (currgend != i) {
674                 GetDialogItem(askdialog, RSRC_ASK_GEND, &type, &handle, &rect);
675                 InvalWindowRect(GetDialogWindow(askdialog), &rect);
676             }
677
678             /* Adjust the alignment popup menu */
679             i = j = curralign;
680             do {
681                 if (validalign(currrole, currrace, j)) {
682                         EnableMenuItem(askmenu[RSRC_ASK_ALIGN], j+1);
683                         CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j+1,
684                                         curralign == j);
685                 } else {
686                         DisableMenuItem(askmenu[RSRC_ASK_ALIGN], j+1);
687                         CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j+1, FALSE);
688                         if ((curralign == j) && (++curralign >= ROLE_ALIGNS))
689                                 curralign = 0;
690                 }
691                 if (++j >= ROLE_ALIGNS) j = 0;
692             } while (i != j);
693             if (curralign != i) {
694                 GetDialogItem(askdialog, RSRC_ASK_ALIGN, &type, &handle, &rect);
695                 InvalWindowRect(GetDialogWindow(askdialog), &rect);
696             }
697
698             /* Adjust the role popup menu */
699             for (i = 0; roles[i].name.m; i++) {
700                 ask_restring((currgend && roles[i].name.f) ?
701                                 roles[i].name.f : roles[i].name.m, str);
702                 SetMenuItemText(askmenu[RSRC_ASK_ROLE], i+1, str);
703                 CheckMenuItem(askmenu[RSRC_ASK_ROLE], i+1, currrole == i);
704             }
705
706             /* Adjust the mode popup menu */
707             CheckMenuItem(askmenu[RSRC_ASK_MODE], 1, currmode == 0);
708             CheckMenuItem(askmenu[RSRC_ASK_MODE], 2, currmode == 1);
709 #ifdef WIZARD
710             CheckMenuItem(askmenu[RSRC_ASK_MODE], 3, currmode == 2);
711 #endif
712
713             /* Wait for an action on an item */
714             ModalDialog(filter, &item);
715             switch (item) {
716             case RSRC_ASK_PLAY:
717                 break;
718             case RSRC_ASK_QUIT:
719                 currmode = -1;
720                 break;
721             case RSRC_ASK_ROLE:
722             case RSRC_ASK_RACE:
723             case RSRC_ASK_ALIGN:
724             case RSRC_ASK_GEND:
725             case RSRC_ASK_MODE:
726                 GetDialogItem(askdialog, item, &type, &handle, &rect);
727                 pt = *(Point *)&rect;
728                 LocalToGlobal(&pt);
729                 if (!!(i = PopUpMenuSelect(askmenu[item], pt.v, pt.h,
730                                 askselect[item] + 1)))
731                         askselect[item] = LoWord(i) - 1;
732                 InvalWindowRect(GetDialogWindow(askdialog), &rect);
733                 break;
734             case RSRC_ASK_NAME:
735 #if 0
736             /* limit the data here to 25 chars */
737             {
738                 short beepTEDelete = 1;
739
740                 while ((**dRec.textH).teLength > 25)
741                 {
742                         if (beepTEDelete++ <= 3)
743                                 SysBeep(3);
744                         TEKey('\b', dRec.textH);
745                 }
746             }
747
748             /* special case filter (that doesn't plug all the holes!) */
749             if (((**dRec.textH).teLength == 1) && (**((**dRec.textH).hText) < 32))
750                 TEKey('\b', dRec.textH);
751 #endif
752                 break;
753             }
754         } while ((item != RSRC_ASK_PLAY) && (item != RSRC_ASK_QUIT));
755
756         /* Process the name */
757         GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
758         GetDialogItemText(handle, str);
759         if (str[0] > PL_NSIZ-1) str[0] = PL_NSIZ-1;
760         BlockMove(&str[1], plname, str[0]);
761         plname[str[0]] = '\0';
762
763         /* Destroy the dialog */
764         for (i = RSRC_ASK_ROLE; i <= RSRC_ASK_MODE; i++) {
765             DeleteMenu(i);
766             DisposeMenu(askmenu[i]);
767         }
768         SetPort(oldport);
769         DisposeDialog(askdialog);
770         DisposeRoutineDescriptor(filter);
771         DisposeRoutineDescriptor(redraw);
772
773         /* Process the mode */
774 #ifdef WIZARD
775         wizard =
776 #endif
777         discover = 0;
778         switch (currmode) {
779         case 0:         /* Normal */
780             break;
781         case 1:         /* Explore */
782             discover = 1;
783             break;
784 #ifdef WIZARD
785         case 2:         /* Debug */
786             wizard = 1;
787             strcpy(plname, WIZARD);
788             break;
789 #endif
790         default:        /* Quit */
791             ExitToShell();
792         }
793
794         /* Process the role */
795         strcpy(pl_character, roles[currrole].name.m);
796         flags.initrole = currrole;
797
798         /* Process the race */
799         flags.initrace = currrace;
800
801         /* Process the gender */
802         flags.female = flags.initgend = currgend;
803
804         /* Process the alignment */
805         flags.initalign = curralign;
806
807         return;
808 }
809
810
811
812 /*** Menu bar routines ***/
813
814 #if !TARGET_API_MAC_CARBON
815 static void
816 alignAD(Rect *pRct, short vExempt)
817 {
818         BitMap qbitmap;
819
820
821         GetQDGlobalsScreenBits(&qbitmap);
822         (*pRct).right -= (*pRct).left;          /* width */
823         (*pRct).bottom -= (*pRct).top;          /* height */
824         (*pRct).left = (qbitmap.bounds.right - (*pRct).right) / 2;
825         (*pRct).top = (qbitmap.bounds.bottom - (*pRct).bottom - vExempt) / 2;
826         (*pRct).top += vExempt;
827         (*pRct).right += (*pRct).left;
828         (*pRct).bottom += (*pRct).top;
829 }
830 #endif
831
832
833 static void
834 mustGetMenuAlerts()
835 {
836         short           i;
837         Rect            **hRct;
838
839         for (i = alrt_Menu_start; i < alrt_Menu_limit; i++)
840         {
841                 if (! (hRct = (Rect **) GetResource('ALRT', i)))        /* AlertTHndl */
842                 {
843                         for (i = 0; i < beepMenuAlertErr; i++)
844                                 SysBeep(3);
845                         ExitToShell();
846                 }
847
848 #if !TARGET_API_MAC_CARBON
849                 alignAD(*hRct, GetMBarHeight());
850 #endif
851         }
852 }
853
854 static void
855 menuError(short menuErr)
856 {
857         short   i;
858
859         for (i = 0; i < beepMenuAlertErr; i++)
860                 SysBeep(3);
861
862         ParamText(menuErrStr[menuErr], "\p", "\p", "\p");
863         (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
864
865         ExitToShell();
866 }
867
868 void
869 InitMenuRes()
870 {
871         static Boolean was_inited = 0;
872         short                   i, j;
873         menuListHandle  mlHnd;
874         MenuHandle              menu;
875
876         if (was_inited)
877                 return;
878         was_inited = 1;
879
880         mustGetMenuAlerts();
881
882         for (i = listMenubar; i <= listSubmenu; i++) {
883                 if (! (mlHnd = (menuListHandle) GetResource('MNU#', (menuBarListID + i))))
884                         menuError(errGetMenuList);
885
886                 pMenuList[i] = (menuListPtr) NewPtr(GetHandleSize((Handle) mlHnd));
887                 *pMenuList[i] = **mlHnd;
888
889                 for (j = 0; j < pMenuList[i]->numMenus; j++)
890                 {
891                         if (! (menu = (MenuHandle) GetMenu((**mlHnd).mref[j].mresID))) {
892                         Str31 d;
893                                 NumToString ((**mlHnd).mref[j].mresID, d);
894                                 menuError(errGetMenu);
895                         }
896
897                         pMenuList[i]->mref[j].mhnd = menu;
898                         SetMenuID(menu, j + (**mlHnd).firstMenuID);     /* consecutive IDs */
899
900                         /* expand apple menu */
901                         if ((i == listMenubar) && (j == menuApple)) {
902                                 AppendResMenu(menu, 'DRVR');
903                         }
904
905                         InsertMenu(menu, ((i == listSubmenu) ? hierMenu : 0));
906                 }
907         }
908         DrawMenuBar();
909         return;
910 }
911
912 void
913 AdjustMenus(short dimMenubar)
914 {
915         short           newMenubar = mbarRegular;
916         WindowRef win = FrontWindow();
917         short           i;
918
919         /*
920          *      if (windowprocs != mac_procs) {
921          *              return;
922          *      }
923          */
924         /* determine the new menubar state */
925         if (dimMenubar)
926                 newMenubar = mbarDim;
927         else if (!win)
928                 newMenubar = mbarNoWindows;
929         else if (GetWindowKind(win) < 0)
930                 newMenubar = mbarDA;
931         else if (!IsWindowVisible(_mt_window))
932                 newMenubar = mbarNoMap;
933
934         if (newMenubar != mbarRegular)
935                 ;                                                       /* we've already found its state */
936 #ifdef WIZARD
937         else if (wizard)
938         {
939                 newMenubar = mbarSpecial;
940
941                 if (kAdjustWizardMenu)
942                 {
943                         kAdjustWizardMenu = 0;
944
945                         SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pDebug");
946                 }
947         }
948 #endif
949
950         else if (discover)
951         {
952                 newMenubar = mbarSpecial;
953
954                 if (kAdjustWizardMenu)
955                 {
956                         kAdjustWizardMenu = 0;
957
958                         SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pExplore");
959
960                         for (i = CountMenuItems(MHND_WIZ); i > menuWizardAttributes; i--)
961                                 DeleteMenuItem(MHND_WIZ, i);
962                 }
963         }
964
965         /* adjust the menubar, if there's a state change */
966         if (theMenubar != newMenubar)
967         {
968                 switch(theMenubar = newMenubar)
969                 {
970                 case mbarDim:
971                         /* disable all menus (except the apple menu) */
972                         for (i = menuFile; i < NUM_MBAR; i++)
973                                 DisableMenuItem(MBARHND(i), 0);
974                         break;
975
976                 case mbarNoWindows:
977                 case mbarDA:
978                 case mbarNoMap:
979                         /* enable the file menu, but ... */
980                         EnableMenuItem(MHND_FILE, 0);
981
982                         /* ... disable the window commands! */
983                         for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
984                                 DisableMenuItem(MHND_FILE, i);
985
986                         /* ... and disable the rest of the menus */
987                         for (i = menuEdit; i < NUM_MBAR; i++)
988                                 DisableMenuItem(MBARHND(i), 0);
989
990                         if (theMenubar == mbarDA)
991                                 EnableMenuItem(MHND_EDIT, 0);
992
993                         break;
994
995                 case mbarRegular:
996                 case mbarSpecial:
997                         /* enable all menus ... */
998                         for (i = menuFile; i < NUM_MBAR; i++)
999                                 EnableMenuItem(MBARHND(i), 0);
1000
1001                         /* ... except the unused Edit menu */
1002                         DisableMenuItem(MHND_EDIT, 0);
1003
1004                         /* ... enable the window commands */
1005                         for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
1006                                 EnableMenuItem(MHND_FILE, i);
1007
1008                         if (theMenubar == mbarRegular)
1009                                 DisableMenuItem(MHND_FILE, menuFilePlayMode);
1010                         else
1011                                 DisableMenuItem(MHND_FILE, menuFileEnterExplore);
1012
1013                         break;
1014                 }
1015
1016                 DrawMenuBar();
1017         }
1018 }
1019
1020 void
1021 DoMenuEvt(long menuEntry)
1022 {
1023         short menuID = HiWord(menuEntry);
1024         short menuItem = LoWord(menuEntry);
1025
1026         switch(menuID - ID1_MBAR)       /* all submenus are default case */
1027         {
1028         case menuApple:
1029                 if (menuItem == menuAppleAboutBox)
1030                         aboutNetHack();
1031 #if !TARGET_API_MAC_CARBON
1032                 else
1033                 {
1034                         unsigned char daName[32];
1035
1036                         GetMenuItemText(MHND_APPLE, menuItem, * (Str255 *) daName);
1037                         (void) OpenDeskAcc(daName);
1038                 }
1039 #endif
1040                 break;
1041
1042         /*
1043          * Those direct calls are ugly: they should be installed into cmd.c .
1044          * Those AddToKeyQueue() calls are also ugly: they should be put into
1045          * the 'STR#' resource.
1046          */
1047         case menuFile:
1048                 switch(menuItem)
1049                 {
1050                 case menuFileRedraw:
1051                         AddToKeyQueue ('R' & 0x1f, 1);
1052                         break;
1053
1054                 case menuFilePrevMsg:
1055                         AddToKeyQueue ('P' & 0x1f, 1);
1056                         break;
1057
1058                 case menuFileCleanup:
1059                         (void) SanePositions();
1060                         break;
1061
1062                 case menuFileEnterExplore:
1063                         AddToKeyQueue ('X', 1);
1064                         break;
1065
1066                 case menuFileSave:
1067                         askSave();
1068                         break;
1069
1070                 case menuFileQuit:
1071                         askQuit();
1072                         break;
1073                 }
1074                 break;
1075
1076         case menuEdit:
1077 #if !TARGET_API_MAC_CARBON
1078                 (void) SystemEdit(menuItem - 1);
1079 #endif
1080                 break;
1081
1082         default:        /* get associated string and add to key queue */
1083                 {
1084                         Str255  mstr;
1085                         short   i;
1086
1087                         GetIndString(mstr, menuID, menuItem);
1088                         if (mstr[0] > QUEUE_LEN)
1089                                 mstr[0] = QUEUE_LEN;
1090
1091                         for (i = 1; ((i <= mstr[0]) && (mstr[i] != mstrEndChar)); i++)
1092                                 AddToKeyQueue(mstr[i], false);
1093                 }
1094                 break;
1095         }
1096
1097         HiliteMenu(0);
1098 }
1099
1100
1101 static void
1102 aboutNetHack() {
1103         if (theMenubar >= mbarRegular) {
1104                 (void) doversion();                             /* is this necessary? */
1105         } else {
1106                 unsigned char aboutStr[32] = "\pNetHack 3.4.";
1107
1108                 if (PATCHLEVEL > 10) {
1109                         aboutStr[++aboutStr[0]] = '0'+PATCHLEVEL/10;
1110                 }
1111
1112                 aboutStr[++aboutStr[0]] = '0' + (PATCHLEVEL % 10);
1113
1114                 ParamText(aboutStr, "\p\rdevteam@www.nethack.org", "\p", "\p");
1115                 (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
1116                 ResetAlertStage();
1117         }
1118 }
1119
1120
1121 static void
1122 askSave()
1123 {
1124         Boolean doSave = 1;
1125         Boolean doYes = 0;
1126
1127         if (theMenubar < mbarRegular) {
1128         short   itemHit;
1129
1130                 ParamText("\pReally Save?", "\p", "\p", "\p");
1131                 itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
1132                 ResetAlertStage();
1133
1134                 if (itemHit != bttnMenuAlertYes) {
1135                         doSave = 0;
1136                 } else {
1137                         doYes = 1;
1138                 }
1139         }
1140         if (doSave) {
1141                 AddToKeyQueue ('S', 1);
1142                 if (doYes) {
1143                         AddToKeyQueue ('y', 1);
1144                 }
1145         }
1146 }
1147
1148 static void
1149 askQuit()
1150 {
1151         Boolean doQuit = 1;
1152         Boolean doYes = 0;
1153         Boolean winMac;
1154         char *quitinput;
1155
1156         if (!strcmp (windowprocs.name, "mac"))
1157                 winMac = 1;
1158         else
1159                 winMac = 0;
1160                 
1161         if (theMenubar < mbarRegular) {
1162         short   itemHit;
1163
1164                 ParamText("\pReally Quit?", "\p", "\p", "\p");
1165                 itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
1166                 ResetAlertStage();
1167
1168                 if (itemHit != bttnMenuAlertYes) {
1169                         doQuit = 0;
1170                 } else {
1171                         doYes = 1;
1172                 }
1173         }
1174         if (doQuit) {
1175                 /* MWM -- forgive me lord, an even uglier kludge to deal with differences
1176                         in command input handling
1177                  */
1178                  if (winMac)
1179                         quitinput = "#quit\r";
1180                 else
1181                         quitinput = "#q\r";
1182                         
1183                 /* KMH -- Ugly kludge */
1184                 while (*quitinput)
1185                         AddToKeyQueue(*quitinput++, 1);
1186                 if (doYes) {
1187                         if (winMac)
1188                                 quitinput = "y\rq\r\r\r";
1189                         else
1190                                 quitinput = "yq\r";
1191                         while (*quitinput)
1192                                 AddToKeyQueue(*quitinput++, 1);
1193                 }
1194         }
1195 }
1196