10c874100SMasahiro Yamada // SPDX-License-Identifier: GPL-2.0+
26f6046cfSSam Ravnborg /*
36f6046cfSSam Ravnborg  *  textbox.c -- implements the text box
46f6046cfSSam Ravnborg  *
56f6046cfSSam Ravnborg  *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
66f6046cfSSam Ravnborg  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
76f6046cfSSam Ravnborg  */
86f6046cfSSam Ravnborg 
96f6046cfSSam Ravnborg #include "dialog.h"
106f6046cfSSam Ravnborg 
112982de69SSam Ravnborg static int hscroll;
122982de69SSam Ravnborg static int begin_reached, end_reached, page_length;
13*e14f1242SMasahiro Yamada static const char *buf, *page;
14*e14f1242SMasahiro Yamada static size_t start, end;
156f6046cfSSam Ravnborg 
166f6046cfSSam Ravnborg /*
17fb318e54SMasahiro Yamada  * Go back 'n' lines in text. Called by dialog_textbox().
18fb318e54SMasahiro Yamada  * 'page' will be updated to point to the desired line in 'buf'.
19fb318e54SMasahiro Yamada  */
back_lines(int n)20fb318e54SMasahiro Yamada static void back_lines(int n)
21fb318e54SMasahiro Yamada {
22fb318e54SMasahiro Yamada 	int i;
23fb318e54SMasahiro Yamada 
24fb318e54SMasahiro Yamada 	begin_reached = 0;
25fb318e54SMasahiro Yamada 	/* Go back 'n' lines */
26fb318e54SMasahiro Yamada 	for (i = 0; i < n; i++) {
27fb318e54SMasahiro Yamada 		if (*page == '\0') {
28fb318e54SMasahiro Yamada 			if (end_reached) {
29fb318e54SMasahiro Yamada 				end_reached = 0;
30fb318e54SMasahiro Yamada 				continue;
31fb318e54SMasahiro Yamada 			}
32fb318e54SMasahiro Yamada 		}
33fb318e54SMasahiro Yamada 		if (page == buf) {
34fb318e54SMasahiro Yamada 			begin_reached = 1;
35fb318e54SMasahiro Yamada 			return;
36fb318e54SMasahiro Yamada 		}
37fb318e54SMasahiro Yamada 		page--;
38fb318e54SMasahiro Yamada 		do {
39fb318e54SMasahiro Yamada 			if (page == buf) {
40fb318e54SMasahiro Yamada 				begin_reached = 1;
41fb318e54SMasahiro Yamada 				return;
42fb318e54SMasahiro Yamada 			}
43fb318e54SMasahiro Yamada 			page--;
44fb318e54SMasahiro Yamada 		} while (*page != '\n');
45fb318e54SMasahiro Yamada 		page++;
46fb318e54SMasahiro Yamada 	}
47fb318e54SMasahiro Yamada }
48fb318e54SMasahiro Yamada 
49fb318e54SMasahiro Yamada /*
50fb318e54SMasahiro Yamada  * Return current line of text. Called by dialog_textbox() and print_line().
51fb318e54SMasahiro Yamada  * 'page' should point to start of current line before calling, and will be
52fb318e54SMasahiro Yamada  * updated to point to start of next line.
53fb318e54SMasahiro Yamada  */
get_line(void)54fb318e54SMasahiro Yamada static char *get_line(void)
55fb318e54SMasahiro Yamada {
56fb318e54SMasahiro Yamada 	int i = 0;
57fb318e54SMasahiro Yamada 	static char line[MAX_LEN + 1];
58fb318e54SMasahiro Yamada 
59fb318e54SMasahiro Yamada 	end_reached = 0;
60fb318e54SMasahiro Yamada 	while (*page != '\n') {
61fb318e54SMasahiro Yamada 		if (*page == '\0') {
62fb318e54SMasahiro Yamada 			end_reached = 1;
63fb318e54SMasahiro Yamada 			break;
64fb318e54SMasahiro Yamada 		} else if (i < MAX_LEN)
65fb318e54SMasahiro Yamada 			line[i++] = *(page++);
66fb318e54SMasahiro Yamada 		else {
67fb318e54SMasahiro Yamada 			/* Truncate lines longer than MAX_LEN characters */
68fb318e54SMasahiro Yamada 			if (i == MAX_LEN)
69fb318e54SMasahiro Yamada 				line[i++] = '\0';
70fb318e54SMasahiro Yamada 			page++;
71fb318e54SMasahiro Yamada 		}
72fb318e54SMasahiro Yamada 	}
73fb318e54SMasahiro Yamada 	if (i <= MAX_LEN)
74fb318e54SMasahiro Yamada 		line[i] = '\0';
75fb318e54SMasahiro Yamada 	if (!end_reached)
76fb318e54SMasahiro Yamada 		page++;		/* move past '\n' */
77fb318e54SMasahiro Yamada 
78fb318e54SMasahiro Yamada 	return line;
79fb318e54SMasahiro Yamada }
80fb318e54SMasahiro Yamada 
81fb318e54SMasahiro Yamada /*
82fb318e54SMasahiro Yamada  * Print a new line of text.
83fb318e54SMasahiro Yamada  */
print_line(WINDOW * win,int row,int width)84fb318e54SMasahiro Yamada static void print_line(WINDOW *win, int row, int width)
85fb318e54SMasahiro Yamada {
86fb318e54SMasahiro Yamada 	char *line;
87fb318e54SMasahiro Yamada 
88fb318e54SMasahiro Yamada 	line = get_line();
89fb318e54SMasahiro Yamada 	line += MIN(strlen(line), hscroll);	/* Scroll horizontally */
90fb318e54SMasahiro Yamada 	wmove(win, row, 0);	/* move cursor to correct line */
91fb318e54SMasahiro Yamada 	waddch(win, ' ');
92fb318e54SMasahiro Yamada 	waddnstr(win, line, MIN(strlen(line), width - 2));
93fb318e54SMasahiro Yamada 
94fb318e54SMasahiro Yamada 	/* Clear 'residue' of previous line */
95fb318e54SMasahiro Yamada 	wclrtoeol(win);
96fb318e54SMasahiro Yamada }
97fb318e54SMasahiro Yamada 
98fb318e54SMasahiro Yamada /*
99fb318e54SMasahiro Yamada  * Print a new page of text.
100fb318e54SMasahiro Yamada  */
print_page(WINDOW * win,int height,int width)101*e14f1242SMasahiro Yamada static void print_page(WINDOW *win, int height, int width)
102fb318e54SMasahiro Yamada {
103fb318e54SMasahiro Yamada 	int i, passed_end = 0;
104fb318e54SMasahiro Yamada 
105fb318e54SMasahiro Yamada 	page_length = 0;
106fb318e54SMasahiro Yamada 	for (i = 0; i < height; i++) {
107fb318e54SMasahiro Yamada 		print_line(win, i, width);
108fb318e54SMasahiro Yamada 		if (!passed_end)
109fb318e54SMasahiro Yamada 			page_length++;
110fb318e54SMasahiro Yamada 		if (end_reached && !passed_end)
111fb318e54SMasahiro Yamada 			passed_end = 1;
112fb318e54SMasahiro Yamada 	}
113fb318e54SMasahiro Yamada 	wnoutrefresh(win);
114fb318e54SMasahiro Yamada }
115fb318e54SMasahiro Yamada 
116fb318e54SMasahiro Yamada /*
117fb318e54SMasahiro Yamada  * Print current position
118fb318e54SMasahiro Yamada  */
print_position(WINDOW * win)119fb318e54SMasahiro Yamada static void print_position(WINDOW *win)
120fb318e54SMasahiro Yamada {
121fb318e54SMasahiro Yamada 	int percent;
122fb318e54SMasahiro Yamada 
123fb318e54SMasahiro Yamada 	wattrset(win, dlg.position_indicator.atr);
124fb318e54SMasahiro Yamada 	wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
125fb318e54SMasahiro Yamada 	percent = (page - buf) * 100 / strlen(buf);
126fb318e54SMasahiro Yamada 	wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
127fb318e54SMasahiro Yamada 	wprintw(win, "(%3d%%)", percent);
128fb318e54SMasahiro Yamada }
129fb318e54SMasahiro Yamada 
130fb318e54SMasahiro Yamada /*
131c8dc68adSSam Ravnborg  * refresh window content
132c8dc68adSSam Ravnborg  */
refresh_text_box(WINDOW * dialog,WINDOW * box,int boxh,int boxw,int cur_y,int cur_x)133c8dc68adSSam Ravnborg static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
134*e14f1242SMasahiro Yamada 			     int cur_y, int cur_x)
135c8dc68adSSam Ravnborg {
136*e14f1242SMasahiro Yamada 	start = page - buf;
137*e14f1242SMasahiro Yamada 
138*e14f1242SMasahiro Yamada 	print_page(box, boxh, boxw);
139c8dc68adSSam Ravnborg 	print_position(dialog);
140c8dc68adSSam Ravnborg 	wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
141c8dc68adSSam Ravnborg 	wrefresh(dialog);
142*e14f1242SMasahiro Yamada 
143*e14f1242SMasahiro Yamada 	end = page - buf;
144c8dc68adSSam Ravnborg }
145c8dc68adSSam Ravnborg 
146c8dc68adSSam Ravnborg /*
1476f6046cfSSam Ravnborg  * Display text from a file in a dialog box.
148537ddae7SBenjamin Poirier  *
149537ddae7SBenjamin Poirier  * keys is a null-terminated array
1506f6046cfSSam Ravnborg  */
dialog_textbox(const char * title,const char * tbuf,int initial_height,int initial_width,int * _vscroll,int * _hscroll,int (* extra_key_cb)(int,size_t,size_t,void *),void * data)151*e14f1242SMasahiro Yamada int dialog_textbox(const char *title, const char *tbuf, int initial_height,
152*e14f1242SMasahiro Yamada 		   int initial_width, int *_vscroll, int *_hscroll,
153*e14f1242SMasahiro Yamada 		   int (*extra_key_cb)(int, size_t, size_t, void *), void *data)
1546f6046cfSSam Ravnborg {
1552982de69SSam Ravnborg 	int i, x, y, cur_x, cur_y, key = 0;
156c8dc68adSSam Ravnborg 	int height, width, boxh, boxw;
157c8dc68adSSam Ravnborg 	WINDOW *dialog, *box;
158537ddae7SBenjamin Poirier 	bool done = false;
1596f6046cfSSam Ravnborg 
1602982de69SSam Ravnborg 	begin_reached = 1;
1612982de69SSam Ravnborg 	end_reached = 0;
1622982de69SSam Ravnborg 	page_length = 0;
1632982de69SSam Ravnborg 	hscroll = 0;
1642982de69SSam Ravnborg 	buf = tbuf;
1656f6046cfSSam Ravnborg 	page = buf;	/* page is pointer to start of page to be displayed */
1666f6046cfSSam Ravnborg 
1671d1e2caeSBenjamin Poirier 	if (_vscroll && *_vscroll) {
1681d1e2caeSBenjamin Poirier 		begin_reached = 0;
1691d1e2caeSBenjamin Poirier 
1701d1e2caeSBenjamin Poirier 		for (i = 0; i < *_vscroll; i++)
1711d1e2caeSBenjamin Poirier 			get_line();
1721d1e2caeSBenjamin Poirier 	}
1731d1e2caeSBenjamin Poirier 	if (_hscroll)
1741d1e2caeSBenjamin Poirier 		hscroll = *_hscroll;
1751d1e2caeSBenjamin Poirier 
176c8dc68adSSam Ravnborg do_resize:
177c8dc68adSSam Ravnborg 	getmaxyx(stdscr, height, width);
178851f6657SSedat Dilek 	if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN)
179c8dc68adSSam Ravnborg 		return -ERRDISPLAYTOOSMALL;
180c8dc68adSSam Ravnborg 	if (initial_height != 0)
181c8dc68adSSam Ravnborg 		height = initial_height;
182c8dc68adSSam Ravnborg 	else
183c8dc68adSSam Ravnborg 		if (height > 4)
184c8dc68adSSam Ravnborg 			height -= 4;
185c8dc68adSSam Ravnborg 		else
186c8dc68adSSam Ravnborg 			height = 0;
187c8dc68adSSam Ravnborg 	if (initial_width != 0)
188c8dc68adSSam Ravnborg 		width = initial_width;
189c8dc68adSSam Ravnborg 	else
190c8dc68adSSam Ravnborg 		if (width > 5)
191c8dc68adSSam Ravnborg 			width -= 5;
192c8dc68adSSam Ravnborg 		else
193c8dc68adSSam Ravnborg 			width = 0;
194c8dc68adSSam Ravnborg 
1956f6046cfSSam Ravnborg 	/* center dialog box on screen */
1964f2de3e1SDirk Gouders 	x = (getmaxx(stdscr) - width) / 2;
1974f2de3e1SDirk Gouders 	y = (getmaxy(stdscr) - height) / 2;
1986f6046cfSSam Ravnborg 
1996f6046cfSSam Ravnborg 	draw_shadow(stdscr, y, x, height, width);
2006f6046cfSSam Ravnborg 
2016f6046cfSSam Ravnborg 	dialog = newwin(height, width, y, x);
2026f6046cfSSam Ravnborg 	keypad(dialog, TRUE);
2036f6046cfSSam Ravnborg 
204c8dc68adSSam Ravnborg 	/* Create window for box region, used for scrolling text */
205c8dc68adSSam Ravnborg 	boxh = height - 4;
206c8dc68adSSam Ravnborg 	boxw = width - 2;
207c8dc68adSSam Ravnborg 	box = subwin(dialog, boxh, boxw, y + 1, x + 1);
208c8dc68adSSam Ravnborg 	wattrset(box, dlg.dialog.atr);
209c8dc68adSSam Ravnborg 	wbkgdset(box, dlg.dialog.atr & A_COLOR);
2106f6046cfSSam Ravnborg 
211c8dc68adSSam Ravnborg 	keypad(box, TRUE);
2126f6046cfSSam Ravnborg 
2136f6046cfSSam Ravnborg 	/* register the new window, along with its borders */
21498e5a157SSam Ravnborg 	draw_box(dialog, 0, 0, height, width,
21598e5a157SSam Ravnborg 		 dlg.dialog.atr, dlg.border.atr);
2166f6046cfSSam Ravnborg 
21798e5a157SSam Ravnborg 	wattrset(dialog, dlg.border.atr);
2186f6046cfSSam Ravnborg 	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
2196f6046cfSSam Ravnborg 	for (i = 0; i < width - 2; i++)
2206f6046cfSSam Ravnborg 		waddch(dialog, ACS_HLINE);
22198e5a157SSam Ravnborg 	wattrset(dialog, dlg.dialog.atr);
22298e5a157SSam Ravnborg 	wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
2236f6046cfSSam Ravnborg 	waddch(dialog, ACS_RTEE);
2246f6046cfSSam Ravnborg 
2256f6046cfSSam Ravnborg 	print_title(dialog, title, width);
2266f6046cfSSam Ravnborg 
227694c49a7SSam Ravnborg 	print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
2286f6046cfSSam Ravnborg 	wnoutrefresh(dialog);
2296f6046cfSSam Ravnborg 	getyx(dialog, cur_y, cur_x);	/* Save cursor position */
2306f6046cfSSam Ravnborg 
2316f6046cfSSam Ravnborg 	/* Print first page of text */
232c8dc68adSSam Ravnborg 	attr_clear(box, boxh, boxw, dlg.dialog.atr);
233*e14f1242SMasahiro Yamada 	refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2346f6046cfSSam Ravnborg 
235537ddae7SBenjamin Poirier 	while (!done) {
2366f6046cfSSam Ravnborg 		key = wgetch(dialog);
2376f6046cfSSam Ravnborg 		switch (key) {
2386f6046cfSSam Ravnborg 		case 'E':	/* Exit */
2396f6046cfSSam Ravnborg 		case 'e':
2406f6046cfSSam Ravnborg 		case 'X':
2416f6046cfSSam Ravnborg 		case 'x':
2429d4792c9SBenjamin Poirier 		case 'q':
243537ddae7SBenjamin Poirier 		case '\n':
244537ddae7SBenjamin Poirier 			done = true;
245537ddae7SBenjamin Poirier 			break;
2466f6046cfSSam Ravnborg 		case 'g':	/* First page */
2476f6046cfSSam Ravnborg 		case KEY_HOME:
2486f6046cfSSam Ravnborg 			if (!begin_reached) {
2496f6046cfSSam Ravnborg 				begin_reached = 1;
2506f6046cfSSam Ravnborg 				page = buf;
251c8dc68adSSam Ravnborg 				refresh_text_box(dialog, box, boxh, boxw,
252*e14f1242SMasahiro Yamada 						 cur_y, cur_x);
2536f6046cfSSam Ravnborg 			}
2546f6046cfSSam Ravnborg 			break;
2556f6046cfSSam Ravnborg 		case 'G':	/* Last page */
2566f6046cfSSam Ravnborg 		case KEY_END:
2576f6046cfSSam Ravnborg 
2586f6046cfSSam Ravnborg 			end_reached = 1;
2592982de69SSam Ravnborg 			/* point to last char in buf */
2602982de69SSam Ravnborg 			page = buf + strlen(buf);
261c8dc68adSSam Ravnborg 			back_lines(boxh);
262*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2636f6046cfSSam Ravnborg 			break;
2646f6046cfSSam Ravnborg 		case 'K':	/* Previous line */
2656f6046cfSSam Ravnborg 		case 'k':
2666f6046cfSSam Ravnborg 		case KEY_UP:
2671a374ae6SBenjamin Poirier 			if (begin_reached)
2681a374ae6SBenjamin Poirier 				break;
269537ddae7SBenjamin Poirier 
2706f6046cfSSam Ravnborg 			back_lines(page_length + 1);
271*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2726f6046cfSSam Ravnborg 			break;
2736f6046cfSSam Ravnborg 		case 'B':	/* Previous page */
2746f6046cfSSam Ravnborg 		case 'b':
2759d4792c9SBenjamin Poirier 		case 'u':
2766f6046cfSSam Ravnborg 		case KEY_PPAGE:
2776f6046cfSSam Ravnborg 			if (begin_reached)
2786f6046cfSSam Ravnborg 				break;
279c8dc68adSSam Ravnborg 			back_lines(page_length + boxh);
280*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2816f6046cfSSam Ravnborg 			break;
2826f6046cfSSam Ravnborg 		case 'J':	/* Next line */
2836f6046cfSSam Ravnborg 		case 'j':
2846f6046cfSSam Ravnborg 		case KEY_DOWN:
2851a374ae6SBenjamin Poirier 			if (end_reached)
2861a374ae6SBenjamin Poirier 				break;
2871a374ae6SBenjamin Poirier 
2881a374ae6SBenjamin Poirier 			back_lines(page_length - 1);
289*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2906f6046cfSSam Ravnborg 			break;
2916f6046cfSSam Ravnborg 		case KEY_NPAGE:	/* Next page */
2926f6046cfSSam Ravnborg 		case ' ':
2939d4792c9SBenjamin Poirier 		case 'd':
2946f6046cfSSam Ravnborg 			if (end_reached)
2956f6046cfSSam Ravnborg 				break;
2966f6046cfSSam Ravnborg 
2976f6046cfSSam Ravnborg 			begin_reached = 0;
298*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
2996f6046cfSSam Ravnborg 			break;
3006f6046cfSSam Ravnborg 		case '0':	/* Beginning of line */
3016f6046cfSSam Ravnborg 		case 'H':	/* Scroll left */
3026f6046cfSSam Ravnborg 		case 'h':
3036f6046cfSSam Ravnborg 		case KEY_LEFT:
3046f6046cfSSam Ravnborg 			if (hscroll <= 0)
3056f6046cfSSam Ravnborg 				break;
3066f6046cfSSam Ravnborg 
3076f6046cfSSam Ravnborg 			if (key == '0')
3086f6046cfSSam Ravnborg 				hscroll = 0;
3096f6046cfSSam Ravnborg 			else
3106f6046cfSSam Ravnborg 				hscroll--;
3116f6046cfSSam Ravnborg 			/* Reprint current page to scroll horizontally */
3126f6046cfSSam Ravnborg 			back_lines(page_length);
313*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
3146f6046cfSSam Ravnborg 			break;
3156f6046cfSSam Ravnborg 		case 'L':	/* Scroll right */
3166f6046cfSSam Ravnborg 		case 'l':
3176f6046cfSSam Ravnborg 		case KEY_RIGHT:
3186f6046cfSSam Ravnborg 			if (hscroll >= MAX_LEN)
3196f6046cfSSam Ravnborg 				break;
3206f6046cfSSam Ravnborg 			hscroll++;
3216f6046cfSSam Ravnborg 			/* Reprint current page to scroll horizontally */
3226f6046cfSSam Ravnborg 			back_lines(page_length);
323*e14f1242SMasahiro Yamada 			refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
3246f6046cfSSam Ravnborg 			break;
325f3cbcdc9SSam Ravnborg 		case KEY_ESC:
326537ddae7SBenjamin Poirier 			if (on_key_esc(dialog) == KEY_ESC)
327537ddae7SBenjamin Poirier 				done = true;
3286f6046cfSSam Ravnborg 			break;
329c8dc68adSSam Ravnborg 		case KEY_RESIZE:
330c8dc68adSSam Ravnborg 			back_lines(height);
331c8dc68adSSam Ravnborg 			delwin(box);
332c8dc68adSSam Ravnborg 			delwin(dialog);
333c8dc68adSSam Ravnborg 			on_key_resize();
334c8dc68adSSam Ravnborg 			goto do_resize;
335537ddae7SBenjamin Poirier 		default:
336*e14f1242SMasahiro Yamada 			if (extra_key_cb && extra_key_cb(key, start, end, data)) {
337537ddae7SBenjamin Poirier 				done = true;
338537ddae7SBenjamin Poirier 				break;
339537ddae7SBenjamin Poirier 			}
340537ddae7SBenjamin Poirier 		}
3416f6046cfSSam Ravnborg 	}
342c8dc68adSSam Ravnborg 	delwin(box);
3436f6046cfSSam Ravnborg 	delwin(dialog);
3441d1e2caeSBenjamin Poirier 	if (_vscroll) {
3451d1e2caeSBenjamin Poirier 		const char *s;
3461d1e2caeSBenjamin Poirier 
3471d1e2caeSBenjamin Poirier 		s = buf;
3481d1e2caeSBenjamin Poirier 		*_vscroll = 0;
3491d1e2caeSBenjamin Poirier 		back_lines(page_length);
3501d1e2caeSBenjamin Poirier 		while (s < page && (s = strchr(s, '\n'))) {
3511d1e2caeSBenjamin Poirier 			(*_vscroll)++;
3521d1e2caeSBenjamin Poirier 			s++;
3531d1e2caeSBenjamin Poirier 		}
3541d1e2caeSBenjamin Poirier 	}
3551d1e2caeSBenjamin Poirier 	if (_hscroll)
3561d1e2caeSBenjamin Poirier 		*_hscroll = hscroll;
357537ddae7SBenjamin Poirier 	return key;
3586f6046cfSSam Ravnborg }
359