162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * textbox.c -- implements the text box 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) 662306a36Sopenharmony_ci * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "dialog.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_cistatic int hscroll; 1262306a36Sopenharmony_cistatic int begin_reached, end_reached, page_length; 1362306a36Sopenharmony_cistatic const char *buf, *page; 1462306a36Sopenharmony_cistatic size_t start, end; 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Go back 'n' lines in text. Called by dialog_textbox(). 1862306a36Sopenharmony_ci * 'page' will be updated to point to the desired line in 'buf'. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_cistatic void back_lines(int n) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci int i; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci begin_reached = 0; 2562306a36Sopenharmony_ci /* Go back 'n' lines */ 2662306a36Sopenharmony_ci for (i = 0; i < n; i++) { 2762306a36Sopenharmony_ci if (*page == '\0') { 2862306a36Sopenharmony_ci if (end_reached) { 2962306a36Sopenharmony_ci end_reached = 0; 3062306a36Sopenharmony_ci continue; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci if (page == buf) { 3462306a36Sopenharmony_ci begin_reached = 1; 3562306a36Sopenharmony_ci return; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci page--; 3862306a36Sopenharmony_ci do { 3962306a36Sopenharmony_ci if (page == buf) { 4062306a36Sopenharmony_ci begin_reached = 1; 4162306a36Sopenharmony_ci return; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci page--; 4462306a36Sopenharmony_ci } while (*page != '\n'); 4562306a36Sopenharmony_ci page++; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* 5062306a36Sopenharmony_ci * Return current line of text. Called by dialog_textbox() and print_line(). 5162306a36Sopenharmony_ci * 'page' should point to start of current line before calling, and will be 5262306a36Sopenharmony_ci * updated to point to start of next line. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic char *get_line(void) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci int i = 0; 5762306a36Sopenharmony_ci static char line[MAX_LEN + 1]; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci end_reached = 0; 6062306a36Sopenharmony_ci while (*page != '\n') { 6162306a36Sopenharmony_ci if (*page == '\0') { 6262306a36Sopenharmony_ci end_reached = 1; 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci } else if (i < MAX_LEN) 6562306a36Sopenharmony_ci line[i++] = *(page++); 6662306a36Sopenharmony_ci else { 6762306a36Sopenharmony_ci /* Truncate lines longer than MAX_LEN characters */ 6862306a36Sopenharmony_ci if (i == MAX_LEN) 6962306a36Sopenharmony_ci line[i++] = '\0'; 7062306a36Sopenharmony_ci page++; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci if (i <= MAX_LEN) 7462306a36Sopenharmony_ci line[i] = '\0'; 7562306a36Sopenharmony_ci if (!end_reached) 7662306a36Sopenharmony_ci page++; /* move past '\n' */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return line; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci * Print a new line of text. 8362306a36Sopenharmony_ci */ 8462306a36Sopenharmony_cistatic void print_line(WINDOW *win, int row, int width) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci char *line; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci line = get_line(); 8962306a36Sopenharmony_ci line += MIN(strlen(line), hscroll); /* Scroll horizontally */ 9062306a36Sopenharmony_ci wmove(win, row, 0); /* move cursor to correct line */ 9162306a36Sopenharmony_ci waddch(win, ' '); 9262306a36Sopenharmony_ci waddnstr(win, line, MIN(strlen(line), width - 2)); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Clear 'residue' of previous line */ 9562306a36Sopenharmony_ci wclrtoeol(win); 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * Print a new page of text. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic void print_page(WINDOW *win, int height, int width) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int i, passed_end = 0; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci page_length = 0; 10662306a36Sopenharmony_ci for (i = 0; i < height; i++) { 10762306a36Sopenharmony_ci print_line(win, i, width); 10862306a36Sopenharmony_ci if (!passed_end) 10962306a36Sopenharmony_ci page_length++; 11062306a36Sopenharmony_ci if (end_reached && !passed_end) 11162306a36Sopenharmony_ci passed_end = 1; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci wnoutrefresh(win); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* 11762306a36Sopenharmony_ci * Print current position 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistatic void print_position(WINDOW *win) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci int percent; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci wattrset(win, dlg.position_indicator.atr); 12462306a36Sopenharmony_ci wbkgdset(win, dlg.position_indicator.atr & A_COLOR); 12562306a36Sopenharmony_ci percent = (page - buf) * 100 / strlen(buf); 12662306a36Sopenharmony_ci wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); 12762306a36Sopenharmony_ci wprintw(win, "(%3d%%)", percent); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * refresh window content 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, 13462306a36Sopenharmony_ci int cur_y, int cur_x) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci start = page - buf; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci print_page(box, boxh, boxw); 13962306a36Sopenharmony_ci print_position(dialog); 14062306a36Sopenharmony_ci wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 14162306a36Sopenharmony_ci wrefresh(dialog); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci end = page - buf; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Display text from a file in a dialog box. 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * keys is a null-terminated array 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ciint dialog_textbox(const char *title, const char *tbuf, int initial_height, 15262306a36Sopenharmony_ci int initial_width, int *_vscroll, int *_hscroll, 15362306a36Sopenharmony_ci int (*extra_key_cb)(int, size_t, size_t, void *), void *data) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci int i, x, y, cur_x, cur_y, key = 0; 15662306a36Sopenharmony_ci int height, width, boxh, boxw; 15762306a36Sopenharmony_ci WINDOW *dialog, *box; 15862306a36Sopenharmony_ci bool done = false; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci begin_reached = 1; 16162306a36Sopenharmony_ci end_reached = 0; 16262306a36Sopenharmony_ci page_length = 0; 16362306a36Sopenharmony_ci hscroll = 0; 16462306a36Sopenharmony_ci buf = tbuf; 16562306a36Sopenharmony_ci page = buf; /* page is pointer to start of page to be displayed */ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (_vscroll && *_vscroll) { 16862306a36Sopenharmony_ci begin_reached = 0; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for (i = 0; i < *_vscroll; i++) 17162306a36Sopenharmony_ci get_line(); 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (_hscroll) 17462306a36Sopenharmony_ci hscroll = *_hscroll; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cido_resize: 17762306a36Sopenharmony_ci getmaxyx(stdscr, height, width); 17862306a36Sopenharmony_ci if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN) 17962306a36Sopenharmony_ci return -ERRDISPLAYTOOSMALL; 18062306a36Sopenharmony_ci if (initial_height != 0) 18162306a36Sopenharmony_ci height = initial_height; 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci if (height > 4) 18462306a36Sopenharmony_ci height -= 4; 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci height = 0; 18762306a36Sopenharmony_ci if (initial_width != 0) 18862306a36Sopenharmony_ci width = initial_width; 18962306a36Sopenharmony_ci else 19062306a36Sopenharmony_ci if (width > 5) 19162306a36Sopenharmony_ci width -= 5; 19262306a36Sopenharmony_ci else 19362306a36Sopenharmony_ci width = 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* center dialog box on screen */ 19662306a36Sopenharmony_ci x = (getmaxx(stdscr) - width) / 2; 19762306a36Sopenharmony_ci y = (getmaxy(stdscr) - height) / 2; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci draw_shadow(stdscr, y, x, height, width); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci dialog = newwin(height, width, y, x); 20262306a36Sopenharmony_ci keypad(dialog, TRUE); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Create window for box region, used for scrolling text */ 20562306a36Sopenharmony_ci boxh = height - 4; 20662306a36Sopenharmony_ci boxw = width - 2; 20762306a36Sopenharmony_ci box = subwin(dialog, boxh, boxw, y + 1, x + 1); 20862306a36Sopenharmony_ci wattrset(box, dlg.dialog.atr); 20962306a36Sopenharmony_ci wbkgdset(box, dlg.dialog.atr & A_COLOR); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci keypad(box, TRUE); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* register the new window, along with its borders */ 21462306a36Sopenharmony_ci draw_box(dialog, 0, 0, height, width, 21562306a36Sopenharmony_ci dlg.dialog.atr, dlg.border.atr); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci wattrset(dialog, dlg.border.atr); 21862306a36Sopenharmony_ci mvwaddch(dialog, height - 3, 0, ACS_LTEE); 21962306a36Sopenharmony_ci for (i = 0; i < width - 2; i++) 22062306a36Sopenharmony_ci waddch(dialog, ACS_HLINE); 22162306a36Sopenharmony_ci wattrset(dialog, dlg.dialog.atr); 22262306a36Sopenharmony_ci wbkgdset(dialog, dlg.dialog.atr & A_COLOR); 22362306a36Sopenharmony_ci waddch(dialog, ACS_RTEE); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci print_title(dialog, title, width); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE); 22862306a36Sopenharmony_ci wnoutrefresh(dialog); 22962306a36Sopenharmony_ci getyx(dialog, cur_y, cur_x); /* Save cursor position */ 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Print first page of text */ 23262306a36Sopenharmony_ci attr_clear(box, boxh, boxw, dlg.dialog.atr); 23362306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci while (!done) { 23662306a36Sopenharmony_ci key = wgetch(dialog); 23762306a36Sopenharmony_ci switch (key) { 23862306a36Sopenharmony_ci case 'E': /* Exit */ 23962306a36Sopenharmony_ci case 'e': 24062306a36Sopenharmony_ci case 'X': 24162306a36Sopenharmony_ci case 'x': 24262306a36Sopenharmony_ci case 'q': 24362306a36Sopenharmony_ci case '\n': 24462306a36Sopenharmony_ci done = true; 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci case 'g': /* First page */ 24762306a36Sopenharmony_ci case KEY_HOME: 24862306a36Sopenharmony_ci if (!begin_reached) { 24962306a36Sopenharmony_ci begin_reached = 1; 25062306a36Sopenharmony_ci page = buf; 25162306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, 25262306a36Sopenharmony_ci cur_y, cur_x); 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci break; 25562306a36Sopenharmony_ci case 'G': /* Last page */ 25662306a36Sopenharmony_ci case KEY_END: 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci end_reached = 1; 25962306a36Sopenharmony_ci /* point to last char in buf */ 26062306a36Sopenharmony_ci page = buf + strlen(buf); 26162306a36Sopenharmony_ci back_lines(boxh); 26262306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case 'K': /* Previous line */ 26562306a36Sopenharmony_ci case 'k': 26662306a36Sopenharmony_ci case KEY_UP: 26762306a36Sopenharmony_ci if (begin_reached) 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci back_lines(page_length + 1); 27162306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case 'B': /* Previous page */ 27462306a36Sopenharmony_ci case 'b': 27562306a36Sopenharmony_ci case 'u': 27662306a36Sopenharmony_ci case KEY_PPAGE: 27762306a36Sopenharmony_ci if (begin_reached) 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci back_lines(page_length + boxh); 28062306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case 'J': /* Next line */ 28362306a36Sopenharmony_ci case 'j': 28462306a36Sopenharmony_ci case KEY_DOWN: 28562306a36Sopenharmony_ci if (end_reached) 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci back_lines(page_length - 1); 28962306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci case KEY_NPAGE: /* Next page */ 29262306a36Sopenharmony_ci case ' ': 29362306a36Sopenharmony_ci case 'd': 29462306a36Sopenharmony_ci if (end_reached) 29562306a36Sopenharmony_ci break; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci begin_reached = 0; 29862306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci case '0': /* Beginning of line */ 30162306a36Sopenharmony_ci case 'H': /* Scroll left */ 30262306a36Sopenharmony_ci case 'h': 30362306a36Sopenharmony_ci case KEY_LEFT: 30462306a36Sopenharmony_ci if (hscroll <= 0) 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (key == '0') 30862306a36Sopenharmony_ci hscroll = 0; 30962306a36Sopenharmony_ci else 31062306a36Sopenharmony_ci hscroll--; 31162306a36Sopenharmony_ci /* Reprint current page to scroll horizontally */ 31262306a36Sopenharmony_ci back_lines(page_length); 31362306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci case 'L': /* Scroll right */ 31662306a36Sopenharmony_ci case 'l': 31762306a36Sopenharmony_ci case KEY_RIGHT: 31862306a36Sopenharmony_ci if (hscroll >= MAX_LEN) 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci hscroll++; 32162306a36Sopenharmony_ci /* Reprint current page to scroll horizontally */ 32262306a36Sopenharmony_ci back_lines(page_length); 32362306a36Sopenharmony_ci refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case KEY_ESC: 32662306a36Sopenharmony_ci if (on_key_esc(dialog) == KEY_ESC) 32762306a36Sopenharmony_ci done = true; 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case KEY_RESIZE: 33062306a36Sopenharmony_ci back_lines(height); 33162306a36Sopenharmony_ci delwin(box); 33262306a36Sopenharmony_ci delwin(dialog); 33362306a36Sopenharmony_ci on_key_resize(); 33462306a36Sopenharmony_ci goto do_resize; 33562306a36Sopenharmony_ci default: 33662306a36Sopenharmony_ci if (extra_key_cb && extra_key_cb(key, start, end, data)) { 33762306a36Sopenharmony_ci done = true; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci delwin(box); 34362306a36Sopenharmony_ci delwin(dialog); 34462306a36Sopenharmony_ci if (_vscroll) { 34562306a36Sopenharmony_ci const char *s; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci s = buf; 34862306a36Sopenharmony_ci *_vscroll = 0; 34962306a36Sopenharmony_ci back_lines(page_length); 35062306a36Sopenharmony_ci while (s < page && (s = strchr(s, '\n'))) { 35162306a36Sopenharmony_ci (*_vscroll)++; 35262306a36Sopenharmony_ci s++; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (_hscroll) 35662306a36Sopenharmony_ci *_hscroll = hscroll; 35762306a36Sopenharmony_ci return key; 35862306a36Sopenharmony_ci} 359