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 (*pRct).right -= (*pRct).left; /* width */
819 (*pRct).bottom -= (*pRct).top; /* height */
820 (*pRct).left = (qd.screenBits.bounds.right - (*pRct).right) / 2;
821 (*pRct).top = (qd.screenBits.bounds.bottom - (*pRct).bottom - vExempt) / 2;
822 (*pRct).top += vExempt;
823 (*pRct).right += (*pRct).left;
824 (*pRct).bottom += (*pRct).top;
825 }
826 #endif
827
828
829 static void
mustGetMenuAlerts()830 mustGetMenuAlerts()
831 {
832 short i;
833 Rect **hRct;
834
835 for (i = alrt_Menu_start; i < alrt_Menu_limit; i++)
836 {
837 if (! (hRct = (Rect **) GetResource('ALRT', i))) /* AlertTHndl */
838 {
839 for (i = 0; i < beepMenuAlertErr; i++)
840 SysBeep(3);
841 ExitToShell();
842 }
843
844 #if !TARGET_API_MAC_CARBON
845 alignAD(*hRct, GetMBarHeight());
846 #endif
847 }
848 }
849
850 static void
menuError(short menuErr)851 menuError(short menuErr)
852 {
853 short i;
854
855 for (i = 0; i < beepMenuAlertErr; i++)
856 SysBeep(3);
857
858 ParamText(menuErrStr[menuErr], "\p", "\p", "\p");
859 (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
860
861 ExitToShell();
862 }
863
864 void
InitMenuRes()865 InitMenuRes()
866 {
867 static Boolean was_inited = 0;
868 short i, j;
869 menuListHandle mlHnd;
870 MenuHandle menu;
871
872 if (was_inited)
873 return;
874 was_inited = 1;
875
876 mustGetMenuAlerts();
877
878 for (i = listMenubar; i <= listSubmenu; i++) {
879 if (! (mlHnd = (menuListHandle) GetResource('MNU#', (menuBarListID + i))))
880 menuError(errGetMenuList);
881
882 pMenuList[i] = (menuListPtr) NewPtr(GetHandleSize((Handle) mlHnd));
883 *pMenuList[i] = **mlHnd;
884
885 for (j = 0; j < pMenuList[i]->numMenus; j++)
886 {
887 if (! (menu = (MenuHandle) GetMenu((**mlHnd).mref[j].mresID))) {
888 Str31 d;
889 NumToString ((**mlHnd).mref[j].mresID, d);
890 menuError(errGetMenu);
891 }
892
893 pMenuList[i]->mref[j].mhnd = menu;
894 #if !TARGET_API_MAC_CARBON
895 * ((short *) *menu) = j + (**mlHnd).firstMenuID;
896 #else
897 SetMenuID(menu, j + (**mlHnd).firstMenuID);
898 #endif
899 /* consecutive IDs */
900
901 /* expand apple menu */
902 if ((i == listMenubar) && (j == menuApple)) {
903 AppendResMenu(menu, 'DRVR');
904 }
905
906 InsertMenu(menu, ((i == listSubmenu) ? hierMenu : 0));
907 }
908 }
909 DrawMenuBar();
910 return;
911 }
912
913 void
AdjustMenus(short dimMenubar)914 AdjustMenus(short dimMenubar)
915 {
916 short newMenubar = mbarRegular;
917 WindowRef win = FrontWindow();
918 short i;
919
920 /*
921 * if (windowprocs != mac_procs) {
922 * return;
923 * }
924 */
925 /* determine the new menubar state */
926 if (dimMenubar)
927 newMenubar = mbarDim;
928 else if (!win)
929 newMenubar = mbarNoWindows;
930 else if (GetWindowKind(win) < 0)
931 newMenubar = mbarDA;
932 else if (!IsWindowVisible(_mt_window))
933 newMenubar = mbarNoMap;
934
935 if (newMenubar != mbarRegular)
936 ; /* we've already found its state */
937 #ifdef WIZARD
938 else if (wizard)
939 {
940 newMenubar = mbarSpecial;
941
942 if (kAdjustWizardMenu)
943 {
944 kAdjustWizardMenu = 0;
945
946 SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pDebug");
947 }
948 }
949 #endif
950
951 else if (discover)
952 {
953 newMenubar = mbarSpecial;
954
955 if (kAdjustWizardMenu)
956 {
957 kAdjustWizardMenu = 0;
958
959 SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pExplore");
960
961 for (i = CountMenuItems(MHND_WIZ); i > menuWizardAttributes; i--)
962 DeleteMenuItem(MHND_WIZ, i);
963 }
964 }
965
966 /* adjust the menubar, if there's a state change */
967 if (theMenubar != newMenubar)
968 {
969 switch(theMenubar = newMenubar)
970 {
971 case mbarDim:
972 /* disable all menus (except the apple menu) */
973 for (i = menuFile; i < NUM_MBAR; i++)
974 DisableMenuItem(MBARHND(i), 0);
975 break;
976
977 case mbarNoWindows:
978 case mbarDA:
979 case mbarNoMap:
980 /* enable the file menu, but ... */
981 EnableMenuItem(MHND_FILE, 0);
982
983 /* ... disable the window commands! */
984 for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
985 DisableMenuItem(MHND_FILE, i);
986
987 /* ... and disable the rest of the menus */
988 for (i = menuEdit; i < NUM_MBAR; i++)
989 DisableMenuItem(MBARHND(i), 0);
990
991 if (theMenubar == mbarDA)
992 EnableMenuItem(MHND_EDIT, 0);
993
994 break;
995
996 case mbarRegular:
997 case mbarSpecial:
998 /* enable all menus ... */
999 for (i = menuFile; i < NUM_MBAR; i++)
1000 EnableMenuItem(MBARHND(i), 0);
1001
1002 /* ... except the unused Edit menu */
1003 DisableMenuItem(MHND_EDIT, 0);
1004
1005 /* ... enable the window commands */
1006 for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
1007 EnableMenuItem(MHND_FILE, i);
1008
1009 if (theMenubar == mbarRegular)
1010 DisableMenuItem(MHND_FILE, menuFilePlayMode);
1011 else
1012 DisableMenuItem(MHND_FILE, menuFileEnterExplore);
1013
1014 break;
1015 }
1016
1017 DrawMenuBar();
1018 }
1019 }
1020
1021 void
DoMenuEvt(long menuEntry)1022 DoMenuEvt(long menuEntry)
1023 {
1024 short menuID = HiWord(menuEntry);
1025 short menuItem = LoWord(menuEntry);
1026
1027 switch(menuID - ID1_MBAR) /* all submenus are default case */
1028 {
1029 case menuApple:
1030 if (menuItem == menuAppleAboutBox)
1031 aboutNetHack();
1032 #if !TARGET_API_MAC_CARBON
1033 else
1034 {
1035 unsigned char daName[32];
1036
1037 GetMenuItemText(MHND_APPLE, menuItem, * (Str255 *) daName);
1038 (void) OpenDeskAcc(daName);
1039 }
1040 #endif
1041 break;
1042
1043 /*
1044 * Those direct calls are ugly: they should be installed into cmd.c .
1045 * Those AddToKeyQueue() calls are also ugly: they should be put into
1046 * the 'STR#' resource.
1047 */
1048 case menuFile:
1049 switch(menuItem)
1050 {
1051 case menuFileRedraw:
1052 AddToKeyQueue ('R' & 0x1f, 1);
1053 break;
1054
1055 case menuFilePrevMsg:
1056 AddToKeyQueue ('P' & 0x1f, 1);
1057 break;
1058
1059 case menuFileCleanup:
1060 (void) SanePositions();
1061 break;
1062
1063 case menuFileEnterExplore:
1064 AddToKeyQueue ('X', 1);
1065 break;
1066
1067 case menuFileSave:
1068 askSave();
1069 break;
1070
1071 case menuFileQuit:
1072 askQuit();
1073 break;
1074 }
1075 break;
1076
1077 case menuEdit:
1078 #if !TARGET_API_MAC_CARBON
1079 (void) SystemEdit(menuItem - 1);
1080 #endif
1081 break;
1082
1083 default: /* get associated string and add to key queue */
1084 {
1085 Str255 mstr;
1086 short i;
1087
1088 GetIndString(mstr, menuID, menuItem);
1089 if (mstr[0] > QUEUE_LEN)
1090 mstr[0] = QUEUE_LEN;
1091
1092 for (i = 1; ((i <= mstr[0]) && (mstr[i] != mstrEndChar)); i++)
1093 AddToKeyQueue(mstr[i], false);
1094
1095 /* Special processing for extended commands. The command
1096 selection appears before mstrEndChar, the rest appears
1097 after. The rest is used only when in Mac windows mode.
1098 In any case we add a newline to get the command
1099 going.
1100 */
1101 if ( mstr[1] == '#' )
1102 {
1103 if ( windowprocs.win_init_nhwindows == mac_procs.win_init_nhwindows )
1104 for ( i++; ((i <= mstr[0]) && (mstr[i] != mstrEndChar)); i++)
1105 AddToKeyQueue(mstr[i], false);
1106 AddToKeyQueue('\n', false);
1107 }
1108 }
1109 break;
1110 }
1111
1112 HiliteMenu(0);
1113 }
1114
1115
1116 static void
aboutNetHack()1117 aboutNetHack() {
1118 if (theMenubar >= mbarRegular) {
1119 (void) doversion(); /* is this necessary? */
1120 } else {
1121 unsigned char aboutStr[32] = "\pSlash'EM 0.0.";
1122
1123 if (PATCHLEVEL > 10) {
1124 aboutStr[++aboutStr[0]] = '0' + PATCHLEVEL / 10;
1125 }
1126
1127 aboutStr[++aboutStr[0]] = '0' + PATCHLEVEL % 10;
1128
1129 if (EDITLEVEL) {
1130 aboutStr[++aboutStr[0]] = 'e';
1131 aboutStr[++aboutStr[0]] = '0' + EDITLEVEL;
1132 }
1133 #ifdef FIXLEVEL
1134 if (FIXLEVEL) {
1135 aboutStr[++aboutStr[0]] = 'f';
1136 aboutStr[++aboutStr[0]] = '0' + FIXLEVEL;
1137 }
1138 #endif
1139 aboutStr[++aboutStr[0]] = CHAR_CR;
1140
1141 ParamText(aboutStr, "\pwww.slashem.org", "\p", "\p");
1142 (void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
1143 ResetAlertStage();
1144 }
1145 }
1146
1147
1148 static void
askSave()1149 askSave()
1150 {
1151 Boolean doSave = 1;
1152 Boolean doYes = 0;
1153
1154 if (theMenubar < mbarRegular) {
1155 short itemHit;
1156
1157 ParamText("\pReally Save?", "\p", "\p", "\p");
1158 itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
1159 ResetAlertStage();
1160
1161 if (itemHit != bttnMenuAlertYes) {
1162 doSave = 0;
1163 } else {
1164 doYes = 1;
1165 }
1166 }
1167 if (doSave) {
1168 AddToKeyQueue ('S', 1);
1169 if (doYes) {
1170 AddToKeyQueue ('y', 1);
1171 }
1172 }
1173 }
1174
1175 static void
askQuit()1176 askQuit()
1177 {
1178 Boolean doQuit = 1;
1179 Boolean doYes = 0;
1180 Boolean winMac;
1181 char *quitinput;
1182
1183 if (!strcmp (windowprocs.name, "mac"))
1184 winMac = 1;
1185 else
1186 winMac = 0;
1187
1188 if (theMenubar < mbarRegular) {
1189 short itemHit;
1190
1191 ParamText("\pReally Quit?", "\p", "\p", "\p");
1192 itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
1193 ResetAlertStage();
1194
1195 if (itemHit != bttnMenuAlertYes) {
1196 doQuit = 0;
1197 } else {
1198 doYes = 1;
1199 }
1200 }
1201 if (doQuit) {
1202 /* MWM -- forgive me lord, an even uglier kludge to deal with differences
1203 in command input handling
1204 */
1205 if (winMac)
1206 quitinput = "#quit\r";
1207 else
1208 quitinput = "#q\r";
1209
1210 /* KMH -- Ugly kludge */
1211 while (*quitinput)
1212 AddToKeyQueue(*quitinput++, 1);
1213 if (doYes) {
1214 if (winMac)
1215 quitinput = "y\rq\r\r\r";
1216 else
1217 quitinput = "yq\r";
1218 while (*quitinput)
1219 AddToKeyQueue(*quitinput++, 1);
1220 }
1221 }
1222 }
1223
1224