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 *)&rect;
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