1// SPDX-License-Identifier: GPL-2.0+
2/*
3 *  inputbox.c -- implements the input box
4 *
5 *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
6 *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
7 */
8
9#include "dialog.h"
10
11char dialog_input_result[MAX_LEN + 1];
12
13/*
14 *  Print the termination buttons
15 */
16static void print_buttons(WINDOW * dialog, int height, int width, int selected)
17{
18	int x = width / 2 - 11;
19	int y = height - 2;
20
21	print_button(dialog, "  Ok  ", y, x, selected == 0);
22	print_button(dialog, " Help ", y, x + 14, selected == 1);
23
24	wmove(dialog, y, x + 1 + 14 * selected);
25	wrefresh(dialog);
26}
27
28/*
29 * Display a dialog box for inputing a string
30 */
31int dialog_inputbox(const char *title, const char *prompt, int height, int width,
32		    const char *init)
33{
34	int i, x, y, box_y, box_x, box_width;
35	int input_x = 0, key = 0, button = -1;
36	int show_x, len, pos;
37	char *instr = dialog_input_result;
38	WINDOW *dialog;
39
40	if (!init)
41		instr[0] = '\0';
42	else
43		strcpy(instr, init);
44
45do_resize:
46	if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGTH_MIN))
47		return -ERRDISPLAYTOOSMALL;
48	if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN))
49		return -ERRDISPLAYTOOSMALL;
50
51	/* center dialog box on screen */
52	x = (getmaxx(stdscr) - width) / 2;
53	y = (getmaxy(stdscr) - height) / 2;
54
55	draw_shadow(stdscr, y, x, height, width);
56
57	dialog = newwin(height, width, y, x);
58	keypad(dialog, TRUE);
59
60	draw_box(dialog, 0, 0, height, width,
61		 dlg.dialog.atr, dlg.border.atr);
62	wattrset(dialog, dlg.border.atr);
63	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
64	for (i = 0; i < width - 2; i++)
65		waddch(dialog, ACS_HLINE);
66	wattrset(dialog, dlg.dialog.atr);
67	waddch(dialog, ACS_RTEE);
68
69	print_title(dialog, title, width);
70
71	wattrset(dialog, dlg.dialog.atr);
72	print_autowrap(dialog, prompt, width - 2, 1, 3);
73
74	/* Draw the input field box */
75	box_width = width - 6;
76	getyx(dialog, y, x);
77	box_y = y + 2;
78	box_x = (width - box_width) / 2;
79	draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2,
80		 dlg.dialog.atr, dlg.border.atr);
81
82	print_buttons(dialog, height, width, 0);
83
84	/* Set up the initial value */
85	wmove(dialog, box_y, box_x);
86	wattrset(dialog, dlg.inputbox.atr);
87
88	len = strlen(instr);
89	pos = len;
90
91	if (len >= box_width) {
92		show_x = len - box_width + 1;
93		input_x = box_width - 1;
94		for (i = 0; i < box_width - 1; i++)
95			waddch(dialog, instr[show_x + i]);
96	} else {
97		show_x = 0;
98		input_x = len;
99		waddstr(dialog, instr);
100	}
101
102	wmove(dialog, box_y, box_x + input_x);
103
104	wrefresh(dialog);
105
106	while (key != KEY_ESC) {
107		key = wgetch(dialog);
108
109		if (button == -1) {	/* Input box selected */
110			switch (key) {
111			case TAB:
112			case KEY_UP:
113			case KEY_DOWN:
114				break;
115			case KEY_BACKSPACE:
116			case 127:
117				if (pos) {
118					wattrset(dialog, dlg.inputbox.atr);
119					if (input_x == 0) {
120						show_x--;
121					} else
122						input_x--;
123
124					if (pos < len) {
125						for (i = pos - 1; i < len; i++) {
126							instr[i] = instr[i+1];
127						}
128					}
129
130					pos--;
131					len--;
132					instr[len] = '\0';
133					wmove(dialog, box_y, box_x);
134					for (i = 0; i < box_width; i++) {
135						if (!instr[show_x + i]) {
136							waddch(dialog, ' ');
137							break;
138						}
139						waddch(dialog, instr[show_x + i]);
140					}
141					wmove(dialog, box_y, input_x + box_x);
142					wrefresh(dialog);
143				}
144				continue;
145			case KEY_LEFT:
146				if (pos > 0) {
147					if (input_x > 0) {
148						wmove(dialog, box_y, --input_x + box_x);
149					} else if (input_x == 0) {
150						show_x--;
151						wmove(dialog, box_y, box_x);
152						for (i = 0; i < box_width; i++) {
153							if (!instr[show_x + i]) {
154								waddch(dialog, ' ');
155								break;
156							}
157							waddch(dialog, instr[show_x + i]);
158						}
159						wmove(dialog, box_y, box_x);
160					}
161					pos--;
162				}
163				continue;
164			case KEY_RIGHT:
165				if (pos < len) {
166					if (input_x < box_width - 1) {
167						wmove(dialog, box_y, ++input_x + box_x);
168					} else if (input_x == box_width - 1) {
169						show_x++;
170						wmove(dialog, box_y, box_x);
171						for (i = 0; i < box_width; i++) {
172							if (!instr[show_x + i]) {
173								waddch(dialog, ' ');
174								break;
175							}
176							waddch(dialog, instr[show_x + i]);
177						}
178						wmove(dialog, box_y, input_x + box_x);
179					}
180					pos++;
181				}
182				continue;
183			default:
184				if (key < 0x100 && isprint(key)) {
185					if (len < MAX_LEN) {
186						wattrset(dialog, dlg.inputbox.atr);
187						if (pos < len) {
188							for (i = len; i > pos; i--)
189								instr[i] = instr[i-1];
190							instr[pos] = key;
191						} else {
192							instr[len] = key;
193						}
194						pos++;
195						len++;
196						instr[len] = '\0';
197
198						if (input_x == box_width - 1) {
199							show_x++;
200						} else {
201							input_x++;
202						}
203
204						wmove(dialog, box_y, box_x);
205						for (i = 0; i < box_width; i++) {
206							if (!instr[show_x + i]) {
207								waddch(dialog, ' ');
208								break;
209							}
210							waddch(dialog, instr[show_x + i]);
211						}
212						wmove(dialog, box_y, input_x + box_x);
213						wrefresh(dialog);
214					} else
215						flash();	/* Alarm user about overflow */
216					continue;
217				}
218			}
219		}
220		switch (key) {
221		case 'O':
222		case 'o':
223			delwin(dialog);
224			return 0;
225		case 'H':
226		case 'h':
227			delwin(dialog);
228			return 1;
229		case KEY_UP:
230		case KEY_LEFT:
231			switch (button) {
232			case -1:
233				button = 1;	/* Indicates "Help" button is selected */
234				print_buttons(dialog, height, width, 1);
235				break;
236			case 0:
237				button = -1;	/* Indicates input box is selected */
238				print_buttons(dialog, height, width, 0);
239				wmove(dialog, box_y, box_x + input_x);
240				wrefresh(dialog);
241				break;
242			case 1:
243				button = 0;	/* Indicates "OK" button is selected */
244				print_buttons(dialog, height, width, 0);
245				break;
246			}
247			break;
248		case TAB:
249		case KEY_DOWN:
250		case KEY_RIGHT:
251			switch (button) {
252			case -1:
253				button = 0;	/* Indicates "OK" button is selected */
254				print_buttons(dialog, height, width, 0);
255				break;
256			case 0:
257				button = 1;	/* Indicates "Help" button is selected */
258				print_buttons(dialog, height, width, 1);
259				break;
260			case 1:
261				button = -1;	/* Indicates input box is selected */
262				print_buttons(dialog, height, width, 0);
263				wmove(dialog, box_y, box_x + input_x);
264				wrefresh(dialog);
265				break;
266			}
267			break;
268		case ' ':
269		case '\n':
270			delwin(dialog);
271			return (button == -1 ? 0 : button);
272		case 'X':
273		case 'x':
274			key = KEY_ESC;
275			break;
276		case KEY_ESC:
277			key = on_key_esc(dialog);
278			break;
279		case KEY_RESIZE:
280			delwin(dialog);
281			on_key_resize();
282			goto do_resize;
283		}
284	}
285
286	delwin(dialog);
287	return KEY_ESC;		/* ESC pressed */
288}
289