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