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
ask_restring(const char * cstr,unsigned char * pstr)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
ask_enable(DialogRef wind,short item,int enable)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
ask_redraw(DialogRef wind,DialogItemIndex item)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
ask_filter(DialogRef wind,EventRecord * event,DialogItemIndex * item)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
mac_askname()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 *)▭
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
alignAD(Rect * pRct,short vExempt)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
mustGetMenuAlerts()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
menuError(short menuErr)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
InitMenuRes()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
AdjustMenus(short dimMenubar)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
DoMenuEvt(long menuEntry)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
aboutNetHack()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
askSave()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
askQuit()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
1197