yann@1: /* yann@1: * textbox.c -- implements the text box yann@1: * yann@1: * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) yann@1: * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) yann@1: * yann@1: * This program is free software; you can redistribute it and/or yann@1: * modify it under the terms of the GNU General Public License yann@1: * as published by the Free Software Foundation; either version 2 yann@1: * of the License, or (at your option) any later version. yann@1: * yann@1: * This program is distributed in the hope that it will be useful, yann@1: * but WITHOUT ANY WARRANTY; without even the implied warranty of yann@1: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the yann@1: * GNU General Public License for more details. yann@1: * yann@1: * You should have received a copy of the GNU General Public License yann@1: * along with this program; if not, write to the Free Software yann@1: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. yann@1: */ yann@1: yann@1: #include "dialog.h" yann@1: yann@1: static void back_lines(int n); yann@1: static void print_page(WINDOW * win, int height, int width); yann@1: static void print_line(WINDOW * win, int row, int width); yann@1: static char *get_line(void); yann@1: static void print_position(WINDOW * win); yann@1: yann@1: static int hscroll; yann@1: static int begin_reached, end_reached, page_length; yann@1: static const char *buf; yann@1: static const char *page; yann@1: yann@1: /* yann@1: * refresh window content yann@1: */ yann@1: static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, yann@1: int cur_y, int cur_x) yann@1: { yann@1: print_page(box, boxh, boxw); yann@1: print_position(dialog); yann@1: wmove(dialog, cur_y, cur_x); /* Restore cursor position */ yann@1: wrefresh(dialog); yann@1: } yann@1: yann@1: yann@1: /* yann@1: * Display text from a file in a dialog box. yann@1: */ yann@1: int dialog_textbox(const char *title, const char *tbuf, yann@1: int initial_height, int initial_width) yann@1: { yann@1: int i, x, y, cur_x, cur_y, key = 0; yann@1: int height, width, boxh, boxw; yann@1: int passed_end; yann@1: WINDOW *dialog, *box; yann@1: yann@1: begin_reached = 1; yann@1: end_reached = 0; yann@1: page_length = 0; yann@1: hscroll = 0; yann@1: buf = tbuf; yann@1: page = buf; /* page is pointer to start of page to be displayed */ yann@1: yann@1: do_resize: yann@1: getmaxyx(stdscr, height, width); yann@1: if (height < 8 || width < 8) yann@1: return -ERRDISPLAYTOOSMALL; yann@1: if (initial_height != 0) yann@1: height = initial_height; yann@1: else yann@1: if (height > 4) yann@1: height -= 4; yann@1: else yann@1: height = 0; yann@1: if (initial_width != 0) yann@1: width = initial_width; yann@1: else yann@1: if (width > 5) yann@1: width -= 5; yann@1: else yann@1: width = 0; yann@1: yann@1: /* center dialog box on screen */ yann@1: x = (COLS - width) / 2; yann@1: y = (LINES - height) / 2; yann@1: yann@1: draw_shadow(stdscr, y, x, height, width); yann@1: yann@1: dialog = newwin(height, width, y, x); yann@1: keypad(dialog, TRUE); yann@1: yann@1: /* Create window for box region, used for scrolling text */ yann@1: boxh = height - 4; yann@1: boxw = width - 2; yann@1: box = subwin(dialog, boxh, boxw, y + 1, x + 1); yann@1: wattrset(box, dlg.dialog.atr); yann@1: wbkgdset(box, dlg.dialog.atr & A_COLOR); yann@1: yann@1: keypad(box, TRUE); yann@1: yann@1: /* register the new window, along with its borders */ yann@1: draw_box(dialog, 0, 0, height, width, yann@1: dlg.dialog.atr, dlg.border.atr); yann@1: yann@1: wattrset(dialog, dlg.border.atr); yann@1: mvwaddch(dialog, height - 3, 0, ACS_LTEE); yann@1: for (i = 0; i < width - 2; i++) yann@1: waddch(dialog, ACS_HLINE); yann@1: wattrset(dialog, dlg.dialog.atr); yann@1: wbkgdset(dialog, dlg.dialog.atr & A_COLOR); yann@1: waddch(dialog, ACS_RTEE); yann@1: yann@1: print_title(dialog, title, width); yann@1: yann@943: print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE); yann@1: wnoutrefresh(dialog); yann@1: getyx(dialog, cur_y, cur_x); /* Save cursor position */ yann@1: yann@1: /* Print first page of text */ yann@1: attr_clear(box, boxh, boxw, dlg.dialog.atr); yann@1: refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); yann@1: yann@1: while ((key != KEY_ESC) && (key != '\n')) { yann@1: key = wgetch(dialog); yann@1: switch (key) { yann@1: case 'E': /* Exit */ yann@1: case 'e': yann@1: case 'X': yann@1: case 'x': yann@1: delwin(box); yann@1: delwin(dialog); yann@1: return 0; yann@1: case 'g': /* First page */ yann@1: case KEY_HOME: yann@1: if (!begin_reached) { yann@1: begin_reached = 1; yann@1: page = buf; yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: } yann@1: break; yann@1: case 'G': /* Last page */ yann@1: case KEY_END: yann@1: yann@1: end_reached = 1; yann@1: /* point to last char in buf */ yann@1: page = buf + strlen(buf); yann@1: back_lines(boxh); yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: break; yann@1: case 'K': /* Previous line */ yann@1: case 'k': yann@1: case KEY_UP: yann@1: if (!begin_reached) { yann@1: back_lines(page_length + 1); yann@1: yann@1: /* We don't call print_page() here but use yann@1: * scrolling to ensure faster screen update. yann@1: * However, 'end_reached' and 'page_length' yann@1: * should still be updated, and 'page' should yann@1: * point to start of next page. This is done yann@1: * by calling get_line() in the following yann@1: * 'for' loop. */ yann@1: scrollok(box, TRUE); yann@1: wscrl(box, -1); /* Scroll box region down one line */ yann@1: scrollok(box, FALSE); yann@1: page_length = 0; yann@1: passed_end = 0; yann@1: for (i = 0; i < boxh; i++) { yann@1: if (!i) { yann@1: /* print first line of page */ yann@1: print_line(box, 0, boxw); yann@1: wnoutrefresh(box); yann@1: } else yann@1: /* Called to update 'end_reached' and 'page' */ yann@1: get_line(); yann@1: if (!passed_end) yann@1: page_length++; yann@1: if (end_reached && !passed_end) yann@1: passed_end = 1; yann@1: } yann@1: yann@1: print_position(dialog); yann@1: wmove(dialog, cur_y, cur_x); /* Restore cursor position */ yann@1: wrefresh(dialog); yann@1: } yann@1: break; yann@1: case 'B': /* Previous page */ yann@1: case 'b': yann@1: case KEY_PPAGE: yann@1: if (begin_reached) yann@1: break; yann@1: back_lines(page_length + boxh); yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: break; yann@1: case 'J': /* Next line */ yann@1: case 'j': yann@1: case KEY_DOWN: yann@1: if (!end_reached) { yann@1: begin_reached = 0; yann@1: scrollok(box, TRUE); yann@1: scroll(box); /* Scroll box region up one line */ yann@1: scrollok(box, FALSE); yann@1: print_line(box, boxh - 1, boxw); yann@1: wnoutrefresh(box); yann@1: print_position(dialog); yann@1: wmove(dialog, cur_y, cur_x); /* Restore cursor position */ yann@1: wrefresh(dialog); yann@1: } yann@1: break; yann@1: case KEY_NPAGE: /* Next page */ yann@1: case ' ': yann@1: if (end_reached) yann@1: break; yann@1: yann@1: begin_reached = 0; yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: break; yann@1: case '0': /* Beginning of line */ yann@1: case 'H': /* Scroll left */ yann@1: case 'h': yann@1: case KEY_LEFT: yann@1: if (hscroll <= 0) yann@1: break; yann@1: yann@1: if (key == '0') yann@1: hscroll = 0; yann@1: else yann@1: hscroll--; yann@1: /* Reprint current page to scroll horizontally */ yann@1: back_lines(page_length); yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: break; yann@1: case 'L': /* Scroll right */ yann@1: case 'l': yann@1: case KEY_RIGHT: yann@1: if (hscroll >= MAX_LEN) yann@1: break; yann@1: hscroll++; yann@1: /* Reprint current page to scroll horizontally */ yann@1: back_lines(page_length); yann@1: refresh_text_box(dialog, box, boxh, boxw, yann@1: cur_y, cur_x); yann@1: break; yann@1: case KEY_ESC: yann@1: key = on_key_esc(dialog); yann@1: break; yann@1: case KEY_RESIZE: yann@1: back_lines(height); yann@1: delwin(box); yann@1: delwin(dialog); yann@1: on_key_resize(); yann@1: goto do_resize; yann@1: } yann@1: } yann@1: delwin(box); yann@1: delwin(dialog); yann@1: return key; /* ESC pressed */ yann@1: } yann@1: yann@1: /* yann@1: * Go back 'n' lines in text. Called by dialog_textbox(). yann@1: * 'page' will be updated to point to the desired line in 'buf'. yann@1: */ yann@1: static void back_lines(int n) yann@1: { yann@1: int i; yann@1: yann@1: begin_reached = 0; yann@1: /* Go back 'n' lines */ yann@1: for (i = 0; i < n; i++) { yann@1: if (*page == '\0') { yann@1: if (end_reached) { yann@1: end_reached = 0; yann@1: continue; yann@1: } yann@1: } yann@1: if (page == buf) { yann@1: begin_reached = 1; yann@1: return; yann@1: } yann@1: page--; yann@1: do { yann@1: if (page == buf) { yann@1: begin_reached = 1; yann@1: return; yann@1: } yann@1: page--; yann@1: } while (*page != '\n'); yann@1: page++; yann@1: } yann@1: } yann@1: yann@1: /* yann@1: * Print a new page of text. Called by dialog_textbox(). yann@1: */ yann@1: static void print_page(WINDOW * win, int height, int width) yann@1: { yann@1: int i, passed_end = 0; yann@1: yann@1: page_length = 0; yann@1: for (i = 0; i < height; i++) { yann@1: print_line(win, i, width); yann@1: if (!passed_end) yann@1: page_length++; yann@1: if (end_reached && !passed_end) yann@1: passed_end = 1; yann@1: } yann@1: wnoutrefresh(win); yann@1: } yann@1: yann@1: /* yann@1: * Print a new line of text. Called by dialog_textbox() and print_page(). yann@1: */ yann@1: static void print_line(WINDOW * win, int row, int width) yann@1: { yann@1: int y, x; yann@1: char *line; yann@1: yann@1: line = get_line(); yann@1: line += MIN(strlen(line), hscroll); /* Scroll horizontally */ yann@1: wmove(win, row, 0); /* move cursor to correct line */ yann@1: waddch(win, ' '); yann@1: waddnstr(win, line, MIN(strlen(line), width - 2)); yann@1: yann@1: getyx(win, y, x); yann@1: /* Clear 'residue' of previous line */ yann@1: #if OLD_NCURSES yann@1: { yann@1: int i; yann@1: for (i = 0; i < width - x; i++) yann@1: waddch(win, ' '); yann@1: } yann@1: #else yann@1: wclrtoeol(win); yann@1: #endif yann@1: } yann@1: yann@1: /* yann@1: * Return current line of text. Called by dialog_textbox() and print_line(). yann@1: * 'page' should point to start of current line before calling, and will be yann@1: * updated to point to start of next line. yann@1: */ yann@1: static char *get_line(void) yann@1: { yann@1: int i = 0; yann@1: static char line[MAX_LEN + 1]; yann@1: yann@1: end_reached = 0; yann@1: while (*page != '\n') { yann@1: if (*page == '\0') { yann@1: if (!end_reached) { yann@1: end_reached = 1; yann@1: break; yann@1: } yann@1: } else if (i < MAX_LEN) yann@1: line[i++] = *(page++); yann@1: else { yann@1: /* Truncate lines longer than MAX_LEN characters */ yann@1: if (i == MAX_LEN) yann@1: line[i++] = '\0'; yann@1: page++; yann@1: } yann@1: } yann@1: if (i <= MAX_LEN) yann@1: line[i] = '\0'; yann@1: if (!end_reached) yann@1: page++; /* move pass '\n' */ yann@1: yann@1: return line; yann@1: } yann@1: yann@1: /* yann@1: * Print current position yann@1: */ yann@1: static void print_position(WINDOW * win) yann@1: { yann@1: int percent; yann@1: yann@1: wattrset(win, dlg.position_indicator.atr); yann@1: wbkgdset(win, dlg.position_indicator.atr & A_COLOR); yann@1: percent = (page - buf) * 100 / strlen(buf); yann@1: wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); yann@1: wprintw(win, "(%3d%%)", percent); yann@1: }