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