Home | History | Annotate | Download | only in lxdialog
      1 /*
      2  *  textbox.c -- implements the text box
      3  *
      4  *  ORIGINAL AUTHOR: Savio Lam (lam836 (at) cs.cuhk.hk)
      5  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap (at) cfw.com)
      6  *
      7  *  This program is free software; you can redistribute it and/or
      8  *  modify it under the terms of the GNU General Public License
      9  *  as published by the Free Software Foundation; either version 2
     10  *  of the License, or (at your option) any later version.
     11  *
     12  *  This program is distributed in the hope that it will be useful,
     13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  *  GNU General Public License for more details.
     16  *
     17  *  You should have received a copy of the GNU General Public License
     18  *  along with this program; if not, write to the Free Software
     19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     20  */
     21 
     22 #include "dialog.h"
     23 
     24 static void back_lines(int n);
     25 static void print_page(WINDOW * win, int height, int width);
     26 static void print_line(WINDOW * win, int row, int width);
     27 static char *get_line(void);
     28 static void print_position(WINDOW * win);
     29 
     30 static int hscroll;
     31 static int begin_reached, end_reached, page_length;
     32 static const char *buf;
     33 static const char *page;
     34 
     35 /*
     36  * refresh window content
     37  */
     38 static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
     39 							  int cur_y, int cur_x)
     40 {
     41 	print_page(box, boxh, boxw);
     42 	print_position(dialog);
     43 	wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
     44 	wrefresh(dialog);
     45 }
     46 
     47 
     48 /*
     49  * Display text from a file in a dialog box.
     50  */
     51 int dialog_textbox(const char *title, const char *tbuf,
     52 		   int initial_height, int initial_width)
     53 {
     54 	int i, x, y, cur_x, cur_y, key = 0;
     55 	int height, width, boxh, boxw;
     56 	int passed_end;
     57 	WINDOW *dialog, *box;
     58 
     59 	begin_reached = 1;
     60 	end_reached = 0;
     61 	page_length = 0;
     62 	hscroll = 0;
     63 	buf = tbuf;
     64 	page = buf;	/* page is pointer to start of page to be displayed */
     65 
     66 do_resize:
     67 	getmaxyx(stdscr, height, width);
     68 	if (height < 8 || width < 8)
     69 		return -ERRDISPLAYTOOSMALL;
     70 	if (initial_height != 0)
     71 		height = initial_height;
     72 	else
     73 		if (height > 4)
     74 			height -= 4;
     75 		else
     76 			height = 0;
     77 	if (initial_width != 0)
     78 		width = initial_width;
     79 	else
     80 		if (width > 5)
     81 			width -= 5;
     82 		else
     83 			width = 0;
     84 
     85 	/* center dialog box on screen */
     86 	x = (COLS - width) / 2;
     87 	y = (LINES - height) / 2;
     88 
     89 	draw_shadow(stdscr, y, x, height, width);
     90 
     91 	dialog = newwin(height, width, y, x);
     92 	keypad(dialog, TRUE);
     93 
     94 	/* Create window for box region, used for scrolling text */
     95 	boxh = height - 4;
     96 	boxw = width - 2;
     97 	box = subwin(dialog, boxh, boxw, y + 1, x + 1);
     98 	wattrset(box, dlg.dialog.atr);
     99 	wbkgdset(box, dlg.dialog.atr & A_COLOR);
    100 
    101 	keypad(box, TRUE);
    102 
    103 	/* register the new window, along with its borders */
    104 	draw_box(dialog, 0, 0, height, width,
    105 		 dlg.dialog.atr, dlg.border.atr);
    106 
    107 	wattrset(dialog, dlg.border.atr);
    108 	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
    109 	for (i = 0; i < width - 2; i++)
    110 		waddch(dialog, ACS_HLINE);
    111 	wattrset(dialog, dlg.dialog.atr);
    112 	wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
    113 	waddch(dialog, ACS_RTEE);
    114 
    115 	print_title(dialog, title, width);
    116 
    117 	print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
    118 	wnoutrefresh(dialog);
    119 	getyx(dialog, cur_y, cur_x);	/* Save cursor position */
    120 
    121 	/* Print first page of text */
    122 	attr_clear(box, boxh, boxw, dlg.dialog.atr);
    123 	refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
    124 
    125 	while ((key != KEY_ESC) && (key != '\n')) {
    126 		key = wgetch(dialog);
    127 		switch (key) {
    128 		case 'E':	/* Exit */
    129 		case 'e':
    130 		case 'X':
    131 		case 'x':
    132 			delwin(box);
    133 			delwin(dialog);
    134 			return 0;
    135 		case 'g':	/* First page */
    136 		case KEY_HOME:
    137 			if (!begin_reached) {
    138 				begin_reached = 1;
    139 				page = buf;
    140 				refresh_text_box(dialog, box, boxh, boxw,
    141 						 cur_y, cur_x);
    142 			}
    143 			break;
    144 		case 'G':	/* Last page */
    145 		case KEY_END:
    146 
    147 			end_reached = 1;
    148 			/* point to last char in buf */
    149 			page = buf + strlen(buf);
    150 			back_lines(boxh);
    151 			refresh_text_box(dialog, box, boxh, boxw,
    152 					 cur_y, cur_x);
    153 			break;
    154 		case 'K':	/* Previous line */
    155 		case 'k':
    156 		case KEY_UP:
    157 			if (!begin_reached) {
    158 				back_lines(page_length + 1);
    159 
    160 				/* We don't call print_page() here but use
    161 				 * scrolling to ensure faster screen update.
    162 				 * However, 'end_reached' and 'page_length'
    163 				 * should still be updated, and 'page' should
    164 				 * point to start of next page. This is done
    165 				 * by calling get_line() in the following
    166 				 * 'for' loop. */
    167 				scrollok(box, TRUE);
    168 				wscrl(box, -1);	/* Scroll box region down one line */
    169 				scrollok(box, FALSE);
    170 				page_length = 0;
    171 				passed_end = 0;
    172 				for (i = 0; i < boxh; i++) {
    173 					if (!i) {
    174 						/* print first line of page */
    175 						print_line(box, 0, boxw);
    176 						wnoutrefresh(box);
    177 					} else
    178 						/* Called to update 'end_reached' and 'page' */
    179 						get_line();
    180 					if (!passed_end)
    181 						page_length++;
    182 					if (end_reached && !passed_end)
    183 						passed_end = 1;
    184 				}
    185 
    186 				print_position(dialog);
    187 				wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
    188 				wrefresh(dialog);
    189 			}
    190 			break;
    191 		case 'B':	/* Previous page */
    192 		case 'b':
    193 		case KEY_PPAGE:
    194 			if (begin_reached)
    195 				break;
    196 			back_lines(page_length + boxh);
    197 			refresh_text_box(dialog, box, boxh, boxw,
    198 					 cur_y, cur_x);
    199 			break;
    200 		case 'J':	/* Next line */
    201 		case 'j':
    202 		case KEY_DOWN:
    203 			if (!end_reached) {
    204 				begin_reached = 0;
    205 				scrollok(box, TRUE);
    206 				scroll(box);	/* Scroll box region up one line */
    207 				scrollok(box, FALSE);
    208 				print_line(box, boxh - 1, boxw);
    209 				wnoutrefresh(box);
    210 				print_position(dialog);
    211 				wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
    212 				wrefresh(dialog);
    213 			}
    214 			break;
    215 		case KEY_NPAGE:	/* Next page */
    216 		case ' ':
    217 			if (end_reached)
    218 				break;
    219 
    220 			begin_reached = 0;
    221 			refresh_text_box(dialog, box, boxh, boxw,
    222 					 cur_y, cur_x);
    223 			break;
    224 		case '0':	/* Beginning of line */
    225 		case 'H':	/* Scroll left */
    226 		case 'h':
    227 		case KEY_LEFT:
    228 			if (hscroll <= 0)
    229 				break;
    230 
    231 			if (key == '0')
    232 				hscroll = 0;
    233 			else
    234 				hscroll--;
    235 			/* Reprint current page to scroll horizontally */
    236 			back_lines(page_length);
    237 			refresh_text_box(dialog, box, boxh, boxw,
    238 					 cur_y, cur_x);
    239 			break;
    240 		case 'L':	/* Scroll right */
    241 		case 'l':
    242 		case KEY_RIGHT:
    243 			if (hscroll >= MAX_LEN)
    244 				break;
    245 			hscroll++;
    246 			/* Reprint current page to scroll horizontally */
    247 			back_lines(page_length);
    248 			refresh_text_box(dialog, box, boxh, boxw,
    249 					 cur_y, cur_x);
    250 			break;
    251 		case KEY_ESC:
    252 			key = on_key_esc(dialog);
    253 			break;
    254 		case KEY_RESIZE:
    255 			back_lines(height);
    256 			delwin(box);
    257 			delwin(dialog);
    258 			on_key_resize();
    259 			goto do_resize;
    260 		}
    261 	}
    262 	delwin(box);
    263 	delwin(dialog);
    264 	return key;		/* ESC pressed */
    265 }
    266 
    267 /*
    268  * Go back 'n' lines in text. Called by dialog_textbox().
    269  * 'page' will be updated to point to the desired line in 'buf'.
    270  */
    271 static void back_lines(int n)
    272 {
    273 	int i;
    274 
    275 	begin_reached = 0;
    276 	/* Go back 'n' lines */
    277 	for (i = 0; i < n; i++) {
    278 		if (*page == '\0') {
    279 			if (end_reached) {
    280 				end_reached = 0;
    281 				continue;
    282 			}
    283 		}
    284 		if (page == buf) {
    285 			begin_reached = 1;
    286 			return;
    287 		}
    288 		page--;
    289 		do {
    290 			if (page == buf) {
    291 				begin_reached = 1;
    292 				return;
    293 			}
    294 			page--;
    295 		} while (*page != '\n');
    296 		page++;
    297 	}
    298 }
    299 
    300 /*
    301  * Print a new page of text. Called by dialog_textbox().
    302  */
    303 static void print_page(WINDOW * win, int height, int width)
    304 {
    305 	int i, passed_end = 0;
    306 
    307 	page_length = 0;
    308 	for (i = 0; i < height; i++) {
    309 		print_line(win, i, width);
    310 		if (!passed_end)
    311 			page_length++;
    312 		if (end_reached && !passed_end)
    313 			passed_end = 1;
    314 	}
    315 	wnoutrefresh(win);
    316 }
    317 
    318 /*
    319  * Print a new line of text. Called by dialog_textbox() and print_page().
    320  */
    321 static void print_line(WINDOW * win, int row, int width)
    322 {
    323 	int y, x;
    324 	char *line;
    325 
    326 	line = get_line();
    327 	line += MIN(strlen(line), hscroll);	/* Scroll horizontally */
    328 	wmove(win, row, 0);	/* move cursor to correct line */
    329 	waddch(win, ' ');
    330 	waddnstr(win, line, MIN(strlen(line), width - 2));
    331 
    332 	getyx(win, y, x);
    333 	/* Clear 'residue' of previous line */
    334 #if OLD_NCURSES
    335 	{
    336 		int i;
    337 		for (i = 0; i < width - x; i++)
    338 			waddch(win, ' ');
    339 	}
    340 #else
    341 	wclrtoeol(win);
    342 #endif
    343 }
    344 
    345 /*
    346  * Return current line of text. Called by dialog_textbox() and print_line().
    347  * 'page' should point to start of current line before calling, and will be
    348  * updated to point to start of next line.
    349  */
    350 static char *get_line(void)
    351 {
    352 	int i = 0;
    353 	static char line[MAX_LEN + 1];
    354 
    355 	end_reached = 0;
    356 	while (*page != '\n') {
    357 		if (*page == '\0') {
    358 			if (!end_reached) {
    359 				end_reached = 1;
    360 				break;
    361 			}
    362 		} else if (i < MAX_LEN)
    363 			line[i++] = *(page++);
    364 		else {
    365 			/* Truncate lines longer than MAX_LEN characters */
    366 			if (i == MAX_LEN)
    367 				line[i++] = '\0';
    368 			page++;
    369 		}
    370 	}
    371 	if (i <= MAX_LEN)
    372 		line[i] = '\0';
    373 	if (!end_reached)
    374 		page++;		/* move pass '\n' */
    375 
    376 	return line;
    377 }
    378 
    379 /*
    380  * Print current position
    381  */
    382 static void print_position(WINDOW * win)
    383 {
    384 	int percent;
    385 
    386 	wattrset(win, dlg.position_indicator.atr);
    387 	wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
    388 	percent = (page - buf) * 100 / strlen(buf);
    389 	wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
    390 	wprintw(win, "(%3d%%)", percent);
    391 }
    392