scripts/config: sync with linux upstream
[oweals/openwrt.git] / scripts / config / lxdialog / textbox.c
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 }