]>
Commit | Line | Data |
---|---|---|
0a9064fb MY |
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 | ||
24 | static void back_lines(int n); | |
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); | |
28 | static char *get_line(void); | |
29 | static void print_position(WINDOW * win); | |
30 | ||
31 | static int hscroll; | |
32 | static int begin_reached, end_reached, page_length; | |
33 | static char *buf; | |
34 | static char *page; | |
35 | ||
36 | /* | |
37 | * refresh window content | |
38 | */ | |
39 | static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, | |
40 | int cur_y, int cur_x, update_text_fn update_text, | |
41 | void *data) | |
42 | { | |
43 | print_page(box, boxh, boxw, update_text, data); | |
44 | print_position(dialog); | |
45 | wmove(dialog, cur_y, cur_x); /* Restore cursor position */ | |
46 | wrefresh(dialog); | |
47 | } | |
48 | ||
49 | ||
50 | /* | |
51 | * Display text from a file in a dialog box. | |
52 | * | |
53 | * keys is a null-terminated array | |
54 | * update_text() may not add or remove any '\n' or '\0' in tbuf | |
55 | */ | |
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) | |
59 | { | |
60 | int i, x, y, cur_x, cur_y, key = 0; | |
61 | int height, width, boxh, boxw; | |
62 | WINDOW *dialog, *box; | |
63 | bool done = false; | |
64 | ||
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 */ | |
71 | ||
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 | ||
81 | do_resize: | |
82 | getmaxyx(stdscr, height, width); | |
83 | if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN) | |
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 | ||
100 | /* center dialog box on screen */ | |
101 | x = (getmaxx(stdscr) - width) / 2; | |
102 | y = (getmaxy(stdscr) - height) / 2; | |
103 | ||
104 | draw_shadow(stdscr, y, x, height, width); | |
105 | ||
106 | dialog = newwin(height, width, y, x); | |
107 | keypad(dialog, TRUE); | |
108 | ||
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); | |
115 | ||
116 | keypad(box, TRUE); | |
117 | ||
118 | /* register the new window, along with its borders */ | |
119 | draw_box(dialog, 0, 0, height, width, | |
120 | dlg.dialog.atr, dlg.border.atr); | |
121 | ||
122 | wattrset(dialog, dlg.border.atr); | |
123 | mvwaddch(dialog, height - 3, 0, ACS_LTEE); | |
124 | for (i = 0; i < width - 2; i++) | |
125 | waddch(dialog, ACS_HLINE); | |
126 | wattrset(dialog, dlg.dialog.atr); | |
127 | wbkgdset(dialog, dlg.dialog.atr & A_COLOR); | |
128 | waddch(dialog, ACS_RTEE); | |
129 | ||
130 | print_title(dialog, title, width); | |
131 | ||
132 | print_button(dialog, gettext(" Exit "), height - 2, width / 2 - 4, TRUE); | |
133 | wnoutrefresh(dialog); | |
134 | getyx(dialog, cur_y, cur_x); /* Save cursor position */ | |
135 | ||
136 | /* Print first page of text */ | |
137 | attr_clear(box, boxh, boxw, dlg.dialog.atr); | |
138 | refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text, | |
139 | data); | |
140 | ||
141 | while (!done) { | |
142 | key = wgetch(dialog); | |
143 | switch (key) { | |
144 | case 'E': /* Exit */ | |
145 | case 'e': | |
146 | case 'X': | |
147 | case 'x': | |
148 | case 'q': | |
149 | case '\n': | |
150 | done = true; | |
151 | break; | |
152 | case 'g': /* First page */ | |
153 | case KEY_HOME: | |
154 | if (!begin_reached) { | |
155 | begin_reached = 1; | |
156 | page = buf; | |
157 | refresh_text_box(dialog, box, boxh, boxw, | |
158 | cur_y, cur_x, update_text, | |
159 | data); | |
160 | } | |
161 | break; | |
162 | case 'G': /* Last page */ | |
163 | case KEY_END: | |
164 | ||
165 | end_reached = 1; | |
166 | /* point to last char in buf */ | |
167 | page = buf + strlen(buf); | |
168 | back_lines(boxh); | |
169 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
170 | cur_x, update_text, data); | |
171 | break; | |
172 | case 'K': /* Previous line */ | |
173 | case 'k': | |
174 | case KEY_UP: | |
175 | if (begin_reached) | |
176 | break; | |
177 | ||
178 | back_lines(page_length + 1); | |
179 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
180 | cur_x, update_text, data); | |
181 | break; | |
182 | case 'B': /* Previous page */ | |
183 | case 'b': | |
184 | case 'u': | |
185 | case KEY_PPAGE: | |
186 | if (begin_reached) | |
187 | break; | |
188 | back_lines(page_length + boxh); | |
189 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
190 | cur_x, update_text, data); | |
191 | break; | |
192 | case 'J': /* Next line */ | |
193 | case 'j': | |
194 | case KEY_DOWN: | |
195 | if (end_reached) | |
196 | break; | |
197 | ||
198 | back_lines(page_length - 1); | |
199 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
200 | cur_x, update_text, data); | |
201 | break; | |
202 | case KEY_NPAGE: /* Next page */ | |
203 | case ' ': | |
204 | case 'd': | |
205 | if (end_reached) | |
206 | break; | |
207 | ||
208 | begin_reached = 0; | |
209 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
210 | cur_x, update_text, data); | |
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); | |
225 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
226 | cur_x, update_text, data); | |
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); | |
236 | refresh_text_box(dialog, box, boxh, boxw, cur_y, | |
237 | cur_x, update_text, data); | |
238 | break; | |
239 | case KEY_ESC: | |
240 | if (on_key_esc(dialog) == KEY_ESC) | |
241 | done = true; | |
242 | break; | |
243 | case KEY_RESIZE: | |
244 | back_lines(height); | |
245 | delwin(box); | |
246 | delwin(dialog); | |
247 | on_key_resize(); | |
248 | goto do_resize; | |
249 | default: | |
250 | for (i = 0; keys[i]; i++) { | |
251 | if (key == keys[i]) { | |
252 | done = true; | |
253 | break; | |
254 | } | |
255 | } | |
256 | } | |
257 | } | |
258 | delwin(box); | |
259 | delwin(dialog); | |
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; | |
273 | return key; | |
274 | } | |
275 | ||
276 | /* | |
277 | * Go back 'n' lines in text. Called by dialog_textbox(). | |
278 | * 'page' will be updated to point to the desired line in 'buf'. | |
279 | */ | |
280 | static void back_lines(int n) | |
281 | { | |
282 | int i; | |
283 | ||
284 | begin_reached = 0; | |
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; | |
291 | } | |
292 | } | |
293 | if (page == buf) { | |
294 | begin_reached = 1; | |
295 | return; | |
296 | } | |
297 | page--; | |
298 | do { | |
299 | if (page == buf) { | |
300 | begin_reached = 1; | |
301 | return; | |
302 | } | |
303 | page--; | |
304 | } while (*page != '\n'); | |
305 | page++; | |
306 | } | |
307 | } | |
308 | ||
309 | /* | |
310 | * Print a new page of text. | |
311 | */ | |
312 | static void print_page(WINDOW *win, int height, int width, update_text_fn | |
313 | update_text, void *data) | |
314 | { | |
315 | int i, passed_end = 0; | |
316 | ||
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 | ||
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); | |
336 | } | |
337 | ||
338 | /* | |
339 | * Print a new line of text. | |
340 | */ | |
341 | static void print_line(WINDOW * win, int row, int width) | |
342 | { | |
343 | char *line; | |
344 | ||
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)); | |
350 | ||
351 | /* Clear 'residue' of previous line */ | |
352 | #if OLD_NCURSES | |
353 | { | |
354 | int x = getcurx(win); | |
355 | int i; | |
356 | for (i = 0; i < width - x; i++) | |
357 | waddch(win, ' '); | |
358 | } | |
359 | #else | |
360 | wclrtoeol(win); | |
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 | */ | |
369 | static char *get_line(void) | |
370 | { | |
371 | int i = 0; | |
372 | static char line[MAX_LEN + 1]; | |
373 | ||
374 | end_reached = 0; | |
375 | while (*page != '\n') { | |
376 | if (*page == '\0') { | |
377 | end_reached = 1; | |
378 | break; | |
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++; | |
386 | } | |
387 | } | |
388 | if (i <= MAX_LEN) | |
389 | line[i] = '\0'; | |
390 | if (!end_reached) | |
391 | page++; /* move past '\n' */ | |
392 | ||
393 | return line; | |
394 | } | |
395 | ||
396 | /* | |
397 | * Print current position | |
398 | */ | |
399 | static void print_position(WINDOW * win) | |
400 | { | |
401 | int percent; | |
402 | ||
403 | wattrset(win, dlg.position_indicator.atr); | |
404 | wbkgdset(win, dlg.position_indicator.atr & A_COLOR); | |
405 | percent = (page - buf) * 100 / strlen(buf); | |
406 | wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); | |
407 | wprintw(win, "(%3d%%)", percent); | |
408 | } |