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