From: PatR Date: Sun, 14 Mar 2021 02:18:53 +0000 (-0800) Subject: \#perminv, 2 of 2: implementation X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=946df19ea2fd39cdbe4f6618b1519953aaf90b68;p=nethack \#perminv, 2 of 2: implementation Add new '|' command, aka #perminv, which allows the player to send menu scrolling keystrokes to the persistent inventory window. Implemented for X11, where its usefulness is limited, and for curses, where it is more needed and also more fully functional. The interface can either prompt for one keystroke, act upon it, and return to normal play, or it can loop for multiple keystrokes until player types or . X11 does the former if the 'slow' application resource is False so that prompting uses popups, and the latter when 'slow' is True where prompting is in a fixed spot and doesn't end up causing the persistent inventory window to be stacked behind the map window. curses always does the loop-until-done approach. It also accepts up and down arrow keys to scroll one line at a time. Also adds two new menu scrolling commands, menu_shift_right (key '}' by default) and menu_shift_left ('{') if wincap2 flags contain WC2_MENU_SHIFT. Shifting allows different substrings of too-long lines to be seen. For X11, neither works because their handling requires a horizontal scrollbar and for some reason that escapes me our menus don't have one of those. If they did, shifts could work for all menus but a shifted window would hide the selection letters. So shifting would be most usefully done as: pan right, read more of any long lines, immediately pan back to the left. For curses, they only apply to the persistent inventory window. Shift right redraws it with class headers and inventory letters shown normally but the item descriptions omit their leftmost portion, showing more text towards the end. Shift left reverses that and does nothing if the beginning is already in view. Forward and backward scrolling while shifted leave the shift in place. --- diff --git a/dat/hh b/dat/hh index 75f87b33b..17d68bf96 100644 --- a/dat/hh +++ b/dat/hh @@ -32,6 +32,7 @@ S save save the game (to be continued later) and exit O options set options / what-is tell what a map symbol represents \ known display list of what's been discovered +| perminv interact with persistent inventory window instead of hero+map v version display version number V history display game history ^A again redo the previous command (^A denotes the keystroke CTRL-A) diff --git a/dat/opthelp b/dat/opthelp index 5b031eb03..c13294d3c 100644 --- a/dat/opthelp +++ b/dat/opthelp @@ -290,6 +290,8 @@ menu_* specify single character accelerators for menu commands. menu_last_page jump to the last page in a menu [|](tcwxq) menu_next_page advance to the next menu page [>](tcwxq) menu_previous_page back up to the previous menu page [<](tcwxq) + menu_shift_left pan view to left (perm_invent only) [{](cx) + menu_shift_right pan view to right (perm_invent only) [}](cx) menu_select_all select all items in a menu [.](tcwxq) menu_select_page select all items on this menu page [,](tcwq) menu_deselect_all deselect all items in a menu [-](tcwxq) diff --git a/doc/Guidebook.mn b/doc/Guidebook.mn index d271c8fe0..7bafb7c0c 100644 --- a/doc/Guidebook.mn +++ b/doc/Guidebook.mn @@ -35,7 +35,7 @@ .ds vr "NetHack 3.7 .ds f0 "\*(vr .ds f1 -.ds f2 "February 11, 2021 +.ds f2 "March 12, 2021 . .\" A note on some special characters: .\" \(lq = left double quote @@ -1068,6 +1068,20 @@ May be preceded by \(oq\f(CRm\fP\(cq to select preferred display order. Show discovered types for one class of objects. .lp "" May be preceded by \(oq\f(CRm\fP\(cq to select preferred display order. +.lp | +If persistent inventory display is supported and enabled (with the +.op perm_invent +option), interact with it instead of with the map. +.lp "" +Allows scrolling with the +menu_first_page, menu_previous_page, +menu_next_page, and menu_last_page +keys (\(oq\f(CR\(ha\fP\(cq, \(oq\f(CR<\fP\(cq, +\(oq\f(CR>\fP\(cq, \(oq\f(CR|\fP\(cq by default). +Some interfaces also support menu_shift_left and menu_shift_right +keys (\(oq\f(CR{\fP\(cq and \(oq\f(CR}\fP\(cq by default). +Use the \fIReturn\fP (aka \fIEnter\fP) or \fIEscape\fP key to +resume play. .lp ! Escape to a shell. See \(lq#shell\(rq below for more details. @@ -3828,6 +3842,20 @@ Default \(oq.\(cq. Menu character accelerator to select all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default \(oq,\(cq. +.lp menu_shift_left +Menu character accelerator to scroll a menu\(emone which has been +scrolled right\(emback to the left. +Implemented by curses for +.op perm_invent +only and by X11. +Default \(oq{\(cq. +.lp menu_shift_right +Menu character accelerator to scroll a menu which has text beyond the +right edge to the right. +Implemented by curses for +.op perm_invent +only and by X11. +Default \(oq}\(cq. ." .lp menu_tab_sep ." Format menu entries using TAB to separate columns (default off). ." Only applicable to some menus, and only useful to some interfaces. diff --git a/doc/Guidebook.tex b/doc/Guidebook.tex index 41bd4229d..e2ed921ae 100644 --- a/doc/Guidebook.tex +++ b/doc/Guidebook.tex @@ -45,7 +45,7 @@ %.au \author{Original version - Eric S. Raymond\\ (Edited and expanded for 3.7 by Mike Stephenson and others)} -\date{February 11, 2021} +\date{March 12, 2021} \maketitle @@ -1174,6 +1174,23 @@ Show discovered types for one class of objects. \\ .lp "" May be preceded by `{\tt m}' to select preferred display order. + +%.lp +\item[\tb{|}] +If persistent inventory display is supported and enabled (with the +{\it perm_invent\/} +option), interact with it instead of with the map. +\\ +%.lp "" +Allows scrolling with the +menu\verb+_+first\verb+_+page, menu\verb+_+previous\verb+_+page, +menu\verb+_+next\verb+_+page, and menu\verb+_+last\verb+_+page +keys (`{\tt \^{}}', `{\tt <}', `{\tt >}', `{\tt |}' by default). +Some interfaces also support menu_shift_left and menu_shift_right +keys (`{\tt \verb+{+}' and `{\tt \verb+}+}' by default). +Use the {\it Return\/} (aka {\it Enter\/}) or {\it Escape\/} key to +resume play. + %.lp \item[\tb{!}] Escape to a shell. @@ -4137,6 +4154,21 @@ Default `.'. Menu character accelerator to select all items on this page of a menu. Implemented by the Amiga, Gem and tty ports. Default `,'. + +%.lp +\item[\ib{menu\verb+_+shift\verb+_+left}] +Menu character accelerator to scroll a menu---one which has been +scrolled right---back to the left. +Implemented by curses for {\it perm\verb+_+invent\/} only and by X11. +Default `{\tt \verb+{+}'. + +%.lp +\item[\ib{menu\verb+_+shift\verb+_+right}] +Menu character accelerator to scroll a menu which has text beyond the +right edge to the right. +Implemented by curses for {\it perm\verb+_+invent\/} only and by X11. +Default `{\tt \verb+}+}'. +Default \(oq}\(cq. % %.lp % \item[\ib{menu\verb+_+tab\verb+_+sep}] % Format menu entries using TAB to separate columns (default off). diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 2c1e523b1..aa1e7ba68 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -823,6 +823,11 @@ show bones levels information in enlightenment at end of game or in explore and wizmode for #wizintrinsic, use any counts entered during menu selection give feedback when boolean options are toggled interactively ('O' command) +'|' command (#perminv) for interacting with persistent inventory display + (curses and X11 only) +menu_shift_left, menu_shift_right menu command keys; default '{' and '}' + (curses for perm_invent only; implemented for X11 too but menus for + it lack horizontal scroll bars so the shifts don't work there) Platform- and/or Interface-Specific New Features diff --git a/doc/window.doc b/doc/window.doc index 1c26bf952..bac8008b5 100644 --- a/doc/window.doc +++ b/doc/window.doc @@ -259,11 +259,16 @@ display_file(str, boolean complain) -- Display the file named str. Complain about missing files iff complain is TRUE. update_inventory(arg) - -- Indicate to the window port that the inventory has been - changed. - -- Merely calls display_inventory() for window-ports that - leave the window up, otherwise empty. - -- 'arg' is not used yet + -- For an argument of 0: + -- Indicate to the window port that the inventory has + been changed. + -- Merely calls display_inventory() for window-ports + that leave the window up, otherwise empty. + -- or for a non-zero argument: + -- Prompts the user for a menu scrolling action and + executes that. + -- May repeat until user finishes (typically by using + or but interface may use other means). doprev_message() -- Display previous messages. Used by the ^P command. -- On the tty-port this scrolls WIN_MESSAGE back one line. @@ -810,6 +815,8 @@ to support: | guicolor | WC2_GUICOLOR | wc2_guicolor |boolean | | hilite_status | WC2_HILITE_STATUS | wc2_hilite_status |strings | | hitpointbar | WC2_HITPOINTBAR | wc2_hitpointbar |boolean | + | menu_shift_left | WC2_MENU_SHIFT | n/a |char | + | menu_shift_right | WC2_MENU_SHIFT | n/a |char | | petattr | WC2_PETATTR | wc2_petattr |int | | selectsaved | WC2_SELECTSAVED | wc2_selectsaved |boolean | | softkeyboard | WC2_SOFTKEYBOARD | wc2_softkeyboard |boolean | diff --git a/include/extern.h b/include/extern.h index 90ff05575..a13c05a63 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1019,6 +1019,7 @@ extern int count_unidentified(struct obj *); extern void identify_pack(int, boolean); extern void learn_unseen_invent(void); extern void update_inventory(void); +extern int doperminv(void); extern void prinv(const char *, struct obj *, long); extern char *xprname(struct obj *, const char *, char, boolean, long, long); extern int ddoinv(void); @@ -1800,6 +1801,7 @@ extern void oc_to_str(char *, char *); extern void add_menu_cmd_alias(char, char); extern char get_menu_cmd_key(char); extern char map_menu_cmd(char); +extern char *collect_menu_keys(char *, unsigned, boolean); extern void show_menu_controls(winid, boolean); extern void assign_warnings(uchar *); extern char *nh_getenv(const char *); diff --git a/include/optlist.h b/include/optlist.h index a1b363330..2dc3cc940 100644 --- a/include/optlist.h +++ b/include/optlist.h @@ -274,7 +274,7 @@ opt_##a, NHOPTC(menu_last_page, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "jump to the last page in a menu") NHOPTC(menu_next_page, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, - "goto the next menu page") + "go to the next menu page") NHOPTB(menu_objsyms, 0, opt_in, set_in_game, Off, Yes, No, No, NoAlias, &iflags.menu_head_objsym) #ifdef TTY_GRAPHICS @@ -285,13 +285,17 @@ opt_##a, (boolean *) 0) #endif NHOPTC(menu_previous_page, 4, opt_in, set_in_config, No, Yes, No, No, - NoAlias, "goto the previous menu page") + NoAlias, "go to the previous menu page") NHOPTC(menu_search, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "search for a menu item") NHOPTC(menu_select_all, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "select all items in a menu") NHOPTC(menu_select_page, 4, opt_in, set_in_config, No, Yes, No, No, NoAlias, "select all items on this page of a menu") + NHOPTC(menu_shift_left, 4, opt_in, set_in_config, No, Yes, No, No, + NoAlias, "pan current menu page left") + NHOPTC(menu_shift_right, 4, opt_in, set_in_config, No, Yes, No, No, + NoAlias, "pan current menu page right") NHOPTB(menu_tab_sep, 0, opt_in, set_wizonly, Off, Yes, No, No, NoAlias, &iflags.menu_tab_sep) NHOPTB(menucolors, 0, opt_in, set_in_game, Off, Yes, Yes, No, NoAlias, diff --git a/include/winX.h b/include/winX.h index 4fc4279bd..06a6409e9 100644 --- a/include/winX.h +++ b/include/winX.h @@ -363,6 +363,7 @@ extern int x_event(int); extern void menu_delete(Widget, XEvent *, String *, Cardinal *); extern void menu_key(Widget, XEvent *, String *, Cardinal *); extern void x11_no_perminv(struct xwindow *); +extern void x11_scroll_perminv(int); extern void create_menu_window(struct xwindow *); extern void destroy_menu_window(struct xwindow *); diff --git a/include/wincurs.h b/include/wincurs.h index 252af8e6e..f9b70ca06 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -195,8 +195,9 @@ extern void curses_status_update(int, genericptr_t, int, int, int, /* cursinvt.c */ -extern void curses_update_inv(void); -extern void curses_add_inv(int, char, attr_t, const char *); +extern void curs_purge_perminv_data(boolean); +extern void curs_update_invt(int); +extern void curs_add_invt(int, char, attr_t, const char *); /* cursinit.c */ diff --git a/include/winprocs.h b/include/winprocs.h index 581a5dbc6..6b7df8aa8 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -231,7 +231,8 @@ extern * via non-display attribute flag */ #define WC2_SUPPRESS_HIST 0x8000L /* 16 putstr(WIN_MESSAGE) supports history * suppression via non-disp attr */ - /* 16 free bits */ +#define WC2_MENU_SHIFT 0x010000L /* 17 horizontal menu scrolling */ + /* 15 free bits */ #define ALIGN_LEFT 1 #define ALIGN_RIGHT 2 diff --git a/include/wintype.h b/include/wintype.h index 47b0b39bf..48c974e29 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -110,12 +110,15 @@ typedef struct mi { /* invalid winid */ #define WIN_ERR ((winid) -1) -/* menu window keyboard commands (may be mapped) */ +/* menu window keyboard commands (may be mapped); menu_shift_right and + menu_shift_left are for interacting with persistent inventory window */ /* clang-format off */ #define MENU_FIRST_PAGE '^' #define MENU_LAST_PAGE '|' #define MENU_NEXT_PAGE '>' #define MENU_PREVIOUS_PAGE '<' +#define MENU_SHIFT_RIGHT '}' +#define MENU_SHIFT_LEFT '{' #define MENU_SELECT_ALL '.' #define MENU_UNSELECT_ALL '-' #define MENU_INVERT_ALL '@' diff --git a/src/cmd.c b/src/cmd.c index 09156831d..6277dec28 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -1885,6 +1885,8 @@ struct ext_func_tab extcmdlist[] = { wiz_panic, IFBURIED | AUTOCOMPLETE | WIZMODECMD, NULL }, { 'p', "pay", "pay your shopping bill", dopay, 0, NULL }, + { '|', "perminv", "scroll persistent inventory display", + doperminv, IFBURIED | GENERALCMD, NULL }, { ',', "pickup", "pick up things at the current location", dopickup, 0, NULL }, { '\0', "polyself", "polymorph self", diff --git a/src/invent.c b/src/invent.c index bd36622e3..f0b0fd06e 100644 --- a/src/invent.c +++ b/src/invent.c @@ -2291,6 +2291,55 @@ update_inventory(void) (*windowprocs.win_update_inventory)(0); } +/* '|' command - call interface's persistent inventory manipulation routine */ +int +doperminv(void) +{ + /* + * If persistent inventory window is enabled, interact with it. + * + * Depending on interface, might accept and execute one scrolling + * request (MENU_{FIRST,NEXT,PREVIOUS,LAST}_PAGE) then return, + * or might stay and handle multiple requests until user finishes + * (typically by typing or but that's up to interface). + */ + + if (iflags.debug_fuzzer) + return 0; +#if 0 + /* [currently this would redraw the persistent inventory window + whether that's needed or not, so also reset any previous + scrolling; we don't want that if the interface only accepts + one scroll command at a time] */ + update_inventory(); /* make sure that it's up to date */ +#endif + + if ((windowprocs.wincap & WC_PERM_INVENT) == 0) { + /* [TODO? perhaps omit "by " if all the window ports + compiled into this binary lack support for perm_invent...] */ + pline("Persistent inventory display is not supported by '%s'.", + windowprocs.name); + + } else if (!iflags.perm_invent) { + pline( + "Persistent inventory ('perm_invent' option) is not presently enabled."); + + } else if (!g.invent) { + /* [should this be left for the interface to decide?] */ + pline("Persistent inventory display is empty."); + + } else { + /* note: we used to request a scrolling key here and pass that to + (*win_update_inventory)(key), but that limited the functionality + and also cluttered message history with prompt and response so + just send non-zero and have the interface be responsible for it */ + (*windowprocs.win_update_inventory)(1); + + } /* iflags.perm_invent */ + + return 0; +} + /* should of course only be called for things in invent */ static char obj_to_let(struct obj *obj) diff --git a/src/options.c b/src/options.c index 2dc6a2acf..4865ed675 100644 --- a/src/options.c +++ b/src/options.c @@ -224,6 +224,10 @@ static const menu_cmd_t default_menu_cmd_info[] = { "Unselect all items on current page" }, { "menu_search", MENU_SEARCH, "Search and invert matching items" }, + { "menu_shift_right", MENU_SHIFT_RIGHT, + "Pan current page to right (perm_invent only)" }, + { "menu_shift_left", MENU_SHIFT_LEFT, + "Pan current page to left (perm_invent only)" }, { (char *) 0, '\0', (char *) 0 } }; @@ -1562,6 +1566,20 @@ optfn_menu_select_page(int optidx, int req, boolean negated, return shared_menu_optfn(optidx, req, negated, opts, op); } +static int +optfn_menu_shift_left(int optidx, int req, boolean negated, + char *opts, char *op) +{ + return shared_menu_optfn(optidx, req, negated, opts, op); +} + +static int +optfn_menu_shift_right(int optidx, int req, boolean negated, + char *opts, char *op) +{ + return shared_menu_optfn(optidx, req, negated, opts, op); +} + /* end of shared key assignments for menu commands */ static int @@ -6814,6 +6832,46 @@ map_menu_cmd(char ch) return ch; } +/* get keystrokes that are used for menu scrolling operations which apply; + printable: for use in a prompt, non-printable: for yn_function() choices */ +char * +collect_menu_keys( + char *outbuf, /* at least big enough for 6 "M-^X" sequences +'\0'*/ + unsigned scrollmask, /* 1: backwards, "^<"; 2: forwards, ">|"; + * 4: left, "{"; 8: right, "}"; */ + boolean printable) /* False: output is string of raw characters, + * True: output is a string of visctrl() sequences; + * matters iff user has mapped any menu scrolling + * commands to control or meta characters */ +{ + struct menuscrollinfo { + char cmdkey; + uchar maskindx; + }; + static const struct menuscrollinfo scroll_keys[] = { + { MENU_FIRST_PAGE, 1 }, + { MENU_PREVIOUS_PAGE, 1 }, + { MENU_NEXT_PAGE, 2 }, + { MENU_LAST_PAGE, 2 }, + { MENU_SHIFT_LEFT, 4 }, + { MENU_SHIFT_RIGHT, 8 }, + }; + int i; + + outbuf[0] = '\0'; + for (i = 0; i < SIZE(scroll_keys); ++i) { + if (scrollmask & scroll_keys[i].maskindx) { + char c = get_menu_cmd_key(scroll_keys[i].cmdkey); + + if (printable) + Strcat(outbuf, visctrl(c)); + else + (void) strkitten(outbuf, c); + } + } + return outbuf; +} + /* Returns the fid of the fruit type; if that type already exists, it * returns the fid of that one; if it does not exist, it adds a new fruit * type to the chain and returns the new one. @@ -7381,6 +7439,7 @@ show_menu_controls(winid win, boolean dolist) char buf[BUFSZ]; const char *fmt, *arg; const struct xtra_cntrls *xcp; + boolean has_menu_shift = wc2_supported("menu_shift"); /* * Relies on spaces to line things up in columns, so must be rendered @@ -7390,11 +7449,16 @@ show_menu_controls(winid win, boolean dolist) putstr(win, 0, "Menu control keys:"); if (dolist) { /* key bindings help: '?i' */ int i; + char ch; fmt = "%-7s %s"; for (i = 0; default_menu_cmd_info[i].desc; i++) { + ch = default_menu_cmd_info[i].cmd; + if ((ch == MENU_SHIFT_RIGHT + || ch == MENU_SHIFT_LEFT) && !has_menu_shift) + continue; Sprintf(buf, fmt, - visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)), + visctrl(get_menu_cmd_key(ch)), default_menu_cmd_info[i].desc); putstr(win, 0, buf); } @@ -7436,6 +7500,16 @@ show_menu_controls(winid win, boolean dolist) visctrl(get_menu_cmd_key(MENU_LAST_PAGE)), "Last page"); putstr(win, 0, buf); + if (has_menu_shift) { + Sprintf(buf, mc_fmt, "Pan view", + visctrl(get_menu_cmd_key(MENU_SHIFT_RIGHT)), + "Right (perm_invent only)"); + putstr(win, 0, buf); + Sprintf(buf, mc_fmt, "", + visctrl(get_menu_cmd_key(MENU_SHIFT_LEFT)), + "Left"); + putstr(win, 0, buf); + } putstr(win, 0, ""); Sprintf(buf, mc_fmt, "Search", visctrl(get_menu_cmd_key(MENU_SEARCH)), @@ -8090,6 +8164,7 @@ static struct wc_Opt wc2_options[] = { { "guicolor", WC2_GUICOLOR }, { "hilite_status", WC2_HILITE_STATUS }, { "hitpointbar", WC2_HITPOINTBAR }, + { "menu_shift", WC2_MENU_SHIFT }, { "petattr", WC2_PETATTR }, { "softkeyboard", WC2_SOFTKEYBOARD }, /* name shown in 'O' menu is different */ diff --git a/win/X11/X11-issues.txt b/win/X11/X11-issues.txt index 1e4f84d89..838ae667d 100644 --- a/win/X11/X11-issues.txt +++ b/win/X11/X11-issues.txt @@ -5,7 +5,8 @@ back to the main window. More focus: although the menu window gets focus, the menu inside that window does not accept focus, so scrolling persistent inventory via -keyboard won't work. +keyboard won't work. [Does not affect the new '|' command which takes +input via yn_function() rather than directly from the perm_invent menu.] When persistent inventory window is displayed, an update that ought to make it grow won't do so even if there is room on the screen for that. @@ -31,3 +32,8 @@ approximately half the width of the vertical scrollbar. It wasn't inserted space in between two sides of map; it was undrawn map. If it had been drawn, the map would have been normal. +Menus which have entries that are too wide to display have their wide +lines truncated rather than adding a horizontal scrollbar. Resizing +larger doesn't recover truncated text and resizing smaller truncates +even more. + diff --git a/win/X11/winX.c b/win/X11/winX.c index c9bf7d2b2..97bbab271 100644 --- a/win/X11/winX.c +++ b/win/X11/winX.c @@ -105,7 +105,7 @@ struct window_procs X11_procs = { #ifdef STATUS_HILITES | WC2_RESET_STATUS | WC2_HILITE_STATUS #endif - | 0L ), + | WC2_MENU_SHIFT ), {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ X11_init_nhwindows, X11_player_selection, X11_askname, X11_get_nh_event, X11_exit_nhwindows, @@ -1241,7 +1241,7 @@ X11_destroy_nhwindow(winid window) /* display persistent inventory in its own window */ void -X11_update_inventory(int arg UNUSED) +X11_update_inventory(int arg) { struct xwindow *wp = 0; @@ -1252,7 +1252,11 @@ X11_update_inventory(int arg UNUSED) /* skip any calls to update_inventory() before in_moveloop starts */ if (g.program_state.in_moveloop || g.program_state.gameover) { updated_inventory = 1; /* hack to avoid mapping&raising window */ - (void) display_inventory((char *) 0, FALSE); + if (!arg) { + (void) display_inventory((char *) 0, FALSE); + } else { + x11_scroll_perminv(arg); + } updated_inventory = 0; } } else if ((wp = &window_list[WIN_INVEN]) != 0 diff --git a/win/X11/winmenu.c b/win/X11/winmenu.c index 4eb127310..fe52a52e2 100644 --- a/win/X11/winmenu.c +++ b/win/X11/winmenu.c @@ -52,6 +52,7 @@ static void select_match(struct xwindow *, char *); static void invert_all(struct xwindow *); static void invert_match(struct xwindow *, char *); static void menu_popdown(struct xwindow *); +static unsigned menu_scrollmask(struct xwindow *); static Widget menu_create_buttons(struct xwindow *, Widget, Widget); static void menu_create_entries(struct xwindow *, struct menu *); static void destroy_menu_entry_widgets(struct xwindow *); @@ -210,6 +211,8 @@ invert_line(struct xwindow *wp, x11_menu_item *curr, int which, long how_many) } } +static XEvent fake_perminv_event; + /* * Called when we get a key press event on a menu window. */ @@ -220,9 +223,11 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) struct menu_info_t *menu_info; x11_menu_item *curr; struct xwindow *wp; + Widget hbar, vbar; char ch; int count; - boolean selected_something; + boolean selected_something, + perminv_scrolling = (event == &fake_perminv_event); nhUse(params); nhUse(num_params); @@ -230,7 +235,10 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) wp = find_widget(w); menu_info = wp->menu_information; - ch = key_event_to_char((XKeyEvent *) event); + if (!perminv_scrolling) + ch = key_event_to_char((XKeyEvent *) event); + else + ch = (char) fake_perminv_event.type; if (ch == '\0') { /* don't accept nul char/modifier event */ /* don't beep */ @@ -238,7 +246,7 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) } /* don't exclude PICK_NONE menus; doing so disables scrolling via keys */ - if (menu_info->is_active) { /* waiting for input */ + if (menu_info->is_active || perminv_scrolling) { /* handle the input */ /* first check for an explicit selector match, so that it won't be overridden if it happens to duplicate a mapped menu command (':' to look inside a container vs ':' to select via search string) */ @@ -290,7 +298,6 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) X11_nhbell(); return; } else if (ch == MENU_FIRST_PAGE || ch == MENU_LAST_PAGE) { - Widget hbar, vbar; float top = (ch == MENU_FIRST_PAGE) ? 0.0 : 1.0; find_scrollbars(wp->w, wp->popup, &hbar, &vbar); @@ -298,8 +305,6 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) XtCallCallbacks(vbar, XtNjumpProc, &top); return; } else if (ch == MENU_NEXT_PAGE || ch == MENU_PREVIOUS_PAGE) { - Widget hbar, vbar; - find_scrollbars(wp->w, wp->popup, &hbar, &vbar); if (vbar) { float shown, top; @@ -312,6 +317,20 @@ menu_key(Widget w, XEvent *event, String *params, Cardinal *num_params) XtCallCallbacks(vbar, XtNjumpProc, &top); } return; + } else if (ch == MENU_SHIFT_RIGHT || ch == MENU_SHIFT_LEFT) { + find_scrollbars(wp->w, wp->popup, &hbar, &vbar); + if (hbar) { + float shown, halfshown, left; + Arg arg[2]; + + XtSetArg(arg[0], nhStr(XtNshown), &shown); + XtSetArg(arg[1], nhStr(XtNtopOfThumb), &left); + XtGetValues(hbar, arg, TWO); + halfshown = shown * 0.5; + left += ((ch == MENU_NEXT_PAGE) ? halfshown : -halfshown); + XtCallCallbacks(hbar, XtNjumpProc, &left); + } + return; } else if (index(menu_info->curr_menu.gacc, ch)) { group_accel: /* matched a group accelerator */ @@ -573,6 +592,39 @@ menu_popdown(struct xwindow *wp) wp->menu_information->is_up = FALSE; /* menu is down */ } +/* construct a bit mask specifying which scrolling operations are allowed */ +static unsigned +menu_scrollmask(struct xwindow *wp) +{ + float shown, top; + Arg args[2]; + Widget hbar = (Widget) 0, vbar = (Widget) 0; + unsigned scrlmask = 0U; + + /* set up args once, then use twice (provided that both scrollbars are + present); 'top' is left for horizontal scrollbar */ + (void) memset(args, 0, sizeof args); + XtSetArg(args[0], nhStr(XtNshown), &shown); + XtSetArg(args[1], nhStr(XtNtopOfThumb), &top); + + find_scrollbars(wp->w, wp->popup, &hbar, &vbar); + if (vbar) { + XtGetValues(vbar, args, TWO); + if (top > 0.0) + scrlmask |= 1U; /* not at top; can scroll up */ + if (top + shown < 1.0) + scrlmask |= 2U; /* more beyond bottom; can scroll down */ + } + if (hbar) { + XtGetValues(hbar, args, TWO); + if (top > 0.0) + scrlmask |= 4U; /* not at left edge; can scroll to left */ + if (top + shown < 1.0) + scrlmask |= 8U; /* more beyond right side; can scroll to right */ + } + return scrlmask; +} + /* Global functions ======================================================= */ /* called by X11_update_inventory() if persistent inventory is currently @@ -586,6 +638,91 @@ x11_no_perminv(struct xwindow *wp) } } +/* called by X11_update_inventory() if user has executed #perminv command */ +void +x11_scroll_perminv(int arg UNUSED) /* arg is always 1 */ +{ + static const char extrakeys[] = "\033 \n\r\003\177\b"; + char ch, menukeys[QBUFSZ]; + boolean save_is_active; + unsigned scrlmask; + Cardinal no_args = 0; + struct xwindow *wp = &window_list[WIN_INVEN]; + + /* caller has ensured that perm_invent is enabled, but the window + might not be displayed; if that's the case, display it */ + if (wp->type == NHW_MENU && !wp->menu_information->is_up) + X11_update_inventory(0); + /* if it's still not displayed for some reason, bail out now */ + if (wp->type != NHW_MENU || !wp->menu_information->is_up) { + X11_nhbell(); + return; + } + + do { + scrlmask = menu_scrollmask(wp); + (void) collect_menu_keys(menukeys, scrlmask, FALSE); + /* + * Add quitchars plus a few others to the player's scrolling keys. + * We accept some extra characters that menus usually ignore: + * ^C will be treated like , leaving menu positioned as-is + * and returning to play; or will be treated + * like and , reseting the menu to its top and + * returning to play; other charcters will either be rejected by + * yn_function or stay here for scrolling. + */ + Strcat(menukeys, extrakeys); + /* append any scrolling keys excluded by scrlmask, after the \033 + added by extrakeys; they'll be acceptable but not shown */ + (void) collect_menu_keys(eos(menukeys), ~scrlmask, FALSE); + + /* normally the perm_invent menu is not flagged 'is_active' because + it doesn't accept input, so menu_popdown() doesn't set the flag + for the event loop to exit; force 'is_active' while this prompt + is in progress so that it won't be left pending if player closes + the menu via mouse */ + save_is_active = wp->menu_information->is_active; + wp->menu_information->is_active = TRUE; + ch = X11_yn_function_core("Inventory scroll:", menukeys, + 0, YN_NO_LOGMESG); + if (wp->menu_information->is_up) + wp->menu_information->is_active = save_is_active; + else + ch = 0; + + if (ch == C('c')) /* ^C */ + ch = '\033'; + else if (ch == '\177' || ch == '\b') /* or */ + ch = '\n'; + + if (ch && ch != '\033') { + /* in case persistent inventory window is covered, force it + to be on top; does not grab pointer or keyboard focus */ + XMapRaised(XtDisplay(wp->popup), XtWindow(wp->popup)); + + /* the fake event never goes onto X's event queue; it is only + examined by menu_key(), so we shortcut the messy details in + favor of easy to handle union type code; might conceivably + confuse a sophisticated debugger so we should possibly redo + this to set it up properly: event->keyevent->keycode */ + fake_perminv_event.type = !index(quitchars, ch) ? ch + : MENU_FIRST_PAGE; + menu_key(wp->w, &fake_perminv_event, (String *) 0, &no_args); + fake_perminv_event.type = 0; + } + + /* if yn_function() is using a popup (the 'slow=False' setting + in NetHack.ad) for its prompt+response and there is any + overlap between the persistent inventory and main windows, + perm_invent would be pushed behind the map every iteration of + this loop, so handle only one character at a time for !slow */ + if (!appResources.slow) + break; + } while (ch && !index(quitchars, ch)); + + return; +} + void X11_start_menu(winid window, unsigned long mbehavior UNUSED) { diff --git a/win/curses/cursinvt.c b/win/curses/cursinvt.c index 05f9bc4b7..bee379394 100644 --- a/win/curses/cursinvt.c +++ b/win/curses/cursinvt.c @@ -8,15 +8,62 @@ #include "wincurs.h" #include "cursinvt.h" -/* Permanent inventory for curses interface */ +static void curs_invt_updated(WINDOW *); +static unsigned pi_article_skip(const char *); +static int curs_scroll_invt(WINDOW *); +static void curs_show_invt(WINDOW *); + +/* + * Persistent inventory (perm_invent) for curses interface. + * It resembles a menu but does not function like one. + */ + +/* pseudo menu line data */ +struct pi_line { + char *invtxt; /* class header or inventory item without letter prefix */ + attr_t c_attr; /* attribute for class headers */ + char letter; /* inventory letter; accelerator if this was really a menu; + * used to distinguish item lines from header lines and for + * display (no selection possible) */ +}; +static struct pi_line zero_pi_line; + +/* full perm_invent data; added to array[] one line at a time */ +struct pi_data { + struct pi_line *array; /* one element for each line of perminv */ + unsigned allocsize, inuseindx; /* num elements allocated and populated */ + unsigned rowoffset, coloffset; /* for displaying a subset due to space */ + unsigned widest; /* longest array[].invtxt */ +}; +#define PERMINV_CHUNK 20 /* number of elements to grow array[] when needed */ + +/* current persistent inventory */ +static struct pi_data pi = { (struct pi_line *) 0, 0, 0, 0, 0, 0 }; + +/* discard saved persistent inventory data */ +void +curs_purge_perminv_data(boolean everything) +{ + if (pi.array) { + unsigned idx; + + for (idx = 0; idx < pi.inuseindx; ++idx) + free(pi.array[idx].invtxt), pi.array[idx].invtxt = 0; + + if (everything) + free(pi.array), pi.array = 0, pi.allocsize = 0; + } + + pi.inuseindx = 0; + pi.rowoffset = pi.coloffset = 0; + pi.widest = 0; +} /* Runs when the game indicates that the inventory has been updated */ void -curses_update_inv(void) +curs_update_invt(int arg) { WINDOW *win = curses_get_nhwin(INV_WIN); - boolean border; - int x = 0, y = 0; /* Check if the inventory window is enabled in first place */ if (!win) { @@ -34,88 +81,344 @@ curses_update_inv(void) return; } - border = curses_window_has_border(INV_WIN); + /* clear anything displayed from previous update */ + werase(win); - /* Figure out drawing area */ - if (border) { - x++; - y++; - } + if (!arg) { - /* Clear the window as it is at the moment. */ - werase(win); + if (pi.array) /* previous data is obsolete */ + curs_purge_perminv_data(FALSE); - display_inventory(NULL, FALSE); + /* ask core to display full inventory in a PICK_NONE menu; + instead of setting up an ordinary menu, it will indirectly + call curs_add_invt() for each line (including class headers) */ + display_inventory(NULL, FALSE); + curs_invt_updated(win); - if (border) + } else { /* 'arg' is non-zero but otherwise unused */ + int scrollingdone; + + /* previous data is still valid; let player interactively scroll it */ + do { + scrollingdone = curs_scroll_invt(win); + curs_invt_updated(win); + } while (!scrollingdone); + + } + return; +} + +/* persistent inventory has been updated or scrolled/panned; re-display it */ +static void +curs_invt_updated(WINDOW *win) +{ + /* display collected inventory data, probably clipped */ + curs_show_invt(win); + + if (curses_window_has_border(INV_WIN)) box(win, 0, 0); wnoutrefresh(win); } -/* Adds an inventory item. 'y' is 1 rather than 0 for the first item. */ -void -curses_add_inv(int y, char accelerator, attr_t attr, const char *str) +/* scroll persistent inventory window forwards or backwards or side-to-side */ +static int +curs_scroll_invt(WINDOW *win UNUSED) { - WINDOW *win = curses_get_nhwin(INV_WIN); - int color = NO_COLOR; - int x = 0, width, height, available_width, stroffset = 0, - border = curses_window_has_border(INV_WIN) ? 1 : 0; - - /* Figure out where to draw the line */ - x += border; /* x starts at 0 and is incremented for border */ - y -= 1 - border; /* y starts at 1 and is decremented for non-border */ + char menukeys[QBUFSZ], qbuf[QBUFSZ]; + unsigned uheight, uwidth, uhalfwidth, scrlmask; + int ch, menucmd, height, width; + int res = 0; curses_get_window_size(INV_WIN, &height, &width); + uheight = (unsigned) height; + uwidth = (unsigned) width; + uhalfwidth = uwidth / 2; + + menukeys[0] = '\0'; + scrlmask = 0U; + if (pi.rowoffset > 0) + scrlmask |= 1U; /* include scroll backwards: ^ and < */ + if (pi.rowoffset + uheight <= pi.inuseindx) + scrlmask |= 2U; /* include scroll forwards: > and | */ + if (pi.coloffset > 0) + scrlmask |= 4U; /* include scroll left: { */ + if (pi.widest > pi.coloffset + uwidth) + scrlmask |= 8U; /* include scroll right: } */ + (void) collect_menu_keys(menukeys, scrlmask, TRUE); + + Snprintf(qbuf, sizeof qbuf, "Inventory scroll: [%s%s%s] ", + menukeys, *menukeys ? " " : "", "Ret Esc"); + + curses_count_window(qbuf); + ch = getch(); + curses_count_window((char *) 0); + curses_clear_unhighlight_message_window(); + + menucmd = (ch <= 0 || ch >= 255) ? ch : (int) (uchar) map_menu_cmd(ch); + switch (menucmd) { + case KEY_ESC: + case C('c'): /* ^C */ + /* for , leave window with scrolling as-is */ + res = -1; + break; + case '\n': + case '\r': + case '\b': + case '\177': + /* for , or when already on last page, + restore window to unscrolled */ + pi.rowoffset = pi.coloffset = 0; + res = 1; + break; + case ' ': + if (pi.rowoffset + uheight <= pi.inuseindx) { + pi.rowoffset = pi.coloffset = 0; + res = 1; + break; + } + /*FALLTHRU*/ + case KEY_RIGHT: + case KEY_NPAGE: + case MENU_NEXT_PAGE: + if (pi.inuseindx <= uheight) + pi.rowoffset = 0; + else if (pi.rowoffset + 2 * uheight <= pi.inuseindx) + pi.rowoffset += uheight; + else + pi.rowoffset = pi.inuseindx - (uheight - 1); + break; + case KEY_LEFT: + case KEY_PPAGE: + case MENU_PREVIOUS_PAGE: + if (pi.rowoffset >= uheight) + pi.rowoffset -= uheight; + else + pi.rowoffset = 0; + break; + + case KEY_END: + case MENU_LAST_PAGE: + if (pi.inuseindx > uheight) + pi.rowoffset = pi.inuseindx - (uheight - 1); + else + pi.rowoffset = 0; + break; + case KEY_HOME: + case MENU_FIRST_PAGE: + pi.rowoffset = 0; + break; + + case KEY_DOWN: + if (pi.rowoffset + uheight <= pi.inuseindx) + pi.rowoffset += 1; + break; + case KEY_UP: + if (pi.rowoffset > 0) + pi.rowoffset -= 1; + else + pi.rowoffset = 0; + break; + + case MENU_SHIFT_RIGHT: + if (pi.widest <= uwidth) { + pi.coloffset = 0; + } else { + pi.coloffset += uhalfwidth; + if (pi.coloffset + uwidth > pi.widest) + pi.coloffset = pi.widest - uwidth; + } + break; + case MENU_SHIFT_LEFT: + if (pi.coloffset >= uhalfwidth) + pi.coloffset -= uhalfwidth; + else + pi.coloffset = 0; + break; + +#if 0 + case MENU_SEARCH: + break; +#endif + case '\0': + default: + curses_nhbell(); + break; + } + return res; +} + +/* check 'str' for an article prefix and return length of that */ +static unsigned +pi_article_skip(const char *str) +{ + unsigned skip = 0; /* number of chars to skip when displaying str */ + /* - * TODO: - * Implement a way to switch focus from map to inventory so that - * the latter can be scrolled. Must not require use of a mouse. - * - * Also, when entries are omitted due to lack of space, mark the - * last line to indicate "there's more that you can't see" (like - * horizontal status window does for excess status conditions). - * Normal menu does this via 'page M of N'. + * if (!strncmpi(str, "a ", 2)) + * skip = 2; + * else if (!strncmpi(str, "an ", 3)) + * skip = 3; + * else if (!strncmpi(str, "the ", 4)) + * skip = 4; */ - if (y - border >= height) /* 'height' is already -2 for Top+Btm borders */ - return; - available_width = width; /* 'width' also already -2 for Lft+Rgt borders */ + if (str[0] == 'a') { + if (str[1] == ' ') + skip = 2; + else if (str[1] == 'n' && str[2] == ' ') + skip = 3; + } else if (str[0] == 't') { + if (str[1] == 'h' && str[2] == 'e' && str[3] == ' ') + skip = 4; + } + + return skip; +} + +/* store an inventory item or class header but don't display anything yet */ +void +curs_add_invt( + int linenum, /* line index; 1..n rather than 0..n-1 */ + char accelerator, /* selector letter for items, 0 for class headers */ + attr_t attr, /* curses attribute for headers, 0 for items */ + const char *str) /* formatted inventory item, without invlet prefix, + * or class header text */ +{ + unsigned idx, len; + struct pi_line newelement, *aptr = pi.array; + + if ((unsigned) linenum > pi.allocsize) { + pi.allocsize += PERMINV_CHUNK; + pi.array = (struct pi_line *) alloc(pi.allocsize * sizeof *aptr); + for (idx = 0; idx < pi.allocsize; ++idx) + pi.array[idx] = (idx < pi.inuseindx) ? aptr[idx] : zero_pi_line; + aptr = pi.array; + } + + newelement.invtxt = dupstr(str); + newelement.c_attr = attr ? A_NORMAL : NONE; /* override menu_headings */ + newelement.letter = accelerator; + aptr[pi.inuseindx++] = newelement; - wmove(win, y, x); + len = strlen(str); if (accelerator) { - /* despite being shown as a menu, nothing is selectable from the - persistent inventory window so avoid the usual highlighting of - inventory letters */ - wprintw(win, "%c) ", accelerator); - available_width -= 3; /* letter+parenthesis+space */ - /* - * Narrow the entries to fit more of the interesting text. Do so - * unconditionally rather than trying to figure whether it's needed. - * When 'sortpack' is enabled we could also strip out " of" - * from " of but if that's to be done, - * the core ought to do it. - * - * 'stroffset': defer skipping the article prefix until after menu - * color pattern matching has taken place so that the persistent - * inventory window always gets same coloring as regular inventory. - */ - if (!strncmpi(str, "a ", 2)) - stroffset = 2; - else if (!strncmpi(str, "an ", 3)) - stroffset = 3; - else if (!strncmpi(str, "the ", 4)) - stroffset = 4; + /* +3: ")c " inventory letter will be inserted before invtxt; + invtxt's "a "/"an "/"the " prefix, if any, will be skipped */ + len += 3; + if (len > pi.widest) + len -= pi_article_skip(str); + } + if (len > pi.widest) + pi.widest = len; +} + +/* display the inventory menu-like data collected in pi.array[] */ +static void +curs_show_invt(WINDOW *win) +{ + const char *str; + char accelerator, tmpbuf[BUFSZ]; + int attr, color; + unsigned lineno, stroffset, widest, left_col, right_col, + first_shown = 0, last_shown = 0, item_count = 0; + int x, y, width, height, available_width, + border = curses_window_has_border(INV_WIN) ? 1 : 0; + + x = border; /* same for every line; 1 if border, 0 otherwise */ + + curses_get_window_size(INV_WIN, &height, &width); + widest = pi.widest; + left_col = pi.coloffset + 1; + right_col = left_col + (unsigned) width - 1; + + for (lineno = 0; lineno < pi.rowoffset; ++lineno) + if (pi.array[lineno].letter) + ++item_count; + + for (lineno = pi.rowoffset; lineno < pi.inuseindx; ++lineno) { + str = pi.array[lineno].invtxt; + accelerator = pi.array[lineno].letter; + attr = pi.array[lineno].c_attr; + color = NO_COLOR; + + if (accelerator) + ++item_count; + + /* Figure out where to draw the line */ + y = (int) (lineno - pi.rowoffset) + border; + if (y - border >= height) { /* height already -2 for Top+Btm border */ + /* 'y' has grown too big; there are too many lines to fit */ + continue; /* skip, but still loop to update 'item_count' */ + } + available_width = width; /* width is already -2 for Lft+Rgt borders */ + + wmove(win, y, x); + + stroffset = 0; + if (accelerator) { /* inventory item line */ + if (!first_shown) + first_shown = item_count; + last_shown = item_count; + /* despite being shown as a menu, nothing is selectable from the + persistent inventory window so avoid the usual highlighting + of inventory letters */ + wprintw(win, "%c) ", accelerator); + available_width -= 3; /* letter+parenthesis+space */ + /* + * Narrow the entries to fit more of the interesting text, + * but defer the removal until after menu colors matching. + * Do so unconditionally rather than trying to figure whether + * it's needed. When 'sortpack' is enabled we could also strip + * out " of" from " of " + * but if that's to be done, the core ought to do it. + */ + stroffset = pi_article_skip(str); /* to skip "a "/"an "/"the " */ + /* if/when scrolled right, invtxt for item lines gets shifted */ + stroffset += pi.coloffset; + + /* only perform menu coloring on item entries, not subtitles */ + if (iflags.use_menu_color) { + attr = 0; + get_menu_coloring(str, &color, (int *) &attr); + attr = curses_convert_attr(attr); + } + } + + if (stroffset < strlen(str)) { + if (color == NO_COLOR) + color = NONE; + curses_menu_color_attr(win, color, attr, ON); + wprintw(win, "%.*s", available_width, str + stroffset); + curses_menu_color_attr(win, color, attr, OFF); + } + + wclrtoeol(win); + } /* lineno loop */ + + if (pi.inuseindx > (unsigned) height) { + /* some lines aren't shown; overwrite rightmost portion of + last line with something like "[1-24 of 30>"; right justified + so that the line might still show something useful; could be on + line of its own, in which case we needed to erase that first */ + y = height - (1 - border); + if ((unsigned) y == pi.inuseindx - pi.rowoffset) { + wmove(win, y, x); + wclrtoeol(win); + } + Sprintf(tmpbuf, "%c%u-%u of %u%c", + (first_shown > 1) ? '<' : '[', + first_shown, last_shown, item_count, + (last_shown < item_count) ? '>' : ']'); + mvwaddstr(win, y, x + (width - (int) strlen(tmpbuf)), tmpbuf); } - /* only perform menu coloring on item entries, not subtitles */ - if (accelerator && iflags.use_menu_color) { - attr = 0; - get_menu_coloring(str, &color, (int *) &attr); - attr = curses_convert_attr(attr); + if (widest > (unsigned) width) { + /* some columns aren't shown; overwrite rightmost portion of + first line with something like "[1-25 of 40}" */ + Sprintf(tmpbuf, "%c%u-%u of %u%c", + (left_col > 1) ? '{' : '[', + left_col, right_col, widest, + (right_col < widest) ? '}' : ']'); + mvwaddstr(win, border, x + (width - (int) strlen(tmpbuf)), tmpbuf); } - if (color == NO_COLOR) - color = NONE; - curses_menu_color_attr(win, color, attr, ON); - wprintw(win, "%.*s", available_width, str + stroffset); - curses_menu_color_attr(win, color, attr, OFF); - wclrtoeol(win); + return; } diff --git a/win/curses/cursinvt.h b/win/curses/cursinvt.h index fc367ab2b..569b1ccb6 100644 --- a/win/curses/cursinvt.h +++ b/win/curses/cursinvt.h @@ -9,6 +9,6 @@ /* Global declarations */ -void curses_update_inv(void); +void curses_update_inv(int); #endif /* CURSINVT_H */ diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 79104a800..835762546 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -48,7 +48,7 @@ struct window_procs curses_procs = { #endif | WC2_FLUSH_STATUS | WC2_TERM_SIZE | WC2_STATUSLINES | WC2_WINDOWBORDERS | WC2_PETATTR | WC2_GUICOLOR - | WC2_SUPPRESS_HIST), + | WC2_SUPPRESS_HIST | WC2_MENU_SHIFT), {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, /* color availability */ curses_init_nhwindows, curses_player_selection, @@ -436,6 +436,7 @@ curses_destroy_nhwindow(winid wid) curses_status_finish(); /* discard cached status data */ break; case INV_WIN: + curs_purge_perminv_data(TRUE); iflags.perm_invent = 0; /* avoid unexpected update_inventory() */ break; case MAP_WIN: @@ -568,7 +569,7 @@ curses_add_menu(winid wid, const glyph_info *glyphinfo, /* persistent inventory window; nothing is selectable; omit glyphinfo because perm_invent is to the side of the map so usually cramped for space */ - curses_add_inv(inv_update, accelerator, curses_attr, str); + curs_add_invt(inv_update, accelerator, curses_attr, str); inv_update++; return; } @@ -631,24 +632,34 @@ curses_select_menu(winid wid, int how, MENU_ITEM_P ** selected) } void -curses_update_inventory(int arg UNUSED) +curses_update_inventory(int arg) { /* Don't do anything if perm_invent is off unless it was on and player just changed the option. */ if (!iflags.perm_invent) { if (curses_get_nhwin(INV_WIN)) { curs_reset_windows(TRUE, FALSE); + curs_purge_perminv_data(FALSE); } return; } - /* Update inventory sidebar. NetHack uses normal menu functions - when drawing the inventory, and we don't want to change the - underlying code. So instead, track if an inventory update is - being performed with a static variable. */ - inv_update = 1; - curses_update_inv(); - inv_update = 0; + /* skip inventory updating during character initialization */ + if (!g.program_state.in_moveloop && !g.program_state.gameover) + return; + + if (!arg) { + /* Update inventory sidebar. NetHack uses normal menu functions + when gathering the inventory, and we don't want to change the + underlying code. So instead, track if an inventory update is + being performed with a static variable. */ + inv_update = 1; + curs_update_invt(0); + inv_update = 0; + } else { + /* perform scrolling operations on persistent inventory window */ + curs_update_invt(arg); + } } /*