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