blob: bc4d4fb1dc75008ded5a4e067955d514a92d0759 [file] [log] [blame]
Patrick Georgi53ea1d42019-11-22 16:55:58 +01001// SPDX-License-Identifier: GPL-2.0+
Patrick Georgi0588d192009-08-12 15:00:51 +00002/*
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)
Patrick Georgi0588d192009-08-12 15:00:51 +00007 */
8
9#include "dialog.h"
10
Patrick Georgi0588d192009-08-12 15:00:51 +000011static int hscroll;
12static int begin_reached, end_reached, page_length;
Patrick Georgid5208402014-04-11 20:24:06 +020013static char *buf;
14static char *page;
Patrick Georgi0588d192009-08-12 15:00:51 +000015
16/*
Patrick Georgi34b149b2023-11-20 18:09:23 +010017 * Go back 'n' lines in text. Called by dialog_textbox().
18 * 'page' will be updated to point to the desired line in 'buf'.
19 */
20static void back_lines(int n)
21{
22 int i;
23
24 begin_reached = 0;
25 /* Go back 'n' lines */
26 for (i = 0; i < n; i++) {
27 if (*page == '\0') {
28 if (end_reached) {
29 end_reached = 0;
30 continue;
31 }
32 }
33 if (page == buf) {
34 begin_reached = 1;
35 return;
36 }
37 page--;
38 do {
39 if (page == buf) {
40 begin_reached = 1;
41 return;
42 }
43 page--;
44 } while (*page != '\n');
45 page++;
46 }
47}
48
49/*
50 * Return current line of text. Called by dialog_textbox() and print_line().
51 * 'page' should point to start of current line before calling, and will be
52 * updated to point to start of next line.
53 */
54static char *get_line(void)
55{
56 int i = 0;
57 static char line[MAX_LEN + 1];
58
59 end_reached = 0;
60 while (*page != '\n') {
61 if (*page == '\0') {
62 end_reached = 1;
63 break;
64 } else if (i < MAX_LEN)
65 line[i++] = *(page++);
66 else {
67 /* Truncate lines longer than MAX_LEN characters */
68 if (i == MAX_LEN)
69 line[i++] = '\0';
70 page++;
71 }
72 }
73 if (i <= MAX_LEN)
74 line[i] = '\0';
75 if (!end_reached)
76 page++; /* move past '\n' */
77
78 return line;
79}
80
81/*
82 * Print a new line of text.
83 */
84static void print_line(WINDOW *win, int row, int width)
85{
86 char *line;
87
88 line = get_line();
89 line += MIN(strlen(line), hscroll); /* Scroll horizontally */
90 wmove(win, row, 0); /* move cursor to correct line */
91 waddch(win, ' ');
92 waddnstr(win, line, MIN(strlen(line), width - 2));
93
94 /* Clear 'residue' of previous line */
95 wclrtoeol(win);
96}
97
98/*
99 * Print a new page of text.
100 */
101static void print_page(WINDOW *win, int height, int width, update_text_fn
102 update_text, void *data)
103{
104 int i, passed_end = 0;
105
106 if (update_text) {
107 char *end;
108
109 for (i = 0; i < height; i++)
110 get_line();
111 end = page;
112 back_lines(height);
113 update_text(buf, page - buf, end - buf, data);
114 }
115
116 page_length = 0;
117 for (i = 0; i < height; i++) {
118 print_line(win, i, width);
119 if (!passed_end)
120 page_length++;
121 if (end_reached && !passed_end)
122 passed_end = 1;
123 }
124 wnoutrefresh(win);
125}
126
127/*
128 * Print current position
129 */
130static void print_position(WINDOW *win)
131{
132 int percent;
133
134 wattrset(win, dlg.position_indicator.atr);
135 wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
136 percent = (page - buf) * 100 / strlen(buf);
137 wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
138 wprintw(win, "(%3d%%)", percent);
139}
140
141/*
Patrick Georgi0588d192009-08-12 15:00:51 +0000142 * refresh window content
143 */
144static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
Patrick Georgid5208402014-04-11 20:24:06 +0200145 int cur_y, int cur_x, update_text_fn update_text,
146 void *data)
Patrick Georgi0588d192009-08-12 15:00:51 +0000147{
Patrick Georgid5208402014-04-11 20:24:06 +0200148 print_page(box, boxh, boxw, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000149 print_position(dialog);
150 wmove(dialog, cur_y, cur_x); /* Restore cursor position */
151 wrefresh(dialog);
152}
153
Patrick Georgi0588d192009-08-12 15:00:51 +0000154/*
155 * Display text from a file in a dialog box.
Patrick Georgid5208402014-04-11 20:24:06 +0200156 *
157 * keys is a null-terminated array
158 * update_text() may not add or remove any '\n' or '\0' in tbuf
Patrick Georgi0588d192009-08-12 15:00:51 +0000159 */
Patrick Georgid5208402014-04-11 20:24:06 +0200160int dialog_textbox(const char *title, char *tbuf, int initial_height,
161 int initial_width, int *keys, int *_vscroll, int *_hscroll,
162 update_text_fn update_text, void *data)
Patrick Georgi0588d192009-08-12 15:00:51 +0000163{
164 int i, x, y, cur_x, cur_y, key = 0;
165 int height, width, boxh, boxw;
Patrick Georgi0588d192009-08-12 15:00:51 +0000166 WINDOW *dialog, *box;
Patrick Georgid5208402014-04-11 20:24:06 +0200167 bool done = false;
Patrick Georgi0588d192009-08-12 15:00:51 +0000168
169 begin_reached = 1;
170 end_reached = 0;
171 page_length = 0;
172 hscroll = 0;
173 buf = tbuf;
174 page = buf; /* page is pointer to start of page to be displayed */
175
Patrick Georgid5208402014-04-11 20:24:06 +0200176 if (_vscroll && *_vscroll) {
177 begin_reached = 0;
178
179 for (i = 0; i < *_vscroll; i++)
180 get_line();
181 }
182 if (_hscroll)
183 hscroll = *_hscroll;
184
Patrick Georgi0588d192009-08-12 15:00:51 +0000185do_resize:
186 getmaxyx(stdscr, height, width);
Patrick Georgid5208402014-04-11 20:24:06 +0200187 if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN)
Patrick Georgi0588d192009-08-12 15:00:51 +0000188 return -ERRDISPLAYTOOSMALL;
189 if (initial_height != 0)
190 height = initial_height;
191 else
192 if (height > 4)
193 height -= 4;
194 else
195 height = 0;
196 if (initial_width != 0)
197 width = initial_width;
198 else
199 if (width > 5)
200 width -= 5;
201 else
202 width = 0;
203
204 /* center dialog box on screen */
Patrick Georgid5208402014-04-11 20:24:06 +0200205 x = (getmaxx(stdscr) - width) / 2;
206 y = (getmaxy(stdscr) - height) / 2;
Patrick Georgi0588d192009-08-12 15:00:51 +0000207
208 draw_shadow(stdscr, y, x, height, width);
209
210 dialog = newwin(height, width, y, x);
211 keypad(dialog, TRUE);
212
213 /* Create window for box region, used for scrolling text */
214 boxh = height - 4;
215 boxw = width - 2;
216 box = subwin(dialog, boxh, boxw, y + 1, x + 1);
217 wattrset(box, dlg.dialog.atr);
218 wbkgdset(box, dlg.dialog.atr & A_COLOR);
219
220 keypad(box, TRUE);
221
222 /* register the new window, along with its borders */
223 draw_box(dialog, 0, 0, height, width,
224 dlg.dialog.atr, dlg.border.atr);
225
226 wattrset(dialog, dlg.border.atr);
227 mvwaddch(dialog, height - 3, 0, ACS_LTEE);
228 for (i = 0; i < width - 2; i++)
229 waddch(dialog, ACS_HLINE);
230 wattrset(dialog, dlg.dialog.atr);
231 wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
232 waddch(dialog, ACS_RTEE);
233
234 print_title(dialog, title, width);
235
Patrick Georgi53ea1d42019-11-22 16:55:58 +0100236 print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
Patrick Georgi0588d192009-08-12 15:00:51 +0000237 wnoutrefresh(dialog);
238 getyx(dialog, cur_y, cur_x); /* Save cursor position */
239
240 /* Print first page of text */
241 attr_clear(box, boxh, boxw, dlg.dialog.atr);
Patrick Georgid5208402014-04-11 20:24:06 +0200242 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
243 data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000244
Patrick Georgid5208402014-04-11 20:24:06 +0200245 while (!done) {
Patrick Georgi0588d192009-08-12 15:00:51 +0000246 key = wgetch(dialog);
247 switch (key) {
248 case 'E': /* Exit */
249 case 'e':
250 case 'X':
251 case 'x':
Patrick Georgid5208402014-04-11 20:24:06 +0200252 case 'q':
253 case '\n':
254 done = true;
255 break;
Patrick Georgi0588d192009-08-12 15:00:51 +0000256 case 'g': /* First page */
257 case KEY_HOME:
258 if (!begin_reached) {
259 begin_reached = 1;
260 page = buf;
261 refresh_text_box(dialog, box, boxh, boxw,
Patrick Georgid5208402014-04-11 20:24:06 +0200262 cur_y, cur_x, update_text,
263 data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000264 }
265 break;
266 case 'G': /* Last page */
267 case KEY_END:
268
269 end_reached = 1;
270 /* point to last char in buf */
271 page = buf + strlen(buf);
272 back_lines(boxh);
Patrick Georgid5208402014-04-11 20:24:06 +0200273 refresh_text_box(dialog, box, boxh, boxw, cur_y,
274 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000275 break;
276 case 'K': /* Previous line */
277 case 'k':
278 case KEY_UP:
Patrick Georgid5208402014-04-11 20:24:06 +0200279 if (begin_reached)
280 break;
Patrick Georgi0588d192009-08-12 15:00:51 +0000281
Patrick Georgid5208402014-04-11 20:24:06 +0200282 back_lines(page_length + 1);
283 refresh_text_box(dialog, box, boxh, boxw, cur_y,
284 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000285 break;
286 case 'B': /* Previous page */
287 case 'b':
Patrick Georgid5208402014-04-11 20:24:06 +0200288 case 'u':
Patrick Georgi0588d192009-08-12 15:00:51 +0000289 case KEY_PPAGE:
290 if (begin_reached)
291 break;
292 back_lines(page_length + boxh);
Patrick Georgid5208402014-04-11 20:24:06 +0200293 refresh_text_box(dialog, box, boxh, boxw, cur_y,
294 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000295 break;
296 case 'J': /* Next line */
297 case 'j':
298 case KEY_DOWN:
Patrick Georgid5208402014-04-11 20:24:06 +0200299 if (end_reached)
300 break;
301
302 back_lines(page_length - 1);
303 refresh_text_box(dialog, box, boxh, boxw, cur_y,
304 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000305 break;
306 case KEY_NPAGE: /* Next page */
307 case ' ':
Patrick Georgid5208402014-04-11 20:24:06 +0200308 case 'd':
Patrick Georgi0588d192009-08-12 15:00:51 +0000309 if (end_reached)
310 break;
311
312 begin_reached = 0;
Patrick Georgid5208402014-04-11 20:24:06 +0200313 refresh_text_box(dialog, box, boxh, boxw, cur_y,
314 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000315 break;
316 case '0': /* Beginning of line */
317 case 'H': /* Scroll left */
318 case 'h':
319 case KEY_LEFT:
320 if (hscroll <= 0)
321 break;
322
323 if (key == '0')
324 hscroll = 0;
325 else
326 hscroll--;
327 /* Reprint current page to scroll horizontally */
328 back_lines(page_length);
Patrick Georgid5208402014-04-11 20:24:06 +0200329 refresh_text_box(dialog, box, boxh, boxw, cur_y,
330 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000331 break;
332 case 'L': /* Scroll right */
333 case 'l':
334 case KEY_RIGHT:
335 if (hscroll >= MAX_LEN)
336 break;
337 hscroll++;
338 /* Reprint current page to scroll horizontally */
339 back_lines(page_length);
Patrick Georgid5208402014-04-11 20:24:06 +0200340 refresh_text_box(dialog, box, boxh, boxw, cur_y,
341 cur_x, update_text, data);
Patrick Georgi0588d192009-08-12 15:00:51 +0000342 break;
343 case KEY_ESC:
Patrick Georgid5208402014-04-11 20:24:06 +0200344 if (on_key_esc(dialog) == KEY_ESC)
345 done = true;
Patrick Georgi0588d192009-08-12 15:00:51 +0000346 break;
Patrick Georgi0588d192009-08-12 15:00:51 +0000347 case KEY_RESIZE:
348 back_lines(height);
349 delwin(box);
350 delwin(dialog);
351 on_key_resize();
352 goto do_resize;
Patrick Georgid5208402014-04-11 20:24:06 +0200353 default:
354 for (i = 0; keys[i]; i++) {
355 if (key == keys[i]) {
356 done = true;
357 break;
358 }
359 }
Patrick Georgi0588d192009-08-12 15:00:51 +0000360 }
361 }
362 delwin(box);
363 delwin(dialog);
Patrick Georgid5208402014-04-11 20:24:06 +0200364 if (_vscroll) {
365 const char *s;
366
367 s = buf;
368 *_vscroll = 0;
369 back_lines(page_length);
370 while (s < page && (s = strchr(s, '\n'))) {
371 (*_vscroll)++;
372 s++;
373 }
374 }
375 if (_hscroll)
376 *_hscroll = hscroll;
377 return key;
Patrick Georgi0588d192009-08-12 15:00:51 +0000378}