diff --git a/sys/mac/mmodal.c b/sys/mac/mmodal.c new file mode 100644 index 000000000..33d6b7535 --- /dev/null +++ b/sys/mac/mmodal.c @@ -0,0 +1,662 @@ +/* SCCS Id: @(#)mmodal.c 3.1 93/01/24 */ +/* Copyright (c) Jon W{tte, Hao-Yang Wang, Jonathan Handler 1992. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include +#if ENABLE_MAC_POPUP +#include "hack.h" +#include "mactty.h" +#endif +#include "macpopup.h" + +/* Flash a dialog button when its accelerator key is pressed */ +void +FlashButton (WindowPtr wind, short item) { + short type; + Handle handle; + Rect rect; + unsigned long ticks; + + /* Apple recommends 8 ticks */ + GetDItem(wind, item, &type, &handle, &rect); + HiliteControl((ControlHandle)handle, kControlButtonPart); + Delay(8, &ticks); + HiliteControl((ControlHandle)handle, 0); + return; +} + + +#if ENABLE_MAC_POPUP +static void mv_handle_click (EventRecord *theEvent); + +#define MAX_MV_DIALOGS 20 +static int old_dialog_count = 0; +static struct { + short id; + Boolean init_visible; + DialogPtr dialog; +} old_dialog[MAX_MV_DIALOGS]; + + +static short frame_corner; +static pascal void FrameItem(DialogPtr dlog, short item); +static UserItemUPP FrameItemUPP = NULL; + +static pascal void +FrameItem (DialogPtr dlog, short item) { + short k; + Handle h; + Rect r; + + GetDItem (dlog, item, &k, &h, &r); + PenSize (3, 3); + FrameRoundRect (&r, frame_corner, frame_corner); + PenNormal (); +} + + +static void +SetFrameItem (DialogPtr dlog, short frame, short item) { + Rect r, r2; + short kind; + Handle h; + + if (!FrameItemUPP) /* initialize handler routine */ + FrameItemUPP = NewUserItemProc(FrameItem); + + GetDItem (dlog, item, &kind, &h, &r); + InsetRect (&r, -4, -4); + r2 = r; + GetDItem (dlog, frame, &kind, &h, &r); + SetDItem (dlog, frame, kind, (Handle) FrameItemUPP, &r2); + frame_corner = 16; +} + + +/* Instead of calling GetNewDialog everytime, just call + SelectWindow/ShowWindow for the old dialog to remember its location. +*/ +/* + * Unfortunately, this does not work, as it doesn't handle old text + * in edit text boxes, and not ParamText parameters either. + * + */ +static DialogPtr +mv_get_new_dialog(short dialogID) { + DialogPtr dialog; + int d_idx = old_dialog_count; + Rect oldRect; + Boolean hadOld = 0; + + old_dialog[0].id = dialogID; + while (old_dialog[d_idx].id != dialogID) + --d_idx; + +/* + * This routine modified so that the old dialog is + * disposed, and the new one read in after we remember + * the old dialog's position. + * + * This takes care of strange default strings and ParamTexts + * + */ + + if (d_idx) { + dialog = old_dialog [d_idx] . dialog; + oldRect = dialog->portBits . bounds; + DisposeDialog (dialog); + old_dialog [d_idx] . dialog = (DialogPtr) 0; + hadOld = 1; + + } else { + d_idx = ++ old_dialog_count; + } + + dialog = GetNewDialog(dialogID, nil, (WindowPtr)-1); + if (dialog) { + if (hadOld) { + MoveWindow (dialog, - oldRect . left, - oldRect . top, FALSE); + } + old_dialog[d_idx].id = dialogID; + old_dialog[d_idx].init_visible + = ((WindowPeek)dialog)->visible; + old_dialog[d_idx].dialog = dialog; + } + return dialog; +} + +/* Instead of actually closing the dialog, just hide it so its location + is remembered. */ +static void mv_close_dialog(DialogPtr dialog) { + HideWindow(dialog); +} + +/* This routine is stolen/borrowed from HandleClick (macwin.c). See the + comments in mv_modal_dialog for more information. */ +static void +mv_handle_click (EventRecord *theEvent) { + int code; + WindowPtr theWindow; + Rect r = (*GetGrayRgn ())->rgnBBox; + + InsetRect (&r, 4, 4); + InitCursor (); + + code = FindWindow (theEvent->where, &theWindow); + + switch (code) { + case inContent : + if (theWindow != FrontWindow ()) { + nhbell (); + } + break; + case inDrag : + SetCursor(&qd.arrow); + DragWindow (theWindow, theEvent->where, &r); + SaveWindowPos (theWindow); + break; + default : + HandleEvent (theEvent); + } +} + +static void +mv_modal_dialog(ModalFilterProcPtr filterProc, short *itemHit) { + GrafPtr org_port; + GetPort(&org_port); + + for (;;) { + DialogPtr dialog = FrontWindow(); + EventRecord evt; + + WaitNextEvent(everyEvent, &evt, GetCaretTime(), (RgnHandle) nil); + + if (evt.what == keyDown) + if (evt.modifiers &cmdKey) { + if ((evt.message & charCodeMask) == '.') { + /* 0x351b is the key code and character code of the esc key. */ + evt.message = 0x351b; + evt.modifiers &= ~cmdKey; + } + } else + trans_num_keys(&evt); + + if (filterProc) { + if ((*filterProc)(dialog, &evt, itemHit)) + break; + } else if (evt.what == keyDown) { + char ch = evt.message & charCodeMask; + if (ch == CHAR_CR || ch == CHAR_ENTER) { + *itemHit = ok; + FlashButton(dialog, ok); + break; + } + } + + if (IsDialogEvent(&evt)) { + DialogPtr dont_care; + if (DialogSelect(&evt, &dont_care, itemHit)) + break; + + /* The following part is problemmatic: (1) Calling HandleEvent + here may cause some re-entrance problem (seems ok, but I am + not sure). (2) It is ugly to treat mouseDown events as a + special case. If we can just say "else HandleEvent(&evt);" + here it will be better. */ + } else if (evt.what == mouseDown) + mv_handle_click(&evt); + else + HandleEvent(&evt); + + SetPort(org_port); + } +} + +/********************************************************************************* + * mactopl routines using dialogs + *********************************************************************************/ + +#define YN_DLOG 133 +#define YNQ_DLOG 134 +#define YNAQ_DLOG 135 +#define YNNAQ_DLOG 136 + +static int yn_user_item [] = {5, 6, 7, 8}; +static short gEnterItem, gEscItem; +static const char *gRespStr = (const char *)0; +static char gDef = 0; +static short dlogID; + + +static void +SetEnterItem (DialogPtr dp, const short newEnterItem) { + short kind; + Handle item; + Rect r, r2; + + if (gEnterItem != newEnterItem) { + GetDItem (dp, gEnterItem, &kind, &item, &r2); + InsetRect (&r2, - 4, - 4); + EraseRect (&r2); + InvalRect (&r2); + + gEnterItem = newEnterItem; + + GetDItem (dp, newEnterItem, &kind, &item, &r2); + frame_corner = kind == ctrlItem + btnCtrl ? 16 : 0; + InsetRect (&r2, - 4, - 4); + InvalRect (&r2); + r = r2; + GetDItem (dp, yn_user_item [dlogID - YN_DLOG], &kind, &item, &r2); + SetDItem (dp, yn_user_item [dlogID - YN_DLOG], kind, item, &r); + } +} + + +static void +do_tabbing (DialogPtr dp) { + SetEnterItem(dp, gEnterItem == 1 ? strlen(gRespStr) : gEnterItem - 1); +} + + +static void +set_yn_number(DialogPtr dp) { + if (gRespStr && gRespStr[gEnterItem-1] == '#') { + short k; + Handle h; + Rect r; + Str255 s; + GetDItem(dp, gEnterItem, &k, &h, &r); + GetIText(h, s); + if (s[0]) + StringToNum(s, &yn_number); + } +} + + +static pascal Boolean +YNAQFilter (DialogPtr dp, EventRecord *ev, short *itemHit) { + unsigned char code; + char ch; + char *re = (char *) gRespStr; + + if (ev->what != keyDown) { + + return 0; + } + code = (ev->message & 0xff00) >> 8; + ch = ev->message & 0xff; + + switch (code) { + case 0x24 : + case 0x4c : + set_yn_number (dp); + *itemHit = gEnterItem; + FlashButton (dp, *itemHit); + return 1; + case 0x35 : + case 0x47 : + *itemHit = gEscItem; + FlashButton (dp, *itemHit); + return 1; + case 0x30 : + do_tabbing (dp); + return 0; + } + switch (ch) { + case '\r' : + case '\n' : + case ' ' : + case 3 : + set_yn_number (dp); + *itemHit = gEnterItem; + FlashButton (dp, *itemHit); + return 1; + + case 9 : + do_tabbing (dp); + return 0; + + case 27 : + *itemHit = gEscItem; + FlashButton (dp, *itemHit); + return 1; + + case CHAR_BS : + case 28 : case 29 : case 30 : case 31 : /* the four arrow keys */ + case '0' : case '1' : case '2' : case '3' : case '4' : + case '5' : case '6' : case '7' : case '8' : case '9' : { + char *loc = strchr (gRespStr, '#'); + if (loc) { + SetEnterItem(dp, loc - gRespStr + 1); + return 0; /* Dialog Manager will then put this key into the text field. */ + } + } + } + + while (*re) { + if (*re == ch) { + *itemHit = (re - gRespStr) + 1; + FlashButton (dp, *itemHit); + return 1; + } + re ++; + } + + nhbell (); + ev->what = nullEvent; + return 0; +} + + +static char +do_question_dialog (char *query, int dlog, int defbut, char *resp) { + Str255 p; + DialogPtr dp; + short item; + + char c = queued_resp ((char *) resp); + if (c) + return c; + + dlogID = dlog; + C2P (query, p); + ParamText ((char *)p, (uchar *) 0, (uchar *) 0, (uchar *) 0); + dp = mv_get_new_dialog (dlog); + if (! dp) { + return 0; + } + SetPort (dp); + ShowWindow (dp); + + gEscItem = strlen (resp); + gEnterItem = defbut; + gRespStr = resp; + + SetFrameItem (dp, yn_user_item [dlogID - YN_DLOG], gEnterItem); + + InitCursor (); + mv_modal_dialog (YNAQFilter, &item); + mv_close_dialog (dp); + return resp [item - 1]; +} + + +static pascal Boolean +OneCharDLOGFilter (DialogPtr dp, EventRecord *ev, short *item) { + char ch; + short k; + Handle h; + Rect r; + unsigned char com [2]; + + if (ev->what != keyDown) { + return 0; + } + ch = ev->message & 0xff; + + com [0] = 1; + com [1] = ch; + + if (ch == 27) { + GetDItem (dp, 4, &k, &h, &r); + SetIText (h, com); + *item = 2; + FlashButton (dp, 2); + return 1; + } + if (! gRespStr || strchr (gRespStr, ch)) { + GetDItem (dp, 4, &k, &h, &r); + SetIText (h, com); + *item = 1; + FlashButton (dp, 1); + return 1; + } + if (ch == 10 || ch == 13 || ch == 3 || ch == 32) { + com [1] = gDef; + GetDItem (dp, 4, &k, &h, &r); + SetIText (h, com); + *item = 1; + FlashButton (dp, 1); + return 1; + } + if (ch > 32 && ch < 127) { + GetDItem (dp, 4, &k, &h, &r); + SetIText (h, com); + *item = 1; + FlashButton (dp, 1); + return 1; + } + nhbell (); + ev->what = nullEvent; + return 1; +} + + +static char +generic_yn_function (query, resp, def) +const char *query, *resp; +char def; +{ + DialogPtr dp; + short k, item; + Handle h; + Rect r; + unsigned char com [32] = {1, 27}; // margin for getitext + Str255 pQuery; + + char c = queued_resp ((char *) resp); + if (c) + return c; + + dp = mv_get_new_dialog (137); + if (! dp) { + return 0; + } + SetPort (dp); + ShowWindow (dp); + InitCursor (); + SetFrameItem (dp, 6, 1); + if (def) { + com [1] = def; + } + strcpy ((char *) &pQuery[1], query); + if (resp && *resp) { + strcat ((char *) &pQuery[1], " ("); + strcat ((char *) &pQuery[1], resp); + strcat ((char *) &pQuery[1], ")"); + } + pQuery[0] = strlen (&pQuery[1]); + ParamText ((char *) pQuery, (uchar *) 0, (uchar *) 0, (uchar *) 0); + GetDItem (dp, 4, &k, &h, &r); + SetIText (h, com); + SelIText (dp, 4, 0, 0x7fff); + InitCursor (); + SetFrameItem (dp, 6, 1); + gRespStr = resp; + gDef = def; + do { + mv_modal_dialog (OneCharDLOGFilter, &item); + + } while (item != 1 && item != 2); + GetIText (h, com); + + mv_close_dialog (dp); + if (item == 2 || ! com [0]) { + return 27; // escape + } + return com [1]; +} + + +static char +ynaq_dialog (query, resp, def) +const char *query, *resp; +char def; +{ + int dia = 0; + + if (resp) { + if (! strcmp (resp, ynchars)) { + dia = YN_DLOG; + } + if (! strcmp (resp, ynqchars)) { + dia = YNQ_DLOG; + } + if (! strcmp (resp, ynaqchars)) { + dia = YNAQ_DLOG; + } + if (! strcmp (resp, ynNaqchars)) { + dia = YNNAQ_DLOG; + } + } + if (! dia) { + return generic_yn_function (query, resp, def); + } + + return do_question_dialog ((char *) query, dia , + (strchr (resp, def) - resp) + 1, (char *) resp); +} + + +char +popup_yn_function(const char *query, const char *resp, char def) { + char ch; + + if (ch = ynaq_dialog (query, resp, def)) + return ch; + + return topl_yn_function(query, resp, def); +} + + +/********************************************************************************* + * mgetline routines using dialogs + *********************************************************************************/ + +static pascal Boolean +getlinFilter (DialogPtr dp, EventRecord *ev, short *itemHit) { + if (ev->what == keyDown) { + int key = ev->message & keyCodeMask, + ch = ev->message & charCodeMask; + + if (ch == 0x1b || key == 0x3500 || key == 0x4700) { + *itemHit = 2; + FlashButton(dp, 2); + return true; + } else if (ch == CHAR_CR || ch == CHAR_ENTER) { + *itemHit = 1; + FlashButton(dp, 1); + return true; + } + } + return false; +} + + + +static Boolean +ExtendedCommandDialogFilter (DialogPtr dp, EventRecord *ev, short *item) { + int ix; + Handle h; + Rect r; + short k; + Str255 s; + unsigned char com [2]; + + if (ev->what != keyDown) { + return 0; + } + com [0] = 1; + com [1] = ev->message & 0xff; + + if (com [1] == 10 || com [1] == 13 || com [1] == 32 || + com [1] == 3) { // various "OK" + *item = 1; + FlashButton (dp, 1); + return 1; + } + if (com [1] == 27 || (ev->message & 0xff00 == 0x3500)) { // escape + *item = 2; + FlashButton (dp, 2); + return 1; + } + for (ix = 3; ix; ix ++) { + h = (Handle) 0; + k = 0; + GetDItem (dp, ix, &k, &h, &r); + if (! k || ! h) { + return 0; + } + if (k == 6) { // Radio Button Item + GetCTitle ((ControlHandle) h, s); + s [0] = 1; + if (! IUEqualString (com, s)) { + *item = ix; + return 1; + } + } + } +/*NOTREACHED*/ + return 0; +} + + +void +popup_getlin (const char *query, char *bufp) { + ControlHandle ctrl; + DialogPtr promptDialog; + short itemHit, type; + Rect box; + Str255 pasStr; + + if (get_line_from_key_queue (bufp)) + return; + + /* + ** Make a copy of the prompt string and convert the copy to a Pascal string. + */ + + C2P(query, pasStr); + + /* + ** Set the query line as parameter text. + */ + + ParamText(pasStr, "\p", "\p", "\p"); + + promptDialog = mv_get_new_dialog(130); + ShowWindow(promptDialog); + + InitCursor (); + SetFrameItem (promptDialog, 6, 1); + do { + mv_modal_dialog(&getlinFilter, &itemHit); + } while ((itemHit != 1) && (itemHit != 2)); + + if (itemHit != 2) { + /* + ** Get the text from the text edit item. + */ + + GetDItem(promptDialog, 4, &type, (Handle *) &ctrl, &box); + GetIText((Handle) ctrl, pasStr); + + /* + ** Convert it to a 'C' string and copy it into the return value. + */ + + P2C (pasStr, bufp); + } else { + /* + ** Return a null-terminated string consisting of a single . + */ + + bufp[0] = '\033'; + bufp[1] = '\0'; + } + + mv_close_dialog(promptDialog); +} + +#endif /* ENABLE_MAC_POPUP */