/* NetHack 3.6 macmenu.c $NHDT-Date: 1432512797 2015/05/25 00:13:17 $ $NHDT-Branch: master $:$NHDT-Revision: 1.13 $ */ /* 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 #include #include #include #include #include #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(FALSE); /* 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); } } }