I'll push a formatting guide at some point. There may still be outstanding changes, but please feel free to resolve those as you arrive a them. To the best of my knowledge, there is no changes to the actual code content, but the formatter does have the occasional bug. If you run into an issue, please fix it!
1155 lines
33 KiB
C
1155 lines
33 KiB
C
/* NetHack 3.6 macmenu.c $NHDT-Date: 1431192785 2015/05/09 17:33:05 $ $NHDT-Branch: master $:$NHDT-Revision: 1.12 $ */
|
|
/* NetHack 3.6 macmenu.c $Date: 2009/05/06 10:49:13 $ $Revision: 1.7 $ */
|
|
/* SCCS Id: @(#)macmenu.c 3.5 1999/11/24 */
|
|
/* Copyright (c) Macintosh NetHack Port Team, 1993. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
/****************************************\
|
|
* Extended Macintosh menu support
|
|
*
|
|
* provides access to all keyboard commands from cmd.c
|
|
* provides control key functionality for classic keyboards
|
|
* provides key equivalent references and logical menu groups
|
|
* supports various menu highlighting modes
|
|
\****************************************/
|
|
|
|
/****************************************\
|
|
* Edit History:
|
|
*
|
|
* 930512 - More bug fixes and getting tty to work again, Jon W{tte
|
|
* 930508 - Bug fixes in-flight, Jon W{tte
|
|
* 04/29/93 - 1st Release Draft, David Hairston
|
|
* 04/11/93 - 1st Draft, David Hairston
|
|
\****************************************/
|
|
|
|
/******** Application Defines ********/
|
|
#include "hack.h"
|
|
#include "mactty.h"
|
|
#include "macwin.h"
|
|
#include "macpopup.h"
|
|
#include "patchlevel.h"
|
|
|
|
/******** Toolbox Defines ********/
|
|
#if !TARGET_API_MAC_CARBON
|
|
#include <Menus.h>
|
|
#include <Devices.h>
|
|
#include <Resources.h>
|
|
#include <TextUtils.h>
|
|
#include <ToolUtils.h>
|
|
#include <Sound.h>
|
|
#endif
|
|
|
|
/* Borrowed from the Mac tty port */
|
|
extern WindowPtr _mt_window;
|
|
|
|
/******** Local Defines ********/
|
|
|
|
/* 'MNU#' (menu list record) */
|
|
typedef union menuRefUnn {
|
|
short mresID; /* MENU resource ID (before GetMenu) */
|
|
MenuHandle mhnd; /* MENU handle (after GetMenu) */
|
|
} menuRefUnn;
|
|
|
|
typedef struct menuListRec {
|
|
short firstMenuID;
|
|
short numMenus;
|
|
menuRefUnn mref[];
|
|
} menuListRec, *menuListPtr, **menuListHandle;
|
|
|
|
/* indices and resource IDs of the menu list data */
|
|
enum {
|
|
listMenubar,
|
|
listSubmenu,
|
|
|
|
menuBarListID = 128,
|
|
subMenuListID
|
|
};
|
|
|
|
/* the following mref[] indices are reserved */
|
|
enum {
|
|
/* menu bar */
|
|
menuApple,
|
|
menuFile,
|
|
menuEdit,
|
|
|
|
/* submenu */
|
|
menuWizard = 0
|
|
};
|
|
|
|
/* the following menu items are reserved */
|
|
enum {
|
|
/* apple */
|
|
menuAppleAboutBox = 1,
|
|
____Apple__1,
|
|
|
|
/* File */
|
|
menuFileRedraw = 1,
|
|
menuFilePrevMsg,
|
|
menuFileCleanup,
|
|
____File___1,
|
|
menuFilePlayMode,
|
|
menuFileEnterExplore,
|
|
____File___2,
|
|
menuFileSave,
|
|
____File___3,
|
|
menuFileQuit,
|
|
|
|
/* standard minimum Edit menu items */
|
|
|
|
/* Wizard */
|
|
menuWizardAttributes = 1
|
|
};
|
|
|
|
/*
|
|
* menuListRec data (preloaded and locked) specifies the number of menus in
|
|
* the menu bar, the number of hierarchal or submenus and the menu IDs of
|
|
* all of those menus. menus that go into in the menu bar are specified by
|
|
* 'MNU#' 128 and submenus are specified by 'MNU#' 129. the fields of the
|
|
* menuListRec are:
|
|
* firstMenuID - the menu ID (not resource ID) of the 1st menu. subsequent
|
|
* menus in the list are _forced_ to have consecutively incremented IDs.
|
|
* numMenus - the total count of menus in a given list (and the extent of
|
|
* valid menu IDs).
|
|
* mref[] - initially the MENU resource ID is stored in the placeholder for
|
|
* the resource handle. after loading (GetResource), the menu handle
|
|
* is stored and the menu ID, in memory, is set as noted above.
|
|
*
|
|
* NOTE: a ResEdit template editor is supplied to edit the 'MNU#' resources.
|
|
*
|
|
* NOTE: the resource IDs do not need to match the menu IDs in a menu list
|
|
* record although they have been originally set that way.
|
|
*
|
|
* NOTE: the menu ID's of menus in the submenu list record may be reset, as
|
|
* noted above. it is the programmers responsibility to make sure that
|
|
* submenu references/IDs are valid.
|
|
*
|
|
* WARNING: the existence of the submenu list record is assumed even if the
|
|
* number of submenus is zero. also, no error checking is done on the
|
|
* extents of the menu IDs. this must be correctly setup by the programmer.
|
|
*/
|
|
|
|
#define ID1_MBAR pMenuList[listMenubar]->firstMenuID
|
|
#define ID1_SUBM pMenuList[listSubmenu]->firstMenuID
|
|
|
|
#define NUM_MBAR pMenuList[listMenubar]->numMenus
|
|
#define NUM_SUBM pMenuList[listSubmenu]->numMenus
|
|
|
|
#define MHND_APPLE pMenuList[listMenubar]->mref[menuApple].mhnd
|
|
#define MHND_FILE pMenuList[listMenubar]->mref[menuFile].mhnd
|
|
#define MHND_EDIT pMenuList[listMenubar]->mref[menuEdit].mhnd
|
|
|
|
#define MBARHND(x) pMenuList[listMenubar]->mref[(x)].mhnd
|
|
|
|
#define MHND_WIZ pMenuList[listSubmenu]->mref[menuWizard].mhnd
|
|
|
|
/* mutually exclusive (and prioritized) menu bar states */
|
|
enum {
|
|
mbarDim,
|
|
mbarNoWindows,
|
|
mbarDA,
|
|
mbarNoMap,
|
|
mbarRegular,
|
|
mbarSpecial /* explore or debug mode */
|
|
};
|
|
|
|
#define WKND_MAP (WIN_BASE_KIND + NHW_MAP)
|
|
|
|
/* menu routine error numbers */
|
|
enum {
|
|
errGetMenuList,
|
|
errGetMenu,
|
|
errGetANDlogTemplate,
|
|
errGetANDlogItems,
|
|
errGetANDialog,
|
|
errANNewMenu,
|
|
err_Menu_total
|
|
};
|
|
|
|
/* menu 'STR#' comment char */
|
|
#define mstrEndChar 0xA5 /* '\245' or option-* or "bullet" */
|
|
|
|
/* 'ALRT' */
|
|
enum {
|
|
alrt_Menu_start = 5000,
|
|
alrtMenuNote = alrt_Menu_start,
|
|
alrtMenu_NY,
|
|
alrt_Menu_limit
|
|
};
|
|
|
|
#define beepMenuAlertErr 1 /* # of SysBeep()'s before exitting */
|
|
enum { bttnMenuAlertNo = 1, bttnMenuAlertYes };
|
|
|
|
/******** Globals ********/
|
|
static unsigned char *menuErrStr[err_Menu_total] = {
|
|
"\pAbort: Bad \'MNU#\' resource!", /* errGetMenuList */
|
|
"\pAbort: Bad \'MENU\' resource!", /* errGetMenu */
|
|
"\pAbort: Bad \'DLOG\' resource!", /* errGetANDlogTemplate */
|
|
"\pAbort: Bad \'DITL\' resource!", /* errGetANDlogItems */
|
|
"\pAbort: Bad Dialog Allocation!", /* errGetANDialog */
|
|
"\pAbort: Bad Menu Allocation!", /* errANNewMenu */
|
|
};
|
|
static menuListPtr pMenuList[2];
|
|
static short theMenubar = mbarDA; /* force initial update */
|
|
static short kAdjustWizardMenu = 1;
|
|
|
|
/******** Prototypes ********/
|
|
#if !TARGET_API_MAC_CARBON
|
|
static void alignAD(Rect *, short);
|
|
#endif
|
|
static void mustGetMenuAlerts(void);
|
|
static void menuError(short);
|
|
static void aboutNetHack(void);
|
|
static void askSave(void);
|
|
static void askQuit(void);
|
|
|
|
/*** Askname dialog box ***/
|
|
|
|
#define RSRC_ASK 6000 /* Askname dialog and item list */
|
|
#define RSRC_ASK_PLAY 1 /* Play button */
|
|
#define RSRC_ASK_QUIT 2 /* Quit button */
|
|
#define RSRC_ASK_DEFAULT 3 /* Default ring */
|
|
#define RSRC_ASK_ROLE 4 /* Role popup menu */
|
|
#define RSRC_ASK_RACE 5 /* Race popup menu */
|
|
#define RSRC_ASK_GEND 6 /* Gender popup menu */
|
|
#define RSRC_ASK_ALIGN 7 /* Alignment popup menu */
|
|
#define RSRC_ASK_MODE 8 /* Mode popup menu */
|
|
#define RSRC_ASK_NAME 9 /* Name text field */
|
|
#define RSRC_ASK_MAX 10 /* Maximum enabled item */
|
|
|
|
#define KEY_MASK 0xff00
|
|
#define KEY_RETURN 0x2400
|
|
#define KEY_ENTER 0x4c00
|
|
#define KEY_ESCAPE 0x3500
|
|
#define CH_MASK 0x00ff
|
|
#define CH_RETURN 0x000d
|
|
#define CH_ENTER 0x0003
|
|
#define CH_ESCAPE 0x001b
|
|
|
|
static void ask_restring(const char *cstr, unsigned char *pstr);
|
|
static void ask_enable(DialogRef wind, short item, int enable);
|
|
static pascal void ask_redraw(DialogRef wind, DialogItemIndex item);
|
|
static pascal Boolean
|
|
ask_filter(DialogRef wind, EventRecord *event, DialogItemIndex *item);
|
|
#define noresource(t, n) \
|
|
{ \
|
|
SysBeep(3); \
|
|
ExitToShell(); \
|
|
}
|
|
#define fatal(s) \
|
|
{ \
|
|
SysBeep(3); \
|
|
ExitToShell(); \
|
|
}
|
|
|
|
static MenuHandle askmenu[RSRC_ASK_MAX];
|
|
static int askselect[RSRC_ASK_MAX];
|
|
#define currrole askselect[RSRC_ASK_ROLE]
|
|
#define currrace askselect[RSRC_ASK_RACE]
|
|
#define currgend askselect[RSRC_ASK_GEND]
|
|
#define curralign askselect[RSRC_ASK_ALIGN]
|
|
#define currmode askselect[RSRC_ASK_MODE]
|
|
|
|
static RGBColor blackcolor = { 0x0000, 0x0000, 0x0000 },
|
|
// indentcolor = {0x4000, 0x4000, 0x4000},
|
|
darkcolor = { 0x8000, 0x8000, 0x8000 },
|
|
backcolor = { 0xdddd, 0xdddd, 0xdddd },
|
|
lightcolor = { 0xffff, 0xffff, 0xffff },
|
|
whitecolor = { 0xffff, 0xffff, 0xffff };
|
|
|
|
/* Convert a mixed-case C string to a Capitalized Pascal string */
|
|
static void
|
|
ask_restring(const char *cstr, unsigned char *pstr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; *cstr && (i < 255); i++)
|
|
pstr[i + 1] = *cstr++;
|
|
pstr[0] = i;
|
|
if ((pstr[1] >= 'a') && (pstr[1] <= 'z'))
|
|
pstr[1] += 'A' - 'a';
|
|
return;
|
|
}
|
|
|
|
/* Enable the dialog item with the given index */
|
|
static void
|
|
ask_enable(DialogRef wind, short item, int enable)
|
|
{
|
|
short type;
|
|
Handle handle;
|
|
Rect rect;
|
|
|
|
/* Enable or disable the appropriate item */
|
|
GetDialogItem(wind, item, &type, &handle, &rect);
|
|
if (enable)
|
|
type &= ~itemDisable;
|
|
else
|
|
type |= itemDisable;
|
|
HiliteControl((ControlHandle) handle, enable ? 0 : 255);
|
|
SetDialogItem(wind, item, type, handle, &rect);
|
|
return;
|
|
}
|
|
|
|
static pascal void
|
|
ask_redraw(DialogRef wind, DialogItemIndex item)
|
|
{
|
|
short type;
|
|
Handle handle;
|
|
Rect rect;
|
|
static char *modechar = "NED";
|
|
|
|
/* Which item shall we redraw? */
|
|
GetDialogItem(wind, item, &type, &handle, &rect);
|
|
switch (item) {
|
|
case RSRC_ASK_DEFAULT:
|
|
PenSize(3, 3);
|
|
FrameRoundRect(&rect, 16, 16);
|
|
break;
|
|
|
|
case RSRC_ASK_ROLE:
|
|
case RSRC_ASK_RACE:
|
|
case RSRC_ASK_GEND:
|
|
case RSRC_ASK_ALIGN:
|
|
case RSRC_ASK_MODE:
|
|
if (macFlags.color) {
|
|
RGBForeColor(&blackcolor);
|
|
RGBBackColor(&backcolor);
|
|
}
|
|
PenNormal();
|
|
TextMode(srcOr);
|
|
EraseRect(&rect);
|
|
|
|
/* Draw the frame and drop shadow */
|
|
rect.right--;
|
|
rect.bottom--;
|
|
FrameRect(&rect);
|
|
MoveTo(rect.right, rect.top + 1);
|
|
LineTo(rect.right, rect.bottom);
|
|
LineTo(rect.left + 1, rect.bottom);
|
|
|
|
/* Draw the menu character */
|
|
MoveTo(rect.left + 4, rect.top + 12);
|
|
switch (item) {
|
|
case RSRC_ASK_ROLE:
|
|
DrawText(roles[askselect[item]].filecode, 0, 3);
|
|
break;
|
|
case RSRC_ASK_RACE:
|
|
DrawText(races[askselect[item]].filecode, 0, 3);
|
|
break;
|
|
case RSRC_ASK_GEND:
|
|
DrawText(genders[askselect[item]].filecode, 0, 3);
|
|
break;
|
|
case RSRC_ASK_ALIGN:
|
|
DrawText(aligns[askselect[item]].filecode, 0, 3);
|
|
break;
|
|
case RSRC_ASK_MODE:
|
|
DrawChar(modechar[askselect[item]]);
|
|
break;
|
|
}
|
|
|
|
/* Draw the popup symbol */
|
|
MoveTo(rect.right - 16, rect.top + 5);
|
|
LineTo(rect.right - 6, rect.top + 5);
|
|
LineTo(rect.right - 11, rect.top + 10);
|
|
LineTo(rect.right - 15, rect.top + 6);
|
|
LineTo(rect.right - 8, rect.top + 6);
|
|
LineTo(rect.right - 11, rect.top + 9);
|
|
LineTo(rect.right - 13, rect.top + 7);
|
|
LineTo(rect.right - 10, rect.top + 7);
|
|
LineTo(rect.right - 11, rect.top + 8);
|
|
|
|
/* Draw the shadow */
|
|
InsetRect(&rect, 1, 1);
|
|
if (macFlags.color) {
|
|
RGBColor color;
|
|
|
|
/* Save the foreground color */
|
|
GetForeColor(&color);
|
|
|
|
/* Draw the top and left */
|
|
RGBForeColor(&lightcolor);
|
|
MoveTo(rect.left, rect.bottom - 1);
|
|
LineTo(rect.left, rect.top);
|
|
LineTo(rect.right - 1, rect.top);
|
|
|
|
/* Draw the bottom and right */
|
|
RGBForeColor(&darkcolor);
|
|
MoveTo(rect.right - 1, rect.top + 1);
|
|
LineTo(rect.right - 1, rect.bottom - 1);
|
|
LineTo(rect.left + 1, rect.bottom - 1);
|
|
|
|
/* Restore the foreground color */
|
|
RGBForeColor(&color);
|
|
}
|
|
break;
|
|
|
|
case RSRC_ASK_NAME:
|
|
PenNormal();
|
|
if (macFlags.color) {
|
|
RGBForeColor(&whitecolor);
|
|
RGBBackColor(&whitecolor);
|
|
TextMode(srcOr);
|
|
} else {
|
|
PenMode(notPatCopy);
|
|
TextMode(srcBic);
|
|
}
|
|
InsetRect(&rect, -1, -1);
|
|
FrameRect(&rect);
|
|
InsetRect(&rect, -1, -1);
|
|
FrameRect(&rect);
|
|
InsetRect(&rect, -2, -2);
|
|
if (macFlags.color) {
|
|
/* Draw the top and left */
|
|
RGBForeColor(&darkcolor);
|
|
MoveTo(rect.left, rect.bottom - 1);
|
|
LineTo(rect.left, rect.top);
|
|
LineTo(rect.right - 1, rect.top);
|
|
|
|
/* Draw the bottom and right */
|
|
RGBForeColor(&lightcolor);
|
|
MoveTo(rect.right - 1, rect.top + 1);
|
|
LineTo(rect.right - 1, rect.bottom - 1);
|
|
LineTo(rect.left + 1, rect.bottom - 1);
|
|
|
|
/* Restore the colors */
|
|
RGBForeColor(&blackcolor);
|
|
RGBBackColor(&backcolor);
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static pascal Boolean
|
|
ask_filter(DialogRef wind, EventRecord *event, DialogItemIndex *item)
|
|
{
|
|
short ch, key;
|
|
|
|
switch (event->what) {
|
|
case keyDown:
|
|
case autoKey:
|
|
ch = event->message & CH_MASK;
|
|
key = event->message & KEY_MASK;
|
|
/* Handle equivalents for OK */
|
|
if ((ch == CH_RETURN) || (key == KEY_RETURN) || (ch == CH_ENTER)
|
|
|| (key == KEY_ENTER)) {
|
|
if (GetDialogTextEditHandle(wind)[0]->teLength) {
|
|
FlashButton(wind, RSRC_ASK_PLAY);
|
|
*item = RSRC_ASK_PLAY;
|
|
} else
|
|
*item = 0;
|
|
return (TRUE);
|
|
}
|
|
/* Handle equivalents for Normal/Explore/Debug */
|
|
if ((event->modifiers & cmdKey) && (ch == 'n')) {
|
|
currmode = 0;
|
|
ask_redraw(wind, RSRC_ASK_MODE);
|
|
*item = RSRC_ASK_MODE;
|
|
return (TRUE);
|
|
}
|
|
if ((event->modifiers & cmdKey) && (ch == 'e')) {
|
|
currmode = 1;
|
|
ask_redraw(wind, RSRC_ASK_MODE);
|
|
*item = RSRC_ASK_MODE;
|
|
return (TRUE);
|
|
}
|
|
if ((event->modifiers & cmdKey) && (ch == 'd')) {
|
|
currmode = 2;
|
|
ask_redraw(wind, RSRC_ASK_MODE);
|
|
*item = RSRC_ASK_MODE;
|
|
return (TRUE);
|
|
}
|
|
/* Handle equivalents for Cancel and Quit */
|
|
if ((ch == CH_ESCAPE) || (key == KEY_ESCAPE)
|
|
|| ((event->modifiers & cmdKey) && (ch == 'q'))
|
|
|| ((event->modifiers & cmdKey) && (ch == '.'))) {
|
|
FlashButton(wind, RSRC_ASK_QUIT);
|
|
*item = RSRC_ASK_QUIT;
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
case updateEvt:
|
|
ask_redraw(wind, RSRC_ASK_NAME);
|
|
return (FALSE);
|
|
default:
|
|
return (FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
mac_askname()
|
|
{
|
|
GrafPtr oldport;
|
|
DialogRef askdialog;
|
|
short i, j, item, type;
|
|
Handle handle;
|
|
Rect rect;
|
|
Str255 str;
|
|
Point pt;
|
|
UserItemUPP redraw = NewUserItemUPP(ask_redraw);
|
|
ModalFilterUPP filter = NewModalFilterUPP(ask_filter);
|
|
|
|
/* Create the dialog */
|
|
if (!(askdialog = GetNewDialog(RSRC_ASK, NULL, (WindowRef) -1)))
|
|
noresource('DLOG', RSRC_ASK);
|
|
GetPort(&oldport);
|
|
SetPortDialogPort(askdialog);
|
|
|
|
/* Initialize the name text item */
|
|
ask_restring(plname, str);
|
|
if (plname[0]) {
|
|
GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
|
|
SetDialogItemText(handle, str);
|
|
}
|
|
#if 0
|
|
{
|
|
Str32 pName;
|
|
pName [0] = 0;
|
|
if (plname && plname [0]) {
|
|
strcpy ((char *) pName, plname);
|
|
c2pstr ((char *) pName);
|
|
} else {
|
|
Handle h;
|
|
h = GetResource ('STR ', -16096);
|
|
if (((Handle) 0 != h) && (GetHandleSize (h) > 0)) {
|
|
DetachResource (h);
|
|
HLock (h);
|
|
if (**h > 31) {
|
|
**h = 31;
|
|
}
|
|
BlockMove (*h, pName, **h + 1);
|
|
DisposeHandle (h);
|
|
}
|
|
}
|
|
if (pName [0]) {
|
|
GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
|
|
SetDialogItemText(handle, pName);
|
|
if (pName [0] > 2 && pName [pName [0] - 1] == '-') {
|
|
short role = (*pANR).anMenu[anRole];
|
|
char suffix = (char) pName[pName[0]],
|
|
*sfxindx = strchr(pl_classes, suffix);
|
|
|
|
if (sfxindx)
|
|
role = (short) (sfxindx - pl_classes);
|
|
else if (suffix == '@')
|
|
role = (short) rn2((int) strlen(pl_classes));
|
|
(*pANR).anMenu[anRole] = role;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
SelectDialogItemText(askdialog, RSRC_ASK_NAME, 0, 32767);
|
|
|
|
/* Initialize the role popup menu */
|
|
if (!(askmenu[RSRC_ASK_ROLE] = NewMenu(RSRC_ASK_ROLE, "\p")))
|
|
fatal("\pCannot create role menu");
|
|
for (i = 0; roles[i].name.m; i++) {
|
|
ask_restring(roles[i].name.m, str);
|
|
AppendMenu(askmenu[RSRC_ASK_ROLE], str);
|
|
}
|
|
InsertMenu(askmenu[RSRC_ASK_ROLE], hierMenu);
|
|
if (flags.initrole >= 0)
|
|
currrole = flags.initrole;
|
|
/* Check for backward compatibility */
|
|
else if ((currrole = str2role(pl_character)) < 0)
|
|
currrole = randrole();
|
|
|
|
/* Initialize the race popup menu */
|
|
if (!(askmenu[RSRC_ASK_RACE] = NewMenu(RSRC_ASK_RACE, "\p")))
|
|
fatal("\pCannot create race menu");
|
|
for (i = 0; races[i].noun; i++) {
|
|
ask_restring(races[i].noun, str);
|
|
AppendMenu(askmenu[RSRC_ASK_RACE], str);
|
|
}
|
|
InsertMenu(askmenu[RSRC_ASK_RACE], hierMenu);
|
|
if (flags.initrace >= 0)
|
|
currrace = flags.initrace;
|
|
else
|
|
currrace = randrace(currrole);
|
|
|
|
/* Initialize the gender popup menu */
|
|
if (!(askmenu[RSRC_ASK_GEND] = NewMenu(RSRC_ASK_GEND, "\p")))
|
|
fatal("\pCannot create gender menu");
|
|
for (i = 0; i < ROLE_GENDERS; i++) {
|
|
ask_restring(genders[i].adj, str);
|
|
AppendMenu(askmenu[RSRC_ASK_GEND], str);
|
|
}
|
|
InsertMenu(askmenu[RSRC_ASK_GEND], hierMenu);
|
|
if (flags.initgend >= 0)
|
|
currgend = flags.initgend;
|
|
else if (flags.female)
|
|
currgend = 1;
|
|
else
|
|
currgend = randgend(currrole, currrace);
|
|
|
|
/* Initialize the alignment popup menu */
|
|
if (!(askmenu[RSRC_ASK_ALIGN] = NewMenu(RSRC_ASK_ALIGN, "\p")))
|
|
fatal("\pCannot create alignment menu");
|
|
for (i = 0; i < ROLE_ALIGNS; i++) {
|
|
ask_restring(aligns[i].adj, str);
|
|
AppendMenu(askmenu[RSRC_ASK_ALIGN], str);
|
|
}
|
|
InsertMenu(askmenu[RSRC_ASK_ALIGN], hierMenu);
|
|
if (flags.initalign >= 0)
|
|
curralign = flags.initalign;
|
|
else
|
|
curralign = randalign(currrole, currrace);
|
|
|
|
/* Initialize the mode popup menu */
|
|
if (!(askmenu[RSRC_ASK_MODE] = NewMenu(RSRC_ASK_MODE, "\p")))
|
|
fatal("\pCannot create mode menu");
|
|
AppendMenu(askmenu[RSRC_ASK_MODE], "\pNormal");
|
|
AppendMenu(askmenu[RSRC_ASK_MODE], "\pExplore");
|
|
AppendMenu(askmenu[RSRC_ASK_MODE], "\pDebug");
|
|
InsertMenu(askmenu[RSRC_ASK_MODE], hierMenu);
|
|
currmode = 0;
|
|
|
|
/* Set the redraw procedures */
|
|
for (item = RSRC_ASK_DEFAULT; item <= RSRC_ASK_MODE; item++) {
|
|
GetDialogItem(askdialog, item, &type, &handle, &rect);
|
|
SetDialogItem(askdialog, item, type, (Handle) redraw, &rect);
|
|
}
|
|
|
|
/* Handle dialog events */
|
|
do {
|
|
/* Adjust the Play button */
|
|
ask_enable(askdialog, RSRC_ASK_PLAY,
|
|
GetDialogTextEditHandle(askdialog)[0]->teLength);
|
|
|
|
/* Adjust the race popup menu */
|
|
i = j = currrace;
|
|
do {
|
|
if (validrace(currrole, j)) {
|
|
EnableMenuItem(askmenu[RSRC_ASK_RACE], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_RACE], j + 1, currrace == j);
|
|
} else {
|
|
DisableMenuItem(askmenu[RSRC_ASK_RACE], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_RACE], j + 1, FALSE);
|
|
if ((currrace == j) && !races[++currrace].noun)
|
|
currrace = 0;
|
|
}
|
|
if (!races[++j].noun)
|
|
j = 0;
|
|
} while (i != j);
|
|
if (currrace != i) {
|
|
GetDialogItem(askdialog, RSRC_ASK_RACE, &type, &handle, &rect);
|
|
InvalWindowRect(GetDialogWindow(askdialog), &rect);
|
|
}
|
|
|
|
/* Adjust the gender popup menu */
|
|
i = j = currgend;
|
|
do {
|
|
if (validgend(currrole, currrace, j)) {
|
|
EnableMenuItem(askmenu[RSRC_ASK_GEND], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_GEND], j + 1, currgend == j);
|
|
} else {
|
|
DisableMenuItem(askmenu[RSRC_ASK_GEND], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_GEND], j + 1, FALSE);
|
|
if ((currgend == j) && (++currgend >= ROLE_GENDERS))
|
|
currgend = 0;
|
|
}
|
|
if (++j >= ROLE_GENDERS)
|
|
j = 0;
|
|
} while (i != j);
|
|
if (currgend != i) {
|
|
GetDialogItem(askdialog, RSRC_ASK_GEND, &type, &handle, &rect);
|
|
InvalWindowRect(GetDialogWindow(askdialog), &rect);
|
|
}
|
|
|
|
/* Adjust the alignment popup menu */
|
|
i = j = curralign;
|
|
do {
|
|
if (validalign(currrole, currrace, j)) {
|
|
EnableMenuItem(askmenu[RSRC_ASK_ALIGN], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j + 1, curralign == j);
|
|
} else {
|
|
DisableMenuItem(askmenu[RSRC_ASK_ALIGN], j + 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_ALIGN], j + 1, FALSE);
|
|
if ((curralign == j) && (++curralign >= ROLE_ALIGNS))
|
|
curralign = 0;
|
|
}
|
|
if (++j >= ROLE_ALIGNS)
|
|
j = 0;
|
|
} while (i != j);
|
|
if (curralign != i) {
|
|
GetDialogItem(askdialog, RSRC_ASK_ALIGN, &type, &handle, &rect);
|
|
InvalWindowRect(GetDialogWindow(askdialog), &rect);
|
|
}
|
|
|
|
/* Adjust the role popup menu */
|
|
for (i = 0; roles[i].name.m; i++) {
|
|
ask_restring((currgend && roles[i].name.f) ? roles[i].name.f
|
|
: roles[i].name.m,
|
|
str);
|
|
SetMenuItemText(askmenu[RSRC_ASK_ROLE], i + 1, str);
|
|
CheckMenuItem(askmenu[RSRC_ASK_ROLE], i + 1, currrole == i);
|
|
}
|
|
|
|
/* Adjust the mode popup menu */
|
|
CheckMenuItem(askmenu[RSRC_ASK_MODE], 1, currmode == 0);
|
|
CheckMenuItem(askmenu[RSRC_ASK_MODE], 2, currmode == 1);
|
|
CheckMenuItem(askmenu[RSRC_ASK_MODE], 3, currmode == 2);
|
|
|
|
/* Wait for an action on an item */
|
|
ModalDialog(filter, &item);
|
|
switch (item) {
|
|
case RSRC_ASK_PLAY:
|
|
break;
|
|
case RSRC_ASK_QUIT:
|
|
currmode = -1;
|
|
break;
|
|
case RSRC_ASK_ROLE:
|
|
case RSRC_ASK_RACE:
|
|
case RSRC_ASK_ALIGN:
|
|
case RSRC_ASK_GEND:
|
|
case RSRC_ASK_MODE:
|
|
GetDialogItem(askdialog, item, &type, &handle, &rect);
|
|
pt = *(Point *) ▭
|
|
LocalToGlobal(&pt);
|
|
if (!!(i = PopUpMenuSelect(askmenu[item], pt.v, pt.h,
|
|
askselect[item] + 1)))
|
|
askselect[item] = LoWord(i) - 1;
|
|
InvalWindowRect(GetDialogWindow(askdialog), &rect);
|
|
break;
|
|
case RSRC_ASK_NAME:
|
|
#if 0
|
|
/* limit the data here to 25 chars */
|
|
{
|
|
short beepTEDelete = 1;
|
|
|
|
while ((**dRec.textH).teLength > 25)
|
|
{
|
|
if (beepTEDelete++ <= 3)
|
|
SysBeep(3);
|
|
TEKey('\b', dRec.textH);
|
|
}
|
|
}
|
|
|
|
/* special case filter (that doesn't plug all the holes!) */
|
|
if (((**dRec.textH).teLength == 1) && (**((**dRec.textH).hText) < 32))
|
|
TEKey('\b', dRec.textH);
|
|
#endif
|
|
break;
|
|
}
|
|
} while ((item != RSRC_ASK_PLAY) && (item != RSRC_ASK_QUIT));
|
|
|
|
/* Process the name */
|
|
GetDialogItem(askdialog, RSRC_ASK_NAME, &type, &handle, &rect);
|
|
GetDialogItemText(handle, str);
|
|
if (str[0] > PL_NSIZ - 1)
|
|
str[0] = PL_NSIZ - 1;
|
|
BlockMove(&str[1], plname, str[0]);
|
|
plname[str[0]] = '\0';
|
|
|
|
/* Destroy the dialog */
|
|
for (i = RSRC_ASK_ROLE; i <= RSRC_ASK_MODE; i++) {
|
|
DeleteMenu(i);
|
|
DisposeMenu(askmenu[i]);
|
|
}
|
|
SetPort(oldport);
|
|
DisposeDialog(askdialog);
|
|
DisposeModalFilterUPP(filter);
|
|
DisposeUserItemUPP(redraw);
|
|
|
|
/* Process the mode */
|
|
wizard = discover = 0;
|
|
switch (currmode) {
|
|
case 0: /* Normal */
|
|
break;
|
|
case 1: /* Explore */
|
|
discover = 1;
|
|
break;
|
|
case 2: /* Debug */
|
|
wizard = 1;
|
|
strcpy(plname, WIZARD_NAME);
|
|
break;
|
|
default: /* Quit */
|
|
ExitToShell();
|
|
}
|
|
|
|
/* Process the role */
|
|
strcpy(pl_character, roles[currrole].name.m);
|
|
flags.initrole = currrole;
|
|
|
|
/* Process the race */
|
|
flags.initrace = currrace;
|
|
|
|
/* Process the gender */
|
|
flags.female = flags.initgend = currgend;
|
|
|
|
/* Process the alignment */
|
|
flags.initalign = curralign;
|
|
|
|
return;
|
|
}
|
|
|
|
/*** Menu bar routines ***/
|
|
|
|
#if !TARGET_API_MAC_CARBON
|
|
static void
|
|
alignAD(Rect *pRct, short vExempt)
|
|
{
|
|
BitMap qbitmap;
|
|
|
|
GetQDGlobalsScreenBits(&qbitmap);
|
|
(*pRct).right -= (*pRct).left; /* width */
|
|
(*pRct).bottom -= (*pRct).top; /* height */
|
|
(*pRct).left = (qbitmap.bounds.right - (*pRct).right) / 2;
|
|
(*pRct).top = (qbitmap.bounds.bottom - (*pRct).bottom - vExempt) / 2;
|
|
(*pRct).top += vExempt;
|
|
(*pRct).right += (*pRct).left;
|
|
(*pRct).bottom += (*pRct).top;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
mustGetMenuAlerts()
|
|
{
|
|
short i;
|
|
Rect **hRct;
|
|
|
|
for (i = alrt_Menu_start; i < alrt_Menu_limit; i++) {
|
|
if (!(hRct = (Rect **) GetResource('ALRT', i))) /* AlertTHndl */
|
|
{
|
|
for (i = 0; i < beepMenuAlertErr; i++)
|
|
SysBeep(3);
|
|
ExitToShell();
|
|
}
|
|
|
|
#if !TARGET_API_MAC_CARBON
|
|
alignAD(*hRct, GetMBarHeight());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void
|
|
menuError(short menuErr)
|
|
{
|
|
short i;
|
|
|
|
for (i = 0; i < beepMenuAlertErr; i++)
|
|
SysBeep(3);
|
|
|
|
ParamText(menuErrStr[menuErr], "\p", "\p", "\p");
|
|
(void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
|
|
|
|
ExitToShell();
|
|
}
|
|
|
|
void
|
|
InitMenuRes()
|
|
{
|
|
static Boolean was_inited = 0;
|
|
short i, j;
|
|
menuListHandle mlHnd;
|
|
MenuHandle menu;
|
|
|
|
if (was_inited)
|
|
return;
|
|
was_inited = 1;
|
|
|
|
mustGetMenuAlerts();
|
|
|
|
for (i = listMenubar; i <= listSubmenu; i++) {
|
|
if (!(mlHnd =
|
|
(menuListHandle) GetResource('MNU#', (menuBarListID + i))))
|
|
menuError(errGetMenuList);
|
|
|
|
pMenuList[i] = (menuListPtr) NewPtr(GetHandleSize((Handle) mlHnd));
|
|
*pMenuList[i] = **mlHnd;
|
|
|
|
for (j = 0; j < pMenuList[i]->numMenus; j++) {
|
|
if (!(menu = (MenuHandle) GetMenu((**mlHnd).mref[j].mresID))) {
|
|
Str31 d;
|
|
NumToString((**mlHnd).mref[j].mresID, d);
|
|
menuError(errGetMenu);
|
|
}
|
|
|
|
pMenuList[i]->mref[j].mhnd = menu;
|
|
SetMenuID(menu, j + (**mlHnd).firstMenuID); /* consecutive IDs */
|
|
|
|
/* expand apple menu */
|
|
if ((i == listMenubar) && (j == menuApple)) {
|
|
AppendResMenu(menu, 'DRVR');
|
|
}
|
|
|
|
InsertMenu(menu, ((i == listSubmenu) ? hierMenu : 0));
|
|
}
|
|
}
|
|
DrawMenuBar();
|
|
return;
|
|
}
|
|
|
|
void
|
|
AdjustMenus(short dimMenubar)
|
|
{
|
|
short newMenubar = mbarRegular;
|
|
WindowRef win = FrontWindow();
|
|
short i;
|
|
|
|
/*
|
|
* if (windowprocs != mac_procs) {
|
|
* return;
|
|
* }
|
|
*/
|
|
/* determine the new menubar state */
|
|
if (dimMenubar)
|
|
newMenubar = mbarDim;
|
|
else if (!win)
|
|
newMenubar = mbarNoWindows;
|
|
else if (GetWindowKind(win) < 0)
|
|
newMenubar = mbarDA;
|
|
else if (!IsWindowVisible(_mt_window))
|
|
newMenubar = mbarNoMap;
|
|
|
|
if (newMenubar != mbarRegular)
|
|
; /* we've already found its state */
|
|
else if (wizard) {
|
|
newMenubar = mbarSpecial;
|
|
|
|
if (kAdjustWizardMenu) {
|
|
kAdjustWizardMenu = 0;
|
|
|
|
SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pDebug");
|
|
}
|
|
}
|
|
|
|
else if (discover) {
|
|
newMenubar = mbarSpecial;
|
|
|
|
if (kAdjustWizardMenu) {
|
|
kAdjustWizardMenu = 0;
|
|
|
|
SetMenuItemText(MHND_FILE, menuFilePlayMode, "\pExplore");
|
|
|
|
for (i = CountMenuItems(MHND_WIZ); i > menuWizardAttributes; i--)
|
|
DeleteMenuItem(MHND_WIZ, i);
|
|
}
|
|
}
|
|
|
|
/* adjust the menubar, if there's a state change */
|
|
if (theMenubar != newMenubar) {
|
|
switch (theMenubar = newMenubar) {
|
|
case mbarDim:
|
|
/* disable all menus (except the apple menu) */
|
|
for (i = menuFile; i < NUM_MBAR; i++)
|
|
DisableMenuItem(MBARHND(i), 0);
|
|
break;
|
|
|
|
case mbarNoWindows:
|
|
case mbarDA:
|
|
case mbarNoMap:
|
|
/* enable the file menu, but ... */
|
|
EnableMenuItem(MHND_FILE, 0);
|
|
|
|
/* ... disable the window commands! */
|
|
for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
|
|
DisableMenuItem(MHND_FILE, i);
|
|
|
|
/* ... and disable the rest of the menus */
|
|
for (i = menuEdit; i < NUM_MBAR; i++)
|
|
DisableMenuItem(MBARHND(i), 0);
|
|
|
|
if (theMenubar == mbarDA)
|
|
EnableMenuItem(MHND_EDIT, 0);
|
|
|
|
break;
|
|
|
|
case mbarRegular:
|
|
case mbarSpecial:
|
|
/* enable all menus ... */
|
|
for (i = menuFile; i < NUM_MBAR; i++)
|
|
EnableMenuItem(MBARHND(i), 0);
|
|
|
|
/* ... except the unused Edit menu */
|
|
DisableMenuItem(MHND_EDIT, 0);
|
|
|
|
/* ... enable the window commands */
|
|
for (i = menuFileRedraw; i <= menuFileEnterExplore; i++)
|
|
EnableMenuItem(MHND_FILE, i);
|
|
|
|
if (theMenubar == mbarRegular)
|
|
DisableMenuItem(MHND_FILE, menuFilePlayMode);
|
|
else
|
|
DisableMenuItem(MHND_FILE, menuFileEnterExplore);
|
|
|
|
break;
|
|
}
|
|
|
|
DrawMenuBar();
|
|
}
|
|
}
|
|
|
|
void
|
|
DoMenuEvt(long menuEntry)
|
|
{
|
|
short menuID = HiWord(menuEntry);
|
|
short menuItem = LoWord(menuEntry);
|
|
|
|
switch (menuID - ID1_MBAR) /* all submenus are default case */
|
|
{
|
|
case menuApple:
|
|
if (menuItem == menuAppleAboutBox)
|
|
aboutNetHack();
|
|
#if !TARGET_API_MAC_CARBON
|
|
else {
|
|
unsigned char daName[32];
|
|
|
|
GetMenuItemText(MHND_APPLE, menuItem, *(Str255 *) daName);
|
|
(void) OpenDeskAcc(daName);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
/*
|
|
* Those direct calls are ugly: they should be installed into cmd.c .
|
|
* Those AddToKeyQueue() calls are also ugly: they should be put into
|
|
* the 'STR#' resource.
|
|
*/
|
|
case menuFile:
|
|
switch (menuItem) {
|
|
case menuFileRedraw:
|
|
AddToKeyQueue('R' & 0x1f, 1);
|
|
break;
|
|
|
|
case menuFilePrevMsg:
|
|
AddToKeyQueue('P' & 0x1f, 1);
|
|
break;
|
|
|
|
case menuFileCleanup:
|
|
(void) SanePositions();
|
|
break;
|
|
|
|
case menuFileEnterExplore:
|
|
AddToKeyQueue('X', 1);
|
|
break;
|
|
|
|
case menuFileSave:
|
|
askSave();
|
|
break;
|
|
|
|
case menuFileQuit:
|
|
askQuit();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case menuEdit:
|
|
#if !TARGET_API_MAC_CARBON
|
|
(void) SystemEdit(menuItem - 1);
|
|
#endif
|
|
break;
|
|
|
|
default: /* get associated string and add to key queue */
|
|
{
|
|
Str255 mstr;
|
|
short i;
|
|
|
|
GetIndString(mstr, menuID, menuItem);
|
|
if (mstr[0] > QUEUE_LEN)
|
|
mstr[0] = QUEUE_LEN;
|
|
|
|
for (i = 1; ((i <= mstr[0]) && (mstr[i] != mstrEndChar)); i++)
|
|
AddToKeyQueue(mstr[i], false);
|
|
} break;
|
|
}
|
|
|
|
HiliteMenu(0);
|
|
}
|
|
|
|
static void
|
|
aboutNetHack()
|
|
{
|
|
if (theMenubar >= mbarRegular) {
|
|
(void) doversion(); /* is this necessary? */
|
|
} else {
|
|
unsigned char aboutStr[32] = "\pNetHack 3.4.";
|
|
|
|
if (PATCHLEVEL > 10) {
|
|
aboutStr[++aboutStr[0]] = '0' + PATCHLEVEL / 10;
|
|
}
|
|
|
|
aboutStr[++aboutStr[0]] = '0' + (PATCHLEVEL % 10);
|
|
|
|
ParamText(aboutStr, "\p\rdevteam@www.nethack.org", "\p", "\p");
|
|
(void) Alert(alrtMenuNote, (ModalFilterUPP) 0L);
|
|
ResetAlertStage();
|
|
}
|
|
}
|
|
|
|
static void
|
|
askSave()
|
|
{
|
|
Boolean doSave = 1;
|
|
Boolean doYes = 0;
|
|
|
|
if (theMenubar < mbarRegular) {
|
|
short itemHit;
|
|
|
|
ParamText("\pReally Save?", "\p", "\p", "\p");
|
|
itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
|
|
ResetAlertStage();
|
|
|
|
if (itemHit != bttnMenuAlertYes) {
|
|
doSave = 0;
|
|
} else {
|
|
doYes = 1;
|
|
}
|
|
}
|
|
if (doSave) {
|
|
AddToKeyQueue('S', 1);
|
|
if (doYes) {
|
|
AddToKeyQueue('y', 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
askQuit()
|
|
{
|
|
Boolean doQuit = 1;
|
|
Boolean doYes = 0;
|
|
Boolean winMac;
|
|
char *quitinput;
|
|
|
|
if (!strcmp(windowprocs.name, "mac"))
|
|
winMac = 1;
|
|
else
|
|
winMac = 0;
|
|
|
|
if (theMenubar < mbarRegular) {
|
|
short itemHit;
|
|
|
|
ParamText("\pReally Quit?", "\p", "\p", "\p");
|
|
itemHit = Alert(alrtMenu_NY, (ModalFilterUPP) 0L);
|
|
ResetAlertStage();
|
|
|
|
if (itemHit != bttnMenuAlertYes) {
|
|
doQuit = 0;
|
|
} else {
|
|
doYes = 1;
|
|
}
|
|
}
|
|
if (doQuit) {
|
|
/* MWM -- forgive me lord, an even uglier kludge to deal with
|
|
differences
|
|
in command input handling
|
|
*/
|
|
if (winMac)
|
|
quitinput = "#quit\r";
|
|
else
|
|
quitinput = "#q\r";
|
|
|
|
/* KMH -- Ugly kludge */
|
|
while (*quitinput)
|
|
AddToKeyQueue(*quitinput++, 1);
|
|
if (doYes) {
|
|
if (winMac)
|
|
quitinput = "y\rq\r\r\r";
|
|
else
|
|
quitinput = "yq\r";
|
|
while (*quitinput)
|
|
AddToKeyQueue(*quitinput++, 1);
|
|
}
|
|
}
|
|
}
|