1188 lines
29 KiB
C
1188 lines
29 KiB
C
/* NetHack 3.5 macmenu.c $NHDT-Date$ $NHDT-Branch$:$NHDT-Revision$ */
|
|
/* NetHack 3.5 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);
|
|
}
|
|
}
|
|
}
|
|
|