From: jwalz Date: Sat, 5 Jan 2002 21:06:03 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: MOVE2GIT~3535 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=151a25df14ec455516f75756e94ff36836b1ca24;p=nethack *** empty log message *** --- diff --git a/win/X11/winmisc.c b/win/X11/winmisc.c new file mode 100644 index 000000000..2fb9010cd --- /dev/null +++ b/win/X11/winmisc.c @@ -0,0 +1,910 @@ +/* SCCS Id: @(#)winmisc.c 3.3 2000/05/21 */ +/* 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 /* for index() */ +#include + +#ifdef PRESERVE_NO_SYSV +# ifdef SYSV +# undef SYSV +# endif +# undef PRESERVE_NO_SYSV +#endif + +#include "hack.h" +#include "func_tab.h" +#include "winX.h" + + +static Widget extended_command_popup; +static Widget extended_command_form; +static Widget *extended_commands = 0; +static int extended_command_selected; /* index of the selected command; */ +static int ps_selected; /* index of selected role */ +#define PS_RANDOM (-50) +#define PS_QUIT (-75) +static const char ps_randchars[] = "*@"; +static const char ps_quitchars[] = "\033qQ"; + +#define EC_NCHARS 32 +static boolean ec_active = FALSE; +static int ec_nchars = 0; +static char ec_chars[EC_NCHARS]; +static Time ec_time; + +static const char extended_command_translations[] = + "#override\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 void NDECL(ec_dismiss); +static Widget FDECL(make_menu, (const char *,const char *,const char *, + const char *,XtCallbackProc, + const char *,XtCallbackProc, + int,const char **, Widget **, + XtCallbackProc,Widget *)); +static void NDECL(init_extended_commands_popup); +static void FDECL(ps_quit, (Widget,XtPointer,XtPointer)); +static void FDECL(ps_random, (Widget,XtPointer,XtPointer)); +static void FDECL(ps_select, (Widget,XtPointer,XtPointer)); + + +/* Player Selection -------------------------------------------------------- */ +/* ARGSUSED */ +static void +ps_quit(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = PS_QUIT; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +ps_random(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = PS_RANDOM; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +ps_select(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ps_selected = (int) client_data; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +void +ps_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + char rolechars[QBUFSZ]; + int i; + + (void)memset(rolechars, '\0', sizeof rolechars); /* for index() */ + 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 (index(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 = index(rolechars, ch); + if (!mark) mark = index(rolechars, lowc(ch)); + if (!mark) mark = index(rolechars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + char racechars[QBUFSZ]; + int i; + + (void)memset(racechars, '\0', sizeof racechars); /* for index() */ + for (i = 0; races[i].noun; ++i) { + ch = lowc(*races[i].noun); + /* this supports at most two races with the same first letter */ + if (index(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 = index(racechars, ch); + if (!mark) mark = index(racechars, lowc(ch)); + if (!mark) mark = index(racechars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + static char gendchars[] = "mf"; + + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(gendchars, ch); + if (!mark) mark = index(gendchars, lowc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(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(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch, *mark; + static char algnchars[] = "LNC"; + + ch = key_event_to_char((XKeyEvent *) event); + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } + mark = index(algnchars, ch); + if (!mark) mark = index(algnchars, highc(ch)); + if (!mark) { + if (index(ps_randchars, ch)) + ps_selected = PS_RANDOM; + else if (index(ps_quitchars, ch)) + ps_selected = PS_QUIT; + else { + X11_nhbell(); /* no such alignment */ + return; + } + } else + ps_selected = (int)(mark - algnchars); + exit_x_event = TRUE; +} + +/* Global functions ========================================================= */ +void +X11_player_selection() +{ + 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) { + clearlocks(); + X11_exit_nhwindows((char *)0); + 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\n", 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) { + clearlocks(); + X11_exit_nhwindows((char *)0); + 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\n", 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) { + clearlocks(); + X11_exit_nhwindows((char *)0); + 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\n", 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) { + clearlocks(); + X11_exit_nhwindows((char *)0); + 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\n", ps_selected); + } else { + flags.initalign = ps_selected; + } + } /* more than one alignment choice available */ + } +} + + +int +X11_get_ext_cmd() +{ + static Boolean initialized = False; + + if (!initialized) { + init_extended_commands_popup(); + initialized = True; + } + + extended_command_selected = -1; /* reset selected value */ + + 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); + + return extended_command_selected; +} + +/* End global functions ===================================================== */ + +/* Extended Command -------------------------------------------------------- */ +/* ARGSUSED */ +static void +extend_select(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + int selected = (int) client_data; + + if (extended_command_selected != selected) { + /* visibly deselect old one */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + + /* select new one */ + swap_fg_bg(extended_commands[selected]); + extended_command_selected = selected; + } + + nh_XtPopdown(extended_command_popup); + /* reset colors while popped down */ + swap_fg_bg(extended_commands[extended_command_selected]); + ec_active = FALSE; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +static void +extend_dismiss(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + ec_dismiss(); +} + +/* ARGSUSED */ +static void +extend_help(w, client_data, call_data) + Widget w; + XtPointer client_data, call_data; +{ + /* We might need to make it known that we already have one listed. */ + (void) doextlist(); +} + +/* ARGSUSED */ +void +ec_delete(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + ec_dismiss(); +} + +static void +ec_dismiss() +{ + /* unselect while still visible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = -1; /* dismiss */ + nh_XtPopdown(extended_command_popup); + ec_active = FALSE; + exit_x_event = TRUE; /* leave event loop */ +} + +/* ARGSUSED */ +void +ec_key(w, event, params, num_params) + Widget w; + XEvent *event; + String *params; + Cardinal *num_params; +{ + char ch; + int i; + XKeyEvent *xkey = (XKeyEvent *) event; + + ch = key_event_to_char(xkey); + + if (ch == '\0') { /* don't accept nul char/modifier event */ + /* don't beep */ + return; + } else if (index("\033\n\r", ch)) { + if (ch == '\033') { + /* unselect while still visible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = -1; /* dismiss */ + } + + nh_XtPopdown(extended_command_popup); + /* unselect while invisible */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + + exit_x_event = TRUE; /* leave event loop */ + ec_active = FALSE; + return; + } + + /* too much time has elapsed */ + if ((xkey->time - ec_time) > 500) + 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 (i = 0; extcmdlist[i].ef_txt; i++) { + if (extcmdlist[i].ef_txt[0] == '?') continue; + + if (!strncmp(ec_chars, extcmdlist[i].ef_txt, ec_nchars)) { + if (extended_command_selected != i) { + /* I should use set() and unset() actions, but how do */ + /* I send the an action to the widget? */ + if (extended_command_selected >= 0) + swap_fg_bg(extended_commands[extended_command_selected]); + extended_command_selected = i; + swap_fg_bg(extended_commands[extended_command_selected]); + } + break; + } + } +} + +/* + * Use our own home-brewed version menu because simpleMenu is designed to + * be used from a menubox. + */ +static void +init_extended_commands_popup() +{ + int i, num_commands; + const char **command_list; + + /* count commands */ + for (num_commands = 0; extcmdlist[num_commands].ef_txt; num_commands++) + ; /* do nothing */ + + /* If the last entry is "help", don't use it. */ + if (strcmp(extcmdlist[num_commands-1].ef_txt, "?") == 0) + --num_commands; + + command_list = + (const char **) alloc((unsigned)num_commands * sizeof(char *)); + + for (i = 0; i < num_commands; i++) + command_list[i] = extcmdlist[i].ef_txt; + + 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); + + free((char *)command_list); +} + +/* ------------------------------------------------------------------------- */ + +/* + * Create a popup widget of the following form: + * + * popup_label + * ----------- ------------ + * |left_name| |right_name| + * ----------- ------------ + * ------------------------ + * | name1 | + * ------------------------ + * ------------------------ + * | name2 | + * ------------------------ + * . + * . + * ------------------------ + * | nameN | + * ------------------------ + */ +static Widget +make_menu(popup_name, popup_label, popup_translations, + left_name, left_callback, + right_name, right_callback, + num_names, widget_names, command_widgets, name_callback, formp) + 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, form, label, above, left, right; + Widget *commands, *curr; + int i; + Arg args[8]; + Cardinal num_args; + Dimension width, max_width; + int distance, skip; + + + commands = (Widget *) alloc((unsigned)num_names * sizeof(Widget)); + + + num_args = 0; + XtSetArg(args[num_args], XtNallowShellResize, True); 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++; + *formp = form = XtCreateManagedWidget("menuform", + formWidgetClass, + popup, + args, num_args); + + /* Get the default distance between objects in the form widget. */ + num_args = 0; + XtSetArg(args[num_args], XtNdefaultDistance, &distance); num_args++; + XtGetValues(form, args, num_args); + + /* + * Create the label. + */ + num_args = 0; + XtSetArg(args[num_args], XtNborderWidth, 0); num_args++; + label = XtCreateManagedWidget(popup_label, + labelWidgetClass, + form, + args, num_args); + + /* + * Create the left button. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; +/* + XtSetArg(args[num_args], XtNshapeStyle, + XmuShapeRoundedRectangle); num_args++; +*/ + left = XtCreateManagedWidget(left_name, + commandWidgetClass, + form, + args, num_args); + XtAddCallback(left, XtNcallback, left_callback, (XtPointer) 0); + skip = 3*distance; /* triple the spacing */ + if(!skip) skip = 3; + + /* + * Create right button. + */ + num_args = 0; + XtSetArg(args[num_args], XtNfromHoriz, left); num_args++; + XtSetArg(args[num_args], XtNfromVert, label); num_args++; +/* + XtSetArg(args[num_args], XtNshapeStyle, + XmuShapeRoundedRectangle); num_args++; +*/ + right = XtCreateManagedWidget(right_name, + 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], XtNfromVert, above); num_args++; + if (above == left) { + /* if first, we are farther apart */ + XtSetArg(args[num_args], XtNvertDistance, skip); num_args++; + } + + *curr = XtCreateManagedWidget(widget_names[i], + commandWidgetClass, + form, + args, num_args); + XtAddCallback(*curr, XtNcallback, name_callback, (XtPointer) i); + above = *curr++; + } + + /* + * Now find the largest width. Start with the width dismiss + help + * buttons, since they are adjacent. + */ + XtSetArg(args[0], XtNwidth, &max_width); + XtGetValues(left, args, ONE); + XtSetArg(args[0], XtNwidth, &width); + XtGetValues(right, args, ONE); + max_width = max_width + width + distance; + + /* 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++; + } + + /* + * 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); + + XtRealizeWidget(popup); + XSetWMProtocols(XtDisplay(popup), XtWindow(popup), &wm_delete_window, 1); + + return popup; +}