From be966cfe5aaa017146435e2342a83e9610c02595 Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 2 Apr 2019 01:11:59 -0700 Subject: [PATCH] curses ^P msg_window:Full This changes the recently added msg_window:f for curses to start viewing the old messages on the last page rather than the first. For msg_window:Reversed (the default for curses) and for either direction when all of the message history happens to fit on one page, there's no change. But for multiple pages, the FIFO feedback now pads the top of the first page with blank lines so that the last page is full, and it starts out showing that last page first. So if you only want to go back few or several messages, they will be in view immediately. Old layout: |first message (oldest) | |1st message of last page | |2nd message of 1st page | | ... | | ... | |final (most recent) mesg | | ... | | (blank filler) | |last message of 1st page | | (blank filler) | | (1 of 2) => | | <= (2 of 2) | and ^P started with first page visible and needed normal menu handling, or '>' or '|', to go forward to view the most recent messages. New layout: |1st message of last page | | (blank filler) | |2nd message of last page | | (blank filler) | | ... | |first message (oldest) | | ... | | ... | |final (most recent) | |last message of 1st page | | <= (2 of 2) | | (1 of 2) => | and ^P starts on last page (two of two in this example) but can go back with '<' and '^'. So if the total size takes one and third pages (which isn't uncommon for the default number of kept messages), you'll see 3/4 of the most recent messages on the initial screen, then you can page backward if you want to see the other 1/4. The page indicator is deliberately drawn a bit differently just to draw attention to the fact you're starting on the last page. I'm not sure whether that is actually worthwhile but it was trivial to do. --- include/wincurs.h | 1 + win/curses/cursdial.c | 142 +++++++++++++++++++++++++++++++++--------- win/curses/cursdial.h | 1 + win/curses/cursmesg.c | 2 + 4 files changed, 117 insertions(+), 29 deletions(-) diff --git a/include/wincurs.h b/include/wincurs.h index 165facaba..1dc6400d5 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -182,6 +182,7 @@ extern void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel); +extern void curs_menu_set_bottom_heavy(winid); extern void curses_finalize_nhmenu(winid wid, const char *prompt); extern int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P **_selected); extern boolean curses_menu_exists(winid wid); diff --git a/win/curses/cursdial.c b/win/curses/cursdial.c index deee077fd..8ec5d20b5 100644 --- a/win/curses/cursdial.c +++ b/win/curses/cursdial.c @@ -81,6 +81,7 @@ typedef struct nhm { int height; /* Window height of menu */ int width; /* Window width of menu */ boolean reuse_accels; /* Non-unique accelerators per page */ + boolean bottom_heavy; /* display multi-page menu starting at end */ struct nhm *prev_menu; /* Pointer to previous entry */ struct nhm *next_menu; /* Pointer to next entry */ } nhmenu; @@ -91,6 +92,8 @@ typedef enum menu_op_type { INVERT } menu_op; +static nhmenu_item *curs_new_menu_item(winid, const char *); +static void curs_pad_menu(nhmenu *, boolean); static nhmenu *get_menu(winid wid); static char menu_get_accel(boolean first); static void menu_determine_pages(nhmenu *menu); @@ -379,6 +382,7 @@ curses_ext_cmd() starty = 0; if (iflags.wc_popup_dialog) { /* Prompt in popup window */ int x0, y0, w, h; /* bounding coords of popup */ + extwin2 = curses_create_window(25, 1, UP); wrefresh(extwin2); /* create window inside window to prevent overwriting of border */ @@ -523,6 +527,7 @@ curses_create_nhmenu(winid wid) new_menu->height = 0; new_menu->width = 0; new_menu->reuse_accels = FALSE; + new_menu->bottom_heavy = FALSE; return; } @@ -534,6 +539,7 @@ curses_create_nhmenu(winid wid) new_menu->height = 0; new_menu->width = 0; new_menu->reuse_accels = FALSE; + new_menu->bottom_heavy = FALSE; new_menu->next_menu = NULL; if (nhmenus == NULL) { /* no menus in memory yet */ @@ -548,15 +554,38 @@ curses_create_nhmenu(winid wid) } } +static nhmenu_item * +curs_new_menu_item(winid wid, const char *str) +{ + char *new_str; + nhmenu_item *new_item; + new_str = curses_copy_of(str); + curses_rtrim(new_str); + new_item = (nhmenu_item *) alloc((unsigned) sizeof (nhmenu_item)); + new_item->wid = wid; + new_item->glyph = NO_GLYPH; + new_item->identifier = zeroany; + new_item->accelerator = '\0';; + new_item->group_accel = '\0'; + new_item->attr = 0; + new_item->str = new_str; + new_item->presel = FALSE; + new_item->selected = FALSE; + new_item->page_num = 0; + new_item->line_num = 0; + new_item->num_lines = 0; + new_item->count = -1; + new_item->next_item = NULL; + return new_item; +} /* Add a menu item to the given menu window */ void -curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, +curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel) { - char *new_str; nhmenu_item *new_item, *current_items, *menu_item_ptr; nhmenu *current_menu = get_menu(wid); @@ -570,23 +599,13 @@ curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, return; } - new_str = curses_copy_of(str); - curses_rtrim((char *) new_str); - new_item = (nhmenu_item *) alloc((unsigned) sizeof (nhmenu_item)); - new_item->wid = wid; + new_item = curs_new_menu_item(wid, str); new_item->glyph = glyph; new_item->identifier = *identifier; new_item->accelerator = accelerator; new_item->group_accel = group_accel; new_item->attr = attr; - new_item->str = new_str; new_item->presel = presel; - new_item->selected = FALSE; - new_item->page_num = 0; - new_item->line_num = 0; - new_item->num_lines = 0; - new_item->count = -1; - new_item->next_item = NULL; current_items = current_menu->entries; menu_item_ptr = current_items; @@ -603,6 +622,67 @@ curses_add_nhmenu_item(winid wid, int glyph, const ANY_P * identifier, } } +/* for menu->bottom_heavy -- insert enough blank lines at top of + first page to make the last page become a full one */ +static void +curs_pad_menu(nhmenu *current_menu, boolean do_pad UNUSED) +{ + nhmenu_item *menu_item_ptr; + int numpages = current_menu->num_pages; + + /* caller has already called menu_win_size() */ + menu_determine_pages(current_menu); /* sets 'menu->num_pages' */ + numpages = current_menu->num_pages; + /* pad beginning of menu so that partial last page becomes full; + might be slightly less than full if any entries take multiple + lines and the padding would force those to span page boundary + and that gets prevented; so we re-count the number of pages + with every insertion instead of trying to calculate the number + of them to add */ + do { + menu_item_ptr = curs_new_menu_item(current_menu->wid, ""); + menu_item_ptr->next_item = current_menu->entries; + current_menu->entries->prev_item = menu_item_ptr; + current_menu->entries = menu_item_ptr; + current_menu->num_entries += 1; + + menu_determine_pages(current_menu); + } while (current_menu->num_pages == numpages); + + /* we inserted blank lines at beginning until final entry spilled + over to another page; take the most recent blank one back out */ + current_menu->num_entries -= 1; + current_menu->entries = menu_item_ptr->next_item; + current_menu->entries->prev_item = (nhmenu_item *) 0; + free((genericptr_t) menu_item_ptr->str); + free((genericptr_t) menu_item_ptr); + + /* reset page count; shouldn't need to re-count */ + current_menu->num_pages = numpages; + return; +} + +/* mark ^P message recall menu, for msg_window:full (FIFO), where we'll + start viewing on the last page so be able to see most recent immediately */ +void +curs_menu_set_bottom_heavy(winid wid) +{ + nhmenu *menu = get_menu(wid); + + /* + * Called after end_menu + finalize_nhmenu, + * before select_menu + display_nhmenu. + */ + menu_win_size(menu); /* (normally not done until display_nhmenu) */ + if (menu_is_multipage(menu, menu->width, menu->height)) { + menu->bottom_heavy = TRUE; + + /* insert enough blank lines at top of first page to make the + last page become a full one */ + curs_pad_menu(menu, TRUE); + } + return; +} /* No more entries are to be added to menu, so details of the menu can be finalized in memory */ @@ -699,8 +779,8 @@ curses_display_nhmenu(winid wid, int how, MENU_ITEM_P ** _selected) } if (count != num_chosen) { - impossible("curses_display_nhmenu: Selected items less than " - "expected number"); + impossible( + "curses_display_nhmenu: Selected items less than expected number"); } } @@ -753,7 +833,7 @@ curses_del_menu(winid wid, boolean del_wid_too) if ((tmpmenu = current_menu->prev_menu) != NULL) { tmpmenu->next_menu = current_menu->next_menu; } else { - nhmenus = current_menu->next_menu; /* New head mode or NULL */ + nhmenus = current_menu->next_menu; /* New head node or NULL */ } if ((tmpmenu = current_menu->next_menu) != NULL) { tmpmenu->prev_menu = current_menu->prev_menu; @@ -821,7 +901,7 @@ menu_is_multipage(nhmenu *menu, int width, int height) int curline = 0; nhmenu_item *menu_item_ptr = menu->entries; - if (strlen(menu->prompt) > 0) { + if (*menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } @@ -864,7 +944,7 @@ menu_determine_pages(nhmenu *menu) int page_end = height; - if (strlen(menu->prompt) > 0) { + if (*menu->prompt) { curline += curses_num_lines(menu->prompt, width) + 1; } @@ -932,8 +1012,7 @@ menu_win_size(nhmenu *menu) /* Add space for accelerator */ curentrywidth = strlen(menu_item_ptr->str) + 4; #if 0 /* FIXME: menu glyphs */ - if (menu_item_ptr->glyph != NO_GLYPH - && iflags.use_menu_glyphs) + if (menu_item_ptr->glyph != NO_GLYPH && iflags.use_menu_glyphs) curentrywidth += 2; #endif } @@ -989,7 +1068,7 @@ static void menu_display_page(nhmenu *menu, WINDOW * win, int page_num) { nhmenu_item *menu_item_ptr; - int count, curletter, entry_cols, start_col, num_lines, footer_x; + int count, curletter, entry_cols, start_col, num_lines; char *tmpstr; boolean first_accel = TRUE; int color = NO_COLOR; @@ -1112,20 +1191,25 @@ menu_display_page(nhmenu *menu, WINDOW * win, int page_num) } if (menu->num_pages > 1) { - footer_x = menu->width - strlen("<- (Page X of Y) ->"); - if (menu->num_pages > 9) { /* Unlikely */ - footer_x -= 2; + int footer_x, footwidth, shoesize = menu->num_pages; + + footwidth = (int) (sizeof "<- (Page X of Y) ->" - sizeof ""); + while (shoesize >= 10) { /* possible for pickup from big piles... */ + /* room for wider feet; extra digit for both X and Y */ + footwidth += 2; + shoesize /= 10; } - mvwprintw(win, menu->height, footer_x + 3, "(Page %d of %d)", - page_num, menu->num_pages); + footer_x = !menu->bottom_heavy ? (menu->width - footwidth) : 2; if (page_num != 1) { curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); mvwaddstr(win, menu->height, footer_x, "<="); curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); } + mvwprintw(win, menu->height, footer_x + 2, " (Page %d of %d) ", + page_num, menu->num_pages); if (page_num != menu->num_pages) { curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, ON); - mvwaddstr(win, menu->height, menu->width - 2, "=>"); + mvwaddstr(win, menu->height, footer_x + footwidth - 2, "=>"); curses_toggle_color_attr(win, HIGHLIGHT_COLOR, NONE, OFF); } } @@ -1142,13 +1226,13 @@ menu_get_selections(WINDOW * win, nhmenu *menu, int how) int curletter; int count = -1; int count_letter = '\0'; - int curpage = 1; + int curpage = !menu->bottom_heavy ? 1 : menu->num_pages; int num_selected = 0; boolean dismiss = FALSE; char search_key[BUFSZ]; nhmenu_item *menu_item_ptr = menu->entries; - menu_display_page(menu, win, 1); + menu_display_page(menu, win, curpage); while (!dismiss) { curletter = getch(); diff --git a/win/curses/cursdial.h b/win/curses/cursdial.h index 0e12be6a5..acc6f855b 100644 --- a/win/curses/cursdial.h +++ b/win/curses/cursdial.h @@ -16,6 +16,7 @@ void curses_create_nhmenu(winid wid); void curses_add_nhmenu_item(winid wid, int glyph, const ANY_P *identifier, CHAR_P accelerator, CHAR_P group_accel, int attr, const char *str, BOOLEAN_P presel); +void curs_menu_set_bottom_heavy(winid); void curses_finalize_nhmenu(winid wid, const char *prompt); int curses_display_nhmenu(winid wid, int how, MENU_ITEM_P **_selected); boolean curses_menu_exists(winid wid); diff --git a/win/curses/cursmesg.c b/win/curses/cursmesg.c index c3d0aa565..2c1decb9a 100644 --- a/win/curses/cursmesg.c +++ b/win/curses/cursmesg.c @@ -313,6 +313,8 @@ curses_prev_mesg() "[No past messages available.]", FALSE); curses_end_menu(wid, ""); + if (!do_lifo) + curs_menu_set_bottom_heavy(wid); curses_select_menu(wid, PICK_NONE, &selected); curses_del_wid(wid); } -- 2.40.0