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