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