/* NetHack 3.7 winmisc.c $NHDT-Date: 1596498374 2020/08/03 23:46:14 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.49 $ */ /* Copyright (c) Dean Luick, 1992 */ /* NetHack may be freely redistributed. See license for details. */ /* * Misc. popup windows: player selection and extended commands. * * + Global functions: player_selection() and get_ext_cmd(). */ #ifndef SYSV #define PRESERVE_NO_SYSV /* X11 include files may define SYSV */ #endif #include #include #include #include #include #include #include #include #include #include #include #include #include /* for strchr() */ #include #ifdef PRESERVE_NO_SYSV #ifdef SYSV #undef SYSV #endif #undef PRESERVE_NO_SYSV #endif #define X11_BUILD #include "hack.h" #undef X11_BUILD #include "func_tab.h" #include "winX.h" static Widget extended_command_popup = 0; static Widget extended_command_form; static Widget *extended_commands = 0; static const char **command_list; static short *command_indx; static int extended_cmd_selected; /* index of the selected command; */ static int ps_selected; /* index of selected role */ #define PS_RANDOM (-50) #define PS_QUIT (-75) /* 'r' for random won't work for role but will for race, gender, alignment */ static const char ps_randchars[] = "*@\n\rrR"; static const char ps_quitchars[] = "\033qQ"; #define EC_NCHARS 32 static boolean ec_full_list = FALSE; static boolean ec_active = FALSE; static int ec_nchars = 0; static char ec_chars[EC_NCHARS]; static Time ec_time; boolean plsel_ask_name; static const char plsel_dialog_translations[] = "#override\n\ Escape: plsel_quit()\n\ Return: plsel_play()"; static const char plsel_input_accelerators[] = "#override\n\ Escape: plsel_quit()\n\ Return: plsel_play()\n\ Tab: \n"; static const char extended_command_translations[] = "#override\n\ Left: scroll(4)\n\ Right: scroll(6)\n\ Up: scroll(8)\n\ Down: scroll(2)\n\ : scroll(8)\n\ : scroll(2)\n\ : ec_key()"; static const char player_select_translations[] = "#override\n\ : ps_key()"; static const char race_select_translations[] = "#override\n\ : race_key()"; static const char gend_select_translations[] = "#override\n\ : gend_key()"; static const char algn_select_translations[] = "#override\n\ : algn_key()"; static const char popup_entry_translations[] = "#override\n\ : scroll(8)\n\ : scroll(2)"; static void plsel_dialog_acceptvalues(void); static void plsel_set_play_button(boolean); static void plsel_set_sensitivities(boolean); static void X11_player_selection_randomize(void); static void X11_player_selection_setupOthers(void); static void racetoggleCallback(Widget, XtPointer, XtPointer); static void roletoggleCallback(Widget, XtPointer, XtPointer); static void gendertoggleCallback(Widget, XtPointer, XtPointer); static void aligntoggleCallback(Widget, XtPointer, XtPointer); static void plsel_random_btn_callback(Widget, XtPointer, XtPointer); static void plsel_play_btn_callback(Widget, XtPointer, XtPointer); static void plsel_quit_btn_callback(Widget, XtPointer, XtPointer); static Widget X11_create_player_selection_name(Widget); static void X11_player_selection_dialog(void); static void X11_player_selection_prompts(void); static void ps_quit(Widget, XtPointer, XtPointer); static void ps_random(Widget, XtPointer, XtPointer); static void ps_select(Widget, XtPointer, XtPointer); static void extend_select(Widget, XtPointer, XtPointer); static void extend_dismiss(Widget, XtPointer, XtPointer); static void extend_help(Widget, XtPointer, XtPointer); static void popup_delete(Widget, XEvent *, String *, Cardinal *); static void ec_dismiss(void); static void ec_scroll_to_view(int); static void init_extended_commands_popup(void); static Widget make_menu(const char *, const char *, const char *, const char *, XtCallbackProc, const char *, XtCallbackProc, int, const char **, Widget **, XtCallbackProc, Widget *); /* Bad Hack alert. Using integers instead of XtPointers */ XtPointer i2xtp(int i) { return (XtPointer) (ptrdiff_t) i; } int xtp2i(XtPointer x) { return (int) (ptrdiff_t) x; } /* Player Selection ------------------------------------------------------- */ /* ARGSUSED */ static void ps_quit(Widget w, XtPointer client_data, XtPointer call_data) { nhUse(w); nhUse(client_data); nhUse(call_data); ps_selected = PS_QUIT; exit_x_event = TRUE; /* leave event loop */ } /* ARGSUSED */ static void ps_random(Widget w, XtPointer client_data, XtPointer call_data) { nhUse(w); nhUse(client_data); nhUse(call_data); ps_selected = PS_RANDOM; exit_x_event = TRUE; /* leave event loop */ } /* ARGSUSED */ static void ps_select(Widget w, XtPointer client_data, XtPointer call_data) { nhUse(w); nhUse(call_data); ps_selected = (int) (ptrdiff_t) client_data; exit_x_event = TRUE; /* leave event loop */ } /* ARGSUSED */ void ps_key(Widget w, XEvent *event, String *params, Cardinal *num_params) { char ch, *mark; char rolechars[QBUFSZ]; int i; nhUse(w); nhUse(params); nhUse(num_params); (void) memset(rolechars, '\0', sizeof rolechars); /* for strchr() */ for (i = 0; roles[i].name.m; ++i) { ch = lowc(*roles[i].name.m); /* if (flags.female && roles[i].name.f) ch = lowc(*roles[i].name.f); */ /* this supports at most two roles with the same first letter */ if (strchr(rolechars, ch)) ch = highc(ch); rolechars[i] = ch; } ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } mark = strchr(rolechars, ch); if (!mark) mark = strchr(rolechars, lowc(ch)); if (!mark) mark = strchr(rolechars, highc(ch)); if (!mark) { if (strchr(ps_randchars, ch)) ps_selected = PS_RANDOM; else if (strchr(ps_quitchars, ch)) ps_selected = PS_QUIT; else { X11_nhbell(); /* no such class */ return; } } else ps_selected = (int) (mark - rolechars); exit_x_event = TRUE; } /* ARGSUSED */ void race_key(Widget w, XEvent *event, String *params, Cardinal *num_params) { char ch, *mark; char racechars[QBUFSZ]; int i; nhUse(w); nhUse(params); nhUse(num_params); (void) memset(racechars, '\0', sizeof racechars); /* for strchr() */ for (i = 0; races[i].noun; ++i) { ch = lowc(*races[i].noun); /* this supports at most two races with the same first letter */ if (strchr(racechars, ch)) ch = highc(ch); racechars[i] = ch; } ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } mark = strchr(racechars, ch); if (!mark) mark = strchr(racechars, lowc(ch)); if (!mark) mark = strchr(racechars, highc(ch)); if (!mark) { if (strchr(ps_randchars, ch)) ps_selected = PS_RANDOM; else if (strchr(ps_quitchars, ch)) ps_selected = PS_QUIT; else { X11_nhbell(); /* no such race */ return; } } else ps_selected = (int) (mark - racechars); exit_x_event = TRUE; } /* ARGSUSED */ void gend_key(Widget w, XEvent *event, String *params, Cardinal *num_params) { char ch, *mark; static char gendchars[] = "mf"; nhUse(w); nhUse(params); nhUse(num_params); ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } mark = strchr(gendchars, ch); if (!mark) mark = strchr(gendchars, lowc(ch)); if (!mark) { if (strchr(ps_randchars, ch)) ps_selected = PS_RANDOM; else if (strchr(ps_quitchars, ch)) ps_selected = PS_QUIT; else { X11_nhbell(); /* no such gender */ return; } } else ps_selected = (int) (mark - gendchars); exit_x_event = TRUE; } /* ARGSUSED */ void algn_key(Widget w, XEvent *event, String *params, Cardinal *num_params) { char ch, *mark; static char algnchars[] = "LNC"; nhUse(w); nhUse(params); nhUse(num_params); ch = key_event_to_char((XKeyEvent *) event); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } mark = strchr(algnchars, ch); if (!mark) mark = strchr(algnchars, highc(ch)); if (!mark) { if (strchr(ps_randchars, ch)) ps_selected = PS_RANDOM; else if (strchr(ps_quitchars, ch)) ps_selected = PS_QUIT; else { X11_nhbell(); /* no such alignment */ return; } } else ps_selected = (int) (mark - algnchars); exit_x_event = TRUE; } int plsel_n_races, plsel_n_roles; Widget *plsel_race_radios = (Widget *) 0; Widget *plsel_role_radios = (Widget *) 0; Widget *plsel_gend_radios = (Widget *) 0; Widget *plsel_align_radios = (Widget *) 0; Widget plsel_name_input; Widget plsel_btn_play; static void plsel_dialog_acceptvalues(void) { Arg args[2]; String s; flags.initrace = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1; flags.initrole = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1; flags.initgend = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1; flags.initalign = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1; XtSetArg(args[0], nhStr(XtNstring), &s); XtGetValues(plsel_name_input, args, ONE); (void) strncpy(svp.plname, (char *) s, sizeof svp.plname - 1); svp.plname[sizeof svp.plname - 1] = '\0'; (void) mungspaces(svp.plname); if (strlen(svp.plname) < 1) (void) strcpy(svp.plname, "Mumbles"); iflags.renameinprogress = FALSE; } /* ARGSUSED */ void plsel_quit(Widget w, XEvent *event, String *params, Cardinal *num_params) { nhUse(w); nhUse(event); nhUse(params); nhUse(num_params); ps_selected = PS_QUIT; exit_x_event = TRUE; /* leave event loop */ } /* ARGSUSED */ void plsel_play(Widget w, XEvent *event, String *params, Cardinal *num_params) { Arg args[2]; Boolean state; nhUse(w); nhUse(event); nhUse(params); nhUse(num_params); XtSetArg(args[0], nhStr(XtNsensitive), &state); XtGetValues(plsel_btn_play, args, ONE); if (state) { plsel_dialog_acceptvalues(); exit_x_event = TRUE; /* leave event loop */ } else { X11_nhbell(); } } /* ARGSUSED */ void plsel_randomize(Widget w, XEvent *event, String *params, Cardinal *num_params) { nhUse(w); nhUse(event); nhUse(params); nhUse(num_params); X11_player_selection_randomize(); } /* enable or disable the Play button */ static void plsel_set_play_button(boolean state) { Arg args[2]; XtSetArg(args[0], nhStr(XtNsensitive), !state); XtSetValues(plsel_btn_play, args, ONE); } static void plsel_set_sensitivities(boolean setcurr) { Arg args[2]; int j, valid; int c = 0; int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0]))-1; int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0]))-1; plsel_set_play_button(ra < 0 || ro < 0); if (ra < 0 || ro < 0) return; valid = -1; for (j = 0; roles[j].name.m; j++) { boolean v = validrace(j, ra); if (j == ro) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_role_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validrace(ro, c)) c = valid; if (setcurr) XawToggleSetCurrent(plsel_role_radios[0], i2xtp(c + 1)); valid = -1; for (j = 0; races[j].noun; j++) { boolean v = validrace(ro, j); if (j == ra) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_race_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validrace(ro, c)) c = valid; if (setcurr) XawToggleSetCurrent(plsel_race_radios[0], i2xtp(c + 1)); X11_player_selection_setupOthers(); } static void X11_player_selection_randomize(void) { int nrole = plsel_n_roles; int nrace = plsel_n_races; int ro, ra, al, gend; boolean choose_race_first; boolean picksomething = (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE || flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE); if (flags.randomall && picksomething) { if (flags.initrole == ROLE_NONE) flags.initrole = ROLE_RANDOM; if (flags.initrace == ROLE_NONE) flags.initrace = ROLE_RANDOM; if (flags.initgend == ROLE_NONE) flags.initgend = ROLE_RANDOM; if (flags.initalign == ROLE_NONE) flags.initalign = ROLE_RANDOM; } rigid_role_checks(); /* Randomize race and role, unless specified in config */ ro = flags.initrole; if (ro == ROLE_NONE || ro == ROLE_RANDOM) { ro = rn2(nrole); } ra = flags.initrace; if (ra == ROLE_NONE || ra == ROLE_RANDOM) { ra = rn2(nrace); if (flags.initrace != ROLE_RANDOM) { } } /* make sure we have a valid combination, honoring the users request if possible. */ choose_race_first = FALSE; if (flags.initrace >= 0 && flags.initrole < 0) { choose_race_first = TRUE; } while (!validrace(ro, ra)) { if (choose_race_first) { ro = rn2(nrole); } else { ra = rn2(nrace); } } gend = flags.initgend; if (gend == ROLE_NONE) { gend = rn2(ROLE_GENDERS); } while (!validgend(ro, ra, gend)) { gend = rn2(ROLE_GENDERS); } al = flags.initalign; if (al == ROLE_NONE) { al = rn2(ROLE_ALIGNS); } while (!validalign(ro, ra, al)) { al = rn2(ROLE_ALIGNS); } XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(gend + 1)); XawToggleSetCurrent(plsel_align_radios[0], i2xtp(al + 1)); XawToggleSetCurrent(plsel_race_radios[0], i2xtp(ra + 1)); XawToggleSetCurrent(plsel_role_radios[0], i2xtp(ro + 1)); plsel_set_sensitivities(FALSE); } static void X11_player_selection_setupOthers(void) { Arg args[2]; int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1; int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1; int valid = -1, c = 0, j; int gchecked = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1; int achecked = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1; if (ro < 0 || ra < 0) return; for (j = 0; j < ROLE_GENDERS; j++) { boolean v = validgend(ro, ra, j); if (j == gchecked) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_gend_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validgend(ro, ra, c)) c = valid; XawToggleSetCurrent(plsel_gend_radios[0], i2xtp(c + 1)); valid = -1; for (j = 0; j < ROLE_ALIGNS; j++) { boolean v = validalign(ro, ra, j); if (j == achecked) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_align_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validalign(ro, ra, c)) c = valid; XawToggleSetCurrent(plsel_align_radios[0], i2xtp(c + 1)); } static void racetoggleCallback(Widget w, XtPointer client, XtPointer call) { Arg args[2]; int j, valid; int c = 0; int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1; int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1; nhUse(w); nhUse(client); nhUse(call); plsel_set_play_button(ra < 0 || ro < 0); if (ra < 0 || ro < 0) return; valid = -1; for (j = 0; roles[j].name.m; j++) { boolean v = validrace(j, ra); if (j == ro) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_role_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validrace(c, ra)) c = valid; j = c + 1; XawToggleSetCurrent(plsel_role_radios[0], i2xtp(j)); X11_player_selection_setupOthers(); } static void roletoggleCallback(Widget w, XtPointer client, XtPointer call) { Arg args[2]; int j, valid; int c = 0; int ra = xtp2i(XawToggleGetCurrent(plsel_race_radios[0])) - 1; int ro = xtp2i(XawToggleGetCurrent(plsel_role_radios[0])) - 1; nhUse(w); nhUse(client); nhUse(call); plsel_set_play_button(ra < 0 || ro < 0); if (ra < 0 || ro < 0) return; valid = -1; for (j = 0; races[j].noun; j++) { boolean v = validrace(ro, j); if (j == ra) c = j; XtSetArg(args[0], nhStr(XtNsensitive), v); XtSetValues(plsel_race_radios[j], args, ONE); if (valid < 0 && v) valid = j; } if (!validrace(ro, c)) c = valid; j = c + 1; XawToggleSetCurrent(plsel_race_radios[0], i2xtp(j)); X11_player_selection_setupOthers(); } static void gendertoggleCallback(Widget w, XtPointer client, XtPointer call) { int i, r = xtp2i(XawToggleGetCurrent(plsel_gend_radios[0])) - 1; nhUse(w); nhUse(client); nhUse(call); plsel_set_play_button(r < 0); for (i = 0; roles[i].name.m; i++) { if (roles[i].name.f) { Arg args[2]; XtSetArg(args[0], XtNlabel, (r < 1) ? roles[i].name.m : roles[i].name.f); XtSetValues(plsel_role_radios[i], args, ONE); } } } static void aligntoggleCallback(Widget w, XtPointer client, XtPointer call) { int r = xtp2i(XawToggleGetCurrent(plsel_align_radios[0])) - 1; nhUse(w); nhUse(client); nhUse(call); plsel_set_play_button(r < 0); } static void plsel_random_btn_callback(Widget w, XtPointer client, XtPointer call) { nhUse(w); nhUse(client); nhUse(call); X11_player_selection_randomize(); } static void plsel_play_btn_callback(Widget w, XtPointer client, XtPointer call) { nhUse(w); nhUse(client); nhUse(call); plsel_dialog_acceptvalues(); exit_x_event = TRUE; /* leave event loop */ } static void plsel_quit_btn_callback(Widget w, XtPointer client, XtPointer call) { nhUse(w); nhUse(client); nhUse(call); ps_selected = PS_QUIT; exit_x_event = TRUE; /* leave event loop */ } static Widget X11_create_player_selection_name(Widget form) { Widget namelabel, name_vp, name_form; Arg args[10]; Cardinal num_args; /* name viewport */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, False); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; name_vp = XtCreateManagedWidget("name_vp", viewportWidgetClass, form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; name_form = XtCreateManagedWidget("name_form", formWidgetClass, name_vp, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Name"); num_args++; XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; namelabel = XtCreateManagedWidget("name_label", labelWidgetClass, name_form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), namelabel); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], nhStr(XtNresizable), True); num_args++; XtSetArg(args[num_args], nhStr(XtNeditType), !plsel_ask_name ? XawtextRead : XawtextEdit); num_args++; XtSetArg(args[num_args], nhStr(XtNresize), XawtextResizeWidth); num_args++; XtSetArg(args[num_args], nhStr(XtNstring), svp.plname); num_args++; XtSetArg(args[num_args], XtNinsertPosition, strlen(svp.plname)); num_args++; XtSetArg(args[num_args], nhStr(XtNaccelerators), XtParseAcceleratorTable(plsel_input_accelerators)); num_args++; plsel_name_input = XtCreateManagedWidget("name_input", asciiTextWidgetClass, name_form, args, num_args); XtInstallAccelerators(plsel_name_input, plsel_name_input); if (plsel_ask_name) { XtSetKeyboardFocus(form, plsel_name_input); } else { XtSetArg(args[0], nhStr(XtNdisplayCaret), False); XtSetValues(plsel_name_input, args, ONE); } return name_vp; } static void X11_player_selection_dialog(void) { Widget popup, popup_vp; Widget form; Widget name_vp; Widget racelabel, race_form, race_vp, race_form2; Widget rolelabel, role_form, role_vp, role_form2; Widget gendlabel, gend_form, gend_radio_m, gend_radio_f, gend_vp, gend_form2; Widget alignlabel, align_form, align_radio_l, align_radio_n, align_radio_c, align_vp, align_form2; Widget btn_vp, btn_form, random_btn, play_btn, quit_btn; Widget tmpwidget; Arg args[10]; Cardinal num_args; int i; int winwid = 400; int cwid = (winwid / 3) - 14; num_args = 0; XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; XtSetArg(args[num_args], XtNtitle, "Player Selection"); num_args++; popup = XtCreatePopupShell("player_selection_dialog", transientShellWidgetClass, toplevel, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(plsel_dialog_translations)); num_args++; popup_vp = XtCreateManagedWidget("plsel_vp", viewportWidgetClass, popup, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(plsel_dialog_translations)); num_args++; form = XtCreateManagedWidget("plsel_form", formWidgetClass, popup_vp, args, num_args); name_vp = X11_create_player_selection_name(form); num_args = 0; XtSetArg(args[num_args], XtNwidth, winwid); num_args++; XtSetValues(name_vp, args, num_args); /* * Layout; role is centered rather than first: * * +------------------------------------+ * | name | * +------------------------------------+ * +--------+ +------------+ +---------+ * | human | |archeologist| | male | * | elf | | barbarian | | female | * | dwarf | | caveman | +---------+ * | gnome | | healer | +---------+ * | orc | | knight | | lawful | * +--------+ | monk | | neutral | * | priest | | chaotic | * | rogue | +---------+ * | ranger | +--------+ * | samurai | + Random + * | tourist | + Play + * | valkyrie | + Quit + * | wizard | +--------+ * +------------+ * * TODO: * make name box same width as race+gap+role+gap+gender/alignment * (resize it after the other boxes have been placed); * make Random/Play/Quit buttons same width as gender/alignment and * align bottom of them with bottom of role (they already specify * the same width for the label text but different decorations-- * room for radio button box--of the other widgets results in the * total width being different); * add 'random' to each of the four boxes and Choose to the Random/ * Play/Quit buttons; if none of the four 'random's are currently * selected, gray-out Choose; conversely, when Choose or Play is * clicked on, make the random assignments for any/all of the four * boxes which have 'random' selected. * Maybe: move gender box underneath race, bottom aligned with role * and move alignment up to where gender currently is. If that's * done, move role column first and race+gender to middle. */ /********************************************/ /* Race */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; race_form = XtCreateManagedWidget("race_form", formWidgetClass, form, args, num_args); /* race label */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Race"); num_args++; racelabel = XtCreateManagedWidget("race_label", labelWidgetClass, race_form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), racelabel); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; race_vp = XtCreateManagedWidget("race_vp", viewportWidgetClass, race_form, args, num_args); num_args = 0; race_form2 = XtCreateManagedWidget("race_form2", formWidgetClass, race_vp, args, num_args); for (i = 0; races[i].noun; i++) continue; plsel_n_races = i; plsel_race_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_races); /* race radio buttons */ for (i = 0; races[i].noun; i++) { Widget racewidget; num_args = 0; if (i > 0) { XtSetArg(args[num_args], nhStr(XtNfromVert), tmpwidget); num_args++; } XtSetArg(args[num_args], XtNwidth, cwid); num_args++; if (i > 0) { XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_race_radios[0]); num_args++; } XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++; racewidget = XtCreateManagedWidget(races[i].noun, toggleWidgetClass, race_form2, args, num_args); XtAddCallback(racewidget, XtNcallback, racetoggleCallback, i2xtp(i)); tmpwidget = racewidget; plsel_race_radios[i] = racewidget; } XawToggleUnsetCurrent(plsel_race_radios[0]); /********************************************/ /* Role */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; XtSetArg(args[num_args], nhStr(XtNfromHoriz), race_form); num_args++; role_form = XtCreateManagedWidget("role_form", formWidgetClass, form, args, num_args); /* role label */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Role"); num_args++; rolelabel = XtCreateManagedWidget("role_label", labelWidgetClass, role_form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), rolelabel); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; role_vp = XtCreateManagedWidget("role_vp", viewportWidgetClass, role_form, args, num_args); num_args = 0; role_form2 = XtCreateManagedWidget("role_form2", formWidgetClass, role_vp, args, num_args); for (i = 0; roles[i].name.m; i++) continue; plsel_n_roles = i; plsel_role_radios = (Widget *) alloc(sizeof (Widget) * plsel_n_roles); /* role radio buttons */ for (i = 0; roles[i].name.m; i++) { Widget rolewidget; num_args = 0; if (i > 0) { XtSetArg(args[num_args], nhStr(XtNfromVert), tmpwidget); num_args++; } XtSetArg(args[num_args], nhStr(XtNwidth), cwid); num_args++; if (i > 0) { XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_role_radios[0]); num_args++; } XtSetArg(args[num_args], nhStr(XtNradioData), (i + 1)); num_args++; rolewidget = XtCreateManagedWidget(roles[i].name.m, toggleWidgetClass, role_form2, args, num_args); XtAddCallback(rolewidget, XtNcallback, roletoggleCallback, i2xtp(i)); tmpwidget = rolewidget; plsel_role_radios[i] = rolewidget; } XawToggleUnsetCurrent(plsel_role_radios[0]); /********************************************/ /* Gender*/ plsel_gend_radios = (Widget *) alloc(sizeof (Widget) * ROLE_GENDERS); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), name_vp); num_args++; XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; gend_form = XtCreateManagedWidget("gender_form", formWidgetClass, form, args, num_args); /* gender label */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Gender"); num_args++; gendlabel = XtCreateManagedWidget("gender_label", labelWidgetClass, gend_form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), gendlabel); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; gend_vp = XtCreateManagedWidget("gender_vp", viewportWidgetClass, gend_form, args, num_args); num_args = 0; gend_form2 = XtCreateManagedWidget("gender_form2", formWidgetClass, gend_vp, args, num_args); /* gender radio buttons */ num_args = 0; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++; plsel_gend_radios[0] = gend_radio_m = XtCreateManagedWidget("Male", toggleWidgetClass, gend_form2, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), gend_radio_m); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_gend_radios[0]); num_args++; XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++; plsel_gend_radios[1] = gend_radio_f = XtCreateManagedWidget("Female", toggleWidgetClass, gend_form2, args, num_args); XawToggleUnsetCurrent(plsel_gend_radios[0]); XtAddCallback(gend_radio_m, XtNcallback, gendertoggleCallback, (XtPointer) (1)); XtAddCallback(gend_radio_f, XtNcallback, gendertoggleCallback, (XtPointer) (2)); /********************************************/ /* Alignment */ plsel_align_radios = (Widget *) alloc(sizeof (Widget) * ROLE_ALIGNS); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), gend_form); num_args++; XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++; XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; align_form = XtCreateManagedWidget("align_form", formWidgetClass, form, args, num_args); /* align label */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNjustify), XtJustifyLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Alignment"); num_args++; alignlabel = XtCreateManagedWidget("align_label", labelWidgetClass, align_form, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNtopMargin), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), alignlabel); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; align_vp = XtCreateManagedWidget("align_vp", viewportWidgetClass, align_form, args, num_args); num_args = 0; align_form2 = XtCreateManagedWidget("align_form2", formWidgetClass, align_vp, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNradioData), 1); num_args++; plsel_align_radios[0] = align_radio_l = XtCreateManagedWidget("Lawful", toggleWidgetClass, align_form2, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_l); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_align_radios[0]); num_args++; XtSetArg(args[num_args], nhStr(XtNradioData), 2); num_args++; plsel_align_radios[1] = align_radio_n = XtCreateManagedWidget("Neutral", toggleWidgetClass, align_form2, args, num_args); num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), align_radio_n); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNradioGroup), plsel_align_radios[0]); num_args++; XtSetArg(args[num_args], nhStr(XtNradioData), 3); num_args++; plsel_align_radios[2] = align_radio_c = XtCreateManagedWidget("Chaotic", toggleWidgetClass, align_form2, args, num_args); XawToggleUnsetCurrent(plsel_align_radios[0]); XtAddCallback(align_radio_l, XtNcallback, aligntoggleCallback, (XtPointer) (1)); XtAddCallback(align_radio_n, XtNcallback, aligntoggleCallback, (XtPointer) (2)); XtAddCallback(align_radio_c, XtNcallback, aligntoggleCallback, (XtPointer) (3)); /********************************************/ /* Buttons! */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), align_form); num_args++; XtSetArg(args[num_args], nhStr(XtNvertDistance), 30); num_args++; XtSetArg(args[num_args], nhStr(XtNrightMargin), 0); num_args++; XtSetArg(args[num_args], nhStr(XtNfromHoriz), role_form); num_args++; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, False); num_args++; XtSetArg(args[num_args], XtNallowHoriz, False); num_args++; btn_vp = XtCreateManagedWidget("btn_vp", viewportWidgetClass, form, args, num_args); num_args = 0; btn_form = XtCreateManagedWidget("btn_form", formWidgetClass, btn_vp, args, num_args); /* "Random" button */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Random"); num_args++; random_btn = XtCreateManagedWidget("random", commandWidgetClass, btn_form, args, num_args); XtAddCallback(random_btn, XtNcallback, plsel_random_btn_callback, form); /* "Play" button */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), random_btn); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Play"); num_args++; plsel_btn_play = play_btn = XtCreateManagedWidget("play", commandWidgetClass, btn_form, args, num_args); XtAddCallback(play_btn, XtNcallback, plsel_play_btn_callback, form); /* "Quit" button */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), play_btn); num_args++; XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], XtNwidth, cwid); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), "Quit"); num_args++; quit_btn = XtCreateManagedWidget("quit", commandWidgetClass, btn_form, args, num_args); XtAddCallback(quit_btn, XtNcallback, plsel_quit_btn_callback, form); /********************************************/ XtRealizeWidget(popup); X11_player_selection_randomize(); if (flags.randomall) { plsel_dialog_acceptvalues(); } else { ps_selected = -1; nh_XtPopup(popup, (int) XtGrabExclusive, form); /* The callback will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); } nh_XtPopdown(popup); XtDestroyWidget(popup); if (plsel_race_radios) free(plsel_race_radios); if (plsel_role_radios) free(plsel_role_radios); if (plsel_gend_radios) free(plsel_gend_radios); if (plsel_align_radios) free(plsel_align_radios); if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) || program_state.done_hup #endif ) { clearlocks(); X11_exit_nhwindows((char *) 0); nh_terminate(0); } } static void X11_player_selection_prompts(void) { int num_roles, num_races, num_gends, num_algns, i, availcount, availindex; Widget popup, player_form; const char **choices; char qbuf[QBUFSZ], plbuf[QBUFSZ]; /* avoid unnecessary prompts further down */ rigid_role_checks(); (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, flags.initrace, flags.initgend, flags.initalign); while (flags.initrole < 0) { if (flags.initrole == ROLE_RANDOM || flags.randomall) { flags.initrole = pick_role(flags.initrace, flags.initgend, flags.initalign, PICK_RANDOM); break; } /* select a role */ for (num_roles = 0; roles[num_roles].name.m; ++num_roles) continue; choices = (const char **) alloc(sizeof (char *) * num_roles); for (;;) { availcount = 0; for (i = 0; i < num_roles; i++) { choices[i] = 0; if (ok_role(i, flags.initrace, flags.initgend, flags.initalign)) { choices[i] = roles[i].name.m; if (flags.initgend >= 0 && flags.female && roles[i].name.f) choices[i] = roles[i].name.f; ++availcount; } } if (availcount > 0) break; else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ else if (flags.initgend >= 0) flags.initgend = -1; else if (flags.initrace >= 0) flags.initrace = -1; else panic("no available ROLE+race+gender+alignment combinations"); } Sprintf(qbuf, "Choose your %s Role", s_suffix(plbuf)); popup = make_menu("player_selection", qbuf, player_select_translations, "quit", ps_quit, "random", ps_random, num_roles, choices, (Widget **) 0, ps_select, &player_form); ps_selected = -1; positionpopup(popup, FALSE); nh_XtPopup(popup, (int) XtGrabExclusive, player_form); /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); nh_XtPopdown(popup); XtDestroyWidget(popup); free((genericptr_t) choices), choices = 0; if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) || program_state.done_hup #endif ) { clearlocks(); X11_exit_nhwindows((char *) 0); nh_terminate(0); } else if (ps_selected == PS_RANDOM) { flags.initrole = ROLE_RANDOM; } else if (ps_selected < 0 || ps_selected >= num_roles) { panic("player_selection: bad role select value %d", ps_selected); } else { flags.initrole = ps_selected; } } (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, flags.initrace, flags.initgend, flags.initalign); while (!validrace(flags.initrole, flags.initrace)) { if (flags.initrace == ROLE_RANDOM || flags.randomall) { flags.initrace = pick_race(flags.initrole, flags.initgend, flags.initalign, PICK_RANDOM); break; } /* select a race */ for (num_races = 0; races[num_races].noun; ++num_races) continue; choices = (const char **) alloc(sizeof(char *) * num_races); for (;;) { availcount = availindex = 0; for (i = 0; i < num_races; i++) { choices[i] = 0; if (ok_race(flags.initrole, i, flags.initgend, flags.initalign)) { choices[i] = races[i].noun; ++availcount; availindex = i; /* used iff only one */ } } if (availcount > 0) break; else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ else if (flags.initgend >= 0) flags.initgend = -1; else panic("no available role+RACE+gender+alignment combinations"); } if (availcount == 1) { flags.initrace = availindex; free((genericptr_t) choices), choices = 0; } else { Sprintf(qbuf, "Pick your %s race", s_suffix(plbuf)); popup = make_menu("race_selection", qbuf, race_select_translations, "quit", ps_quit, "random", ps_random, num_races, choices, (Widget **) 0, ps_select, &player_form); ps_selected = -1; positionpopup(popup, FALSE); nh_XtPopup(popup, (int) XtGrabExclusive, player_form); /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); nh_XtPopdown(popup); XtDestroyWidget(popup); free((genericptr_t) choices), choices = 0; if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) || program_state.done_hup #endif ) { clearlocks(); X11_exit_nhwindows((char *) 0); nh_terminate(0); } else if (ps_selected == PS_RANDOM) { flags.initrace = ROLE_RANDOM; } else if (ps_selected < 0 || ps_selected >= num_races) { panic("player_selection: bad race select value %d", ps_selected); } else { flags.initrace = ps_selected; } } /* more than one race choice available */ } (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, flags.initrace, flags.initgend, flags.initalign); while (!validgend(flags.initrole, flags.initrace, flags.initgend)) { if (flags.initgend == ROLE_RANDOM || flags.randomall) { flags.initgend = pick_gend(flags.initrole, flags.initrace, flags.initalign, PICK_RANDOM); break; } /* select a gender */ num_gends = 2; /* genders[2] isn't allowed */ choices = (const char **) alloc(sizeof(char *) * num_gends); for (;;) { availcount = availindex = 0; for (i = 0; i < num_gends; i++) { choices[i] = 0; if (ok_gend(flags.initrole, flags.initrace, i, flags.initalign)) { choices[i] = genders[i].adj; ++availcount; availindex = i; /* used iff only one */ } } if (availcount > 0) break; else if (flags.initalign >= 0) flags.initalign = -1; /* reset */ else panic("no available role+race+GENDER+alignment combinations"); } if (availcount == 1) { flags.initgend = availindex; free((genericptr_t) choices), choices = 0; } else { Sprintf(qbuf, "Your %s gender?", s_suffix(plbuf)); popup = make_menu("gender_selection", qbuf, gend_select_translations, "quit", ps_quit, "random", ps_random, num_gends, choices, (Widget **) 0, ps_select, &player_form); ps_selected = -1; positionpopup(popup, FALSE); nh_XtPopup(popup, (int) XtGrabExclusive, player_form); /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); nh_XtPopdown(popup); XtDestroyWidget(popup); free((genericptr_t) choices), choices = 0; if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) || program_state.done_hup #endif ) { clearlocks(); X11_exit_nhwindows((char *) 0); nh_terminate(0); } else if (ps_selected == PS_RANDOM) { flags.initgend = ROLE_RANDOM; } else if (ps_selected < 0 || ps_selected >= num_gends) { panic("player_selection: bad gender select value %d", ps_selected); } else { flags.initgend = ps_selected; } } /* more than one gender choice available */ } (void) root_plselection_prompt(plbuf, QBUFSZ - 1, flags.initrole, flags.initrace, flags.initgend, flags.initalign); while (!validalign(flags.initrole, flags.initrace, flags.initalign)) { if (flags.initalign == ROLE_RANDOM || flags.randomall) { flags.initalign = pick_align(flags.initrole, flags.initrace, flags.initgend, PICK_RANDOM); break; } /* select an alignment */ num_algns = 3; /* aligns[3] isn't allowed */ choices = (const char **) alloc(sizeof(char *) * num_algns); for (;;) { availcount = availindex = 0; for (i = 0; i < num_algns; i++) { choices[i] = 0; if (ok_align(flags.initrole, flags.initrace, flags.initgend, i)) { choices[i] = aligns[i].adj; ++availcount; availindex = i; /* used iff only one */ } } if (availcount > 0) break; else panic("no available role+race+gender+ALIGNMENT combinations"); } if (availcount == 1) { flags.initalign = availindex; free((genericptr_t) choices), choices = 0; } else { Sprintf(qbuf, "Your %s alignment?", s_suffix(plbuf)); popup = make_menu("alignment_selection", qbuf, algn_select_translations, "quit", ps_quit, "random", ps_random, num_algns, choices, (Widget **) 0, ps_select, &player_form); ps_selected = -1; positionpopup(popup, FALSE); nh_XtPopup(popup, (int) XtGrabExclusive, player_form); /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); nh_XtPopdown(popup); XtDestroyWidget(popup); free((genericptr_t) choices), choices = 0; if (ps_selected == PS_QUIT #if defined(HANGUPHANDLING) || program_state.done_hup #endif ) { clearlocks(); X11_exit_nhwindows((char *) 0); nh_terminate(0); } else if (ps_selected == PS_RANDOM) { flags.initalign = ROLE_RANDOM; } else if (ps_selected < 0 || ps_selected >= num_algns) { panic("player_selection: bad alignment select value %d", ps_selected); } else { flags.initalign = ps_selected; } } /* more than one alignment choice available */ } } /* Global functions ======================================================== */ void X11_player_selection(void) { if (iflags.wc_player_selection == VIA_DIALOG) { if (!*svp.plname) { #ifdef UNIX char *defplname = get_login_name(); #else char *defplname = (char *)0; #endif (void) strncpy(svp.plname, defplname ? defplname : "Mumbles", sizeof svp.plname - 1); svp.plname[sizeof svp.plname - 1] = '\0'; iflags.renameinprogress = TRUE; } X11_player_selection_dialog(); } else { /* iflags.wc_player_selection == VIA_PROMPTS */ X11_player_selection_prompts(); } } /* called by core to have the player pick an extended command */ int X11_get_ext_cmd(void) { if (iflags.extmenu != ec_full_list) { /* player has toggled the 'extmenu' option, toss the old widgets */ if (extended_commands) release_extended_cmds(); /* will set extended_commands to Null */ ec_full_list = iflags.extmenu; } if (!extended_commands) init_extended_commands_popup(); extended_cmd_selected = -1; /* reset selected value */ ec_scroll_to_view(-1); /* force scroll bar to top */ positionpopup(extended_command_popup, FALSE); /* center on cursor */ nh_XtPopup(extended_command_popup, (int) XtGrabExclusive, extended_command_form); /* The callbacks will enable the event loop exit. */ (void) x_event(EXIT_ON_EXIT); if (extended_cmd_selected < 0) { return -1; } return command_indx[extended_cmd_selected]; } void release_extended_cmds(void) { if (extended_commands) { XtDestroyWidget(extended_command_popup), extended_command_popup = 0; free((genericptr_t) extended_commands), extended_commands = 0; free((genericptr_t) command_list), command_list = (const char **) 0; free((genericptr_t) command_indx), command_indx = (short *) 0; } } /* End global functions =================================================== */ /* Extended Command ------------------------------------------------------- */ /* ARGSUSED */ static void extend_select(Widget w, XtPointer client_data, XtPointer call_data) { int selected = (int) (ptrdiff_t) client_data; nhUse(w); nhUse(call_data); if (extended_cmd_selected != selected) { /* visibly deselect old one */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); /* select new one */ swap_fg_bg(extended_commands[selected]); extended_cmd_selected = selected; } nh_XtPopdown(extended_command_popup); /* reset colors while popped down */ swap_fg_bg(extended_commands[extended_cmd_selected]); ec_active = FALSE; exit_x_event = TRUE; /* leave event loop */ } /* ARGSUSED */ static void extend_dismiss(Widget w, XtPointer client_data, XtPointer call_data) { nhUse(w); nhUse(client_data); nhUse(call_data); ec_dismiss(); } /* ARGSUSED */ static void extend_help(Widget w, XtPointer client_data, XtPointer call_data) { nhUse(w); nhUse(client_data); nhUse(call_data); /* We might need to make it known that we already have one listed. */ (void) doextlist(); } /* ARGSUSED */ void ec_delete(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (w == extended_command_popup) { ec_dismiss(); } else { popup_delete(w, event, params, num_params); } } /* ARGSUSED */ static void popup_delete(Widget w, XEvent *event, String *params, Cardinal *num_params) { nhUse(event); nhUse(params); nhUse(num_params); ps_selected = PS_QUIT; nh_XtPopdown(w); exit_x_event = TRUE; /* leave event loop */ } static void ec_dismiss(void) { /* unselect while still visible */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = -1; /* dismiss */ nh_XtPopdown(extended_command_popup); ec_active = FALSE; exit_x_event = TRUE; /* leave event loop */ } /* scroll the extended command menu if necessary so that choices extended_cmd_selected through ec_indx will be visible */ static void ec_scroll_to_view(int ec_indx) /* might be greater than extended_cmd_selected */ { Widget viewport, scrollbar, tmpw; Arg args[5]; Cardinal num_args; Position lo_y, hi_y; /* ext cmd label y */ float s_shown, s_top; /* scrollbar pos */ float s_min, s_max; Dimension h, hh, wh, vh; /* widget and viewport heights */ Dimension border_width; int distance = 0; boolean force_top = (ec_indx < 0); /* * If the extended command menu needs to be scrolled in order to move * either the highlighted entry (extended_cmd_selected) or the target * entry (ec_indx) into view, we want to make both end up visible. * [Highlighted one is the first matching entry when the user types * something, such as "adjust" after typing 'a', and will be chosen * by pressing . Target entry is one past the last matching * entry (or last matching entry itself if at end of command list), * showing the user the other potential matches so far.] * * If that's not possible (maybe menu has been resized so that it's * too small), the highlighted entry takes precedence and the target * will be decremented until close enough to fit. */ if (force_top) ec_indx = 0; /* get viewport and scrollbar widgets */ tmpw = extended_commands[ec_indx]; viewport = XtParent(tmpw); do { scrollbar = XtNameToWidget(tmpw, "*vertical"); if (scrollbar) break; tmpw = XtParent(tmpw); } while (tmpw); if (scrollbar && viewport) { /* get selected ext command label y position and height */ num_args = 0; XtSetArg(args[num_args], XtNy, &hi_y); num_args++; XtSetArg(args[num_args], XtNheight, &h); num_args++; XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width); num_args++; XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); num_args++; XtGetValues(extended_commands[ec_indx], args, num_args); if (distance < 1 || distance > 32766) /* defaultDistance is weird */ distance = 4; /* vertical distance between top of one command widget and the next */ hh = h + distance + 2 * border_width; /* location of the highlighted entry, if any */ if (extended_cmd_selected >= 0) { XtSetArg(args[0], XtNy, &lo_y); XtGetValues(extended_commands[extended_cmd_selected], args, ONE); } else lo_y = hi_y; /* get menu widget and viewport heights */ XtSetArg(args[0], XtNheight, &wh); XtGetValues(tmpw, args, ONE); XtSetArg(args[0], XtNheight, &vh); XtGetValues(viewport, args, ONE); /* widget might be too small if it has been resized or there are a very large number of ambiguous choices */ if (hi_y - lo_y > wh) { ec_indx = extended_cmd_selected; if (wh > hh) ec_indx += (wh / hh); XtSetArg(args[0], XtNy, &hi_y); XtGetValues(extended_commands[ec_indx], args, num_args); } /* get scrollbar "height" and "top" position; floats between 0-1 */ num_args = 0; XtSetArg(args[num_args], XtNshown, &s_shown); num_args++; XtSetArg(args[num_args], nhStr(XtNtopOfThumb), &s_top); num_args++; XtGetValues(scrollbar, args, num_args); s_min = s_top * vh; s_max = (s_top + s_shown) * vh; /* scroll if outside the view */ if (force_top) { s_min = 0.0; XtCallCallbacks(scrollbar, XtNjumpProc, &s_min); } else if ((int) lo_y <= (int) s_min) { s_min = (float) (lo_y / (float) vh); XtCallCallbacks(scrollbar, XtNjumpProc, &s_min); } else if ((int) (hi_y + h) >= (int) s_max) { s_min = (float) ((hi_y + h) / (float) vh) - s_shown; XtCallCallbacks(scrollbar, XtNjumpProc, &s_min); } } } /* ARGSUSED */ void ec_key(Widget w, XEvent *event, String *params, Cardinal *num_params) { char ch; int i, pass; float shown, top; Arg arg[2]; Widget hbar, vbar; XKeyEvent *xkey = (XKeyEvent *) event; nhUse(params); nhUse(num_params); ch = key_event_to_char(xkey); if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ return; } else if (ch == '?') { extend_help((Widget) 0, (XtPointer) 0, (XtPointer) 0); return; } else if (strchr("\033\n\r", ch)) { if (ch == '\033') { /* unselect while still visible */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = -1; /* dismiss */ } nh_XtPopdown(extended_command_popup); /* unselect while invisible */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); exit_x_event = TRUE; /* leave event loop */ ec_active = FALSE; return; } else if (ch == MENU_FIRST_PAGE || ch == MENU_LAST_PAGE) { find_scrollbars(w, (Widget) 0, &hbar, &vbar); if (vbar) { top = (ch == MENU_FIRST_PAGE) ? 0.0 : 1.0; XtCallCallbacks(vbar, XtNjumpProc, &top); } return; } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) { find_scrollbars(w, (Widget) 0, &hbar, &vbar); if (vbar) { XtSetArg(arg[0], nhStr(XtNshown), &shown); XtSetArg(arg[1], nhStr(XtNtopOfThumb), &top); XtGetValues(vbar, arg, TWO); top += ((ch == MENU_NEXT_PAGE) ? shown : -shown); XtCallCallbacks(vbar, XtNjumpProc, &top); } return; } /* * If too much time has elapsed, treat current key as starting a new * choice, otherwise it is a continuation of the choice in progress. * Extra letters might be needed to disambiguate between choices * ("ride" vs "rub", for instance), or player may just be typing in * the whole word. */ if (ec_active && (xkey->time - ec_time) > 2500) /* 2.5 seconds */ ec_active = FALSE; if (!ec_active) { ec_nchars = 0; ec_active = TRUE; } ec_time = xkey->time; ec_chars[ec_nchars++] = ch; if (ec_nchars >= EC_NCHARS) ec_nchars = EC_NCHARS - 1; /* don't overflow */ for (pass = 0; pass < 2; pass++) { if (pass == 1) { /* first pass finished, but no matching command was found */ /* start a new one with the last char entered */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = -1; /* dismiss */ ec_chars[0] = ec_chars[ec_nchars - 1]; ec_nchars = 1; } for (i = 0; command_list[i]; ++i) { if (!strncmp(ec_chars, command_list[i], ec_nchars)) { if (extended_cmd_selected != i) { /* I should use set() and unset() actions, but how do I send the an action to the widget? */ if (extended_cmd_selected >= 0) swap_fg_bg(extended_commands[extended_cmd_selected]); extended_cmd_selected = i; swap_fg_bg(extended_commands[extended_cmd_selected]); } /* advance to one past last matching entry, so that all ambiguous choices, plus one to show there aren't any more such, will scroll into view */ do { if (!command_list[i + 1]) break; /* end of list */ ++i; } while (!strncmp(ec_chars, command_list[i], ec_nchars)); ec_scroll_to_view(i); return; } } } } /* * Use our own home-brewed version menu because simpleMenu is designed to * be used from a menubox. */ static void init_extended_commands_popup(void) { int i, num_commands; int *matches; int ecmflags = ECM_NO1CHARCMD; if (ec_full_list) ecmflags |= ECM_IGNOREAC; num_commands = extcmds_match(NULL, ecmflags, &matches); i = num_commands + 1; /* room for each extcmd, plus terminator */ command_list = (const char **) alloc((unsigned) (i * sizeof(char *))); command_indx = (short *) alloc((unsigned) (i * sizeof(short))); for (i = 0; i < num_commands; i++) { struct ext_func_tab *ec = extcmds_getentry(matches[i]); command_indx[i] = matches[i]; command_list[i] = ec->ef_txt; } command_list[i] = (char *) 0; command_indx[i] = -1; extended_command_popup = make_menu("extended_commands", "Extended Commands", extended_command_translations, "dismiss", extend_dismiss, "help", extend_help, num_commands, command_list, &extended_commands, extend_select, &extended_command_form); } /* ------------------------------------------------------------------------ */ /* * Create a popup widget of the following form: * * popup_label * ----------- ------------ * |left_name| |right_name| * ----------- ------------ * ------------------------ * | name1 | * ------------------------ * ------------------------ * | name2 | * ------------------------ * . * . * ------------------------ * | nameN | * ------------------------ */ static Widget make_menu(const char *popup_name, const char *popup_label, const char *popup_translations, const char *left_name, XtCallbackProc left_callback, const char *right_name, XtCallbackProc right_callback, int num_names, const char **widget_names, /* return array of command widgets */ Widget **command_widgets, XtCallbackProc name_callback, Widget *formp) /* return */ { Widget popup, popform, form, label, above, left, right, view; Widget *commands, *curr; int i; Arg args[12]; Cardinal num_args; Dimension width, other_width, max_width, border_width, height, cumulative_height, screen_height; int distance, skip; char btnname[BUFSZ]; commands = (Widget *) alloc((unsigned) num_names * sizeof (Widget)); num_args = 0; XtSetArg(args[num_args], XtNallowShellResize, True); num_args++; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; popup = XtCreatePopupShell(popup_name, transientShellWidgetClass, toplevel, args, num_args); XtOverrideTranslations( popup, XtParseTranslationTable("WM_PROTOCOLS: ec_delete()")); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(popup_translations)); num_args++; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; popform = XtCreateManagedWidget("topmenuform", formWidgetClass, popup, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNforceBars, False); num_args++; XtSetArg(args[num_args], XtNallowVert, True); num_args++; /*XtSetArg(args[num_args], XtNborderWidth, 0); num_args++;*/ XtSetArg(args[num_args], nhStr(XtNuseBottom), True); num_args++; XtSetArg(args[num_args], nhStr(XtNuseRight), True); num_args++; XtSetArg(args[num_args], nhStr(XtNtop), XtChainTop); num_args++; XtSetArg(args[num_args], nhStr(XtNbottom), XtChainBottom); num_args++; XtSetArg(args[num_args], nhStr(XtNleft), XtChainLeft); num_args++; XtSetArg(args[num_args], nhStr(XtNright), XtChainRight); num_args++; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(popup_translations)); num_args++; view = XtCreateManagedWidget("menuformview", viewportWidgetClass, popform, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(popup_translations)); num_args++; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; XtSetArg(args[num_args], nhStr(XtNdefaultDistance), 0); num_args++; *formp = form = XtCreateManagedWidget("menuform", formWidgetClass, view, args, num_args); /* * Get the default distance between objects in the viewport widget. * (Something is fishy here: 'distance' ends up being 0 but there * is a non-zero gap between the borders of the internal widgets. * It matches exactly the default value of 4 for defaultDistance.) */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNdefaultDistance), &distance); num_args++; XtSetArg(args[num_args], nhStr(XtNborderWidth), &border_width); num_args++; XtGetValues(view, args, num_args); if (distance < 1 || distance > 32766) distance = 4; /* * Create the label. */ num_args = 0; XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; label = XtCreateManagedWidget(popup_label, labelWidgetClass, form, args, num_args); cumulative_height = 0; XtSetArg(args[0], XtNheight, &height); XtGetValues(label, args, ONE); cumulative_height += distance + height; /* no border for label */ /* * Create the left button. */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), left_name); num_args++; #if 0 XtSetArg(args[num_args], nhStr(XtNshapeStyle), XmuShapeRoundedRectangle); num_args++; #endif Sprintf(btnname, "btn_%s", left_name); left = XtCreateManagedWidget(btnname, commandWidgetClass, form, args, num_args); XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0); skip = (distance < 4) ? 8 : 2 * distance; num_args = 0; XtSetArg(args[0], XtNheight, &height); XtGetValues(left, args, ONE); cumulative_height += distance + height + 2 * border_width; /* * Create right button. */ num_args = 0; XtSetArg(args[num_args], nhStr(XtNfromHoriz), left); num_args++; XtSetArg(args[num_args], nhStr(XtNhorizDistance), skip); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), label); num_args++; XtSetArg(args[num_args], nhStr(XtNlabel), right_name); num_args++; #if 0 XtSetArg(args[num_args], nhStr(XtNshapeStyle), XmuShapeRoundedRectangle); num_args++; #endif Sprintf(btnname, "btn_%s", right_name); right = XtCreateManagedWidget(btnname, commandWidgetClass, form, args, num_args); XtAddCallback(right, XtNcallback, right_callback, (XtPointer) 0); XtInstallAccelerators(form, left); XtInstallAccelerators(form, right); /* * Create and place the command widgets. */ for (i = 0, above = left, curr = commands; i < num_names; i++) { if (!widget_names[i]) continue; num_args = 0; XtSetArg(args[num_args], XtNtranslations, XtParseTranslationTable(popup_entry_translations)); num_args++; XtSetArg(args[num_args], nhStr(XtNfromVert), above); num_args++; if (above == left) { /* if first, we are farther apart */ XtSetArg(args[num_args], nhStr(XtNvertDistance), skip); num_args++; cumulative_height += skip; } else cumulative_height += distance; cumulative_height += height + 2 * border_width; *curr = XtCreateManagedWidget(widget_names[i], commandWidgetClass, form, args, num_args); XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) (ptrdiff_t) i); above = *curr++; } cumulative_height += distance; /* space at bottom of form */ /* * Now find the largest width. Start with width of left + right buttons * ('dismiss' + 'help' or 'quit' + 'random'), since they are adjacent. */ XtSetArg(args[0], XtNwidth, &max_width); XtGetValues(left, args, ONE); XtSetArg(args[0], XtNwidth, &width); XtGetValues(right, args, ONE); /* doesn't count leftmost 'distance + border_width' and rightmost 'border_width + distance' since all entries have those */ max_width = max_width + border_width + skip + border_width + width; /* Next, the title. */ XtSetArg(args[0], XtNwidth, &width); XtGetValues(label, args, ONE); if (width > max_width) max_width = width; /* Finally, the commands. */ for (i = 0, curr = commands; i < num_names; i++) { if (!widget_names[i]) continue; XtSetArg(args[0], XtNwidth, &width); XtGetValues(*curr, args, ONE); if (width > max_width) max_width = width; curr++; } /* * Re-do the two side-by-side widgets to take up half the width each. * * With max_width and skip both having even values, we never have to * tweak left or right to maybe be one pixel wider than the other. */ if (max_width % 2) ++max_width; XtSetArg(args[0], XtNwidth, &width); XtGetValues(left, args, ONE); XtSetArg(args[0], XtNwidth, &other_width); XtGetValues(right, args, ONE); if (width + border_width + skip / 2 < max_width / 2 && other_width + border_width + skip / 2 < max_width / 2) { /* both are narrower than half */ width = other_width = max_width / 2 - border_width - skip / 2; XtSetArg(args[0], XtNwidth, width); XtSetValues(left, args, ONE); XtSetArg(args[0], XtNwidth, other_width); XtSetValues(right, args, ONE); } else if (width + border_width + skip / 2 < max_width / 2) { /* 'other_width' (right) is half or more */ width = max_width - other_width - 2 * border_width - skip; XtSetArg(args[0], XtNwidth, width); XtSetValues(left, args, ONE); } else if (other_width + border_width + skip / 2 < max_width / 2) { /* 'width' (left) is half or more */ other_width = max_width - width - 2 * border_width - skip; XtSetArg(args[0], XtNwidth, other_width); XtSetValues(right, args, ONE); } else { ; /* both are exactly half... */ } /* * Finally, set all of the single line widgets to the largest width. */ XtSetArg(args[0], XtNwidth, max_width); XtSetValues(label, args, ONE); for (i = 0, curr = commands; i < num_names; i++) { if (!widget_names[i]) continue; XtSetArg(args[0], XtNwidth, max_width); XtSetValues(*curr, args, ONE); curr++; } if (command_widgets) *command_widgets = commands; else free((char *) commands); /* * If the menu's complete height is too big for the display, * forcing the height to be smaller will cause the vertical * scroll bar (enabled but not forced above) to be included. */ screen_height = XHeightOfScreen(XtScreen(popup)); screen_height -= appResources.extcmd_height_delta; /* NetHack.ad */ if (cumulative_height >= screen_height) { /* 25 is a guesstimate for scrollbar width; window manager might override the request for y==1 */ num_args = 0; XtSetArg(args[num_args], XtNy, 1); num_args++; XtSetArg(args[num_args], XtNwidth, max_width + 25); num_args++; XtSetArg(args[num_args], XtNheight, screen_height - 1); num_args++; XtSetValues(popup, args, num_args); } XtRealizeWidget(popup); XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1); /* during role selection, highlight "random" as pre-selected choice */ if (right_callback == ps_random && strchr(ps_randchars, '\n')) swap_fg_bg(right); return popup; } /*winmisc.c*/