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