Removes trailing spaces. Removes tabs.
[vlc.git] / 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);
26 static void print_line (WINDOW * win, int row, int width);
27 static char *get_line (void);
28 static void print_position (WINDOW * win, int height, int width);
29
30 static int hscroll = 0, fd, file_size, bytes_read;
31 static int begin_reached = 1, end_reached = 0, page_length;
32 static char *buf, *page;
33
34 /*
35  * Display text from a file in a dialog box.
36  */
37 int
38 dialog_textbox (const char *title, const char *file, int height, int width)
39 {
40     int i, x, y, cur_x, cur_y, fpos, key = 0;
41     int passed_end;
42     char search_term[MAX_LEN + 1];
43     WINDOW *dialog, *text;
44
45     search_term[0] = '\0';    /* no search term entered yet */
46
47     /* Open input file for reading */
48     if ((fd = open (file, O_RDONLY)) == -1) {
49     endwin ();
50     fprintf (stderr,
51          "\nCan't open input file in dialog_textbox().\n");
52     exit (-1);
53     }
54     /* Get file size. Actually, 'file_size' is the real file size - 1,
55        since it's only the last byte offset from the beginning */
56     if ((file_size = lseek (fd, 0, SEEK_END)) == -1) {
57     endwin ();
58     fprintf (stderr, "\nError getting file size in dialog_textbox().\n");
59     exit (-1);
60     }
61     /* Restore file pointer to beginning of file after getting file size */
62     if (lseek (fd, 0, SEEK_SET) == -1) {
63     endwin ();
64     fprintf (stderr, "\nError moving file pointer in dialog_textbox().\n");
65     exit (-1);
66     }
67     /* Allocate space for read buffer */
68     if ((buf = malloc (BUF_SIZE + 1)) == NULL) {
69     endwin ();
70     fprintf (stderr, "\nCan't allocate memory in dialog_textbox().\n");
71     exit (-1);
72     }
73     if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
74     endwin ();
75     fprintf (stderr, "\nError reading file in dialog_textbox().\n");
76     exit (-1);
77     }
78     buf[bytes_read] = '\0';    /* mark end of valid data */
79     page = buf;            /* page is pointer to start of page to be displayed */
80
81     /* center dialog box on screen */
82     x = (COLS - width) / 2;
83     y = (LINES - height) / 2;
84
85
86     draw_shadow (stdscr, y, x, height, width);
87
88     dialog = newwin (height, width, y, x);
89     keypad (dialog, TRUE);
90
91     /* Create window for text region, used for scrolling text */
92     text = subwin (dialog, height - 4, width - 2, y + 1, x + 1);
93     wattrset (text, dialog_attr);
94     wbkgdset (text, dialog_attr & A_COLOR);
95
96     keypad (text, TRUE);
97
98     /* register the new window, along with its borders */
99     draw_box (dialog, 0, 0, height, width, dialog_attr, border_attr);
100
101     wattrset (dialog, border_attr);
102     mvwaddch (dialog, height-3, 0, ACS_LTEE);
103     for (i = 0; i < width - 2; i++)
104     waddch (dialog, ACS_HLINE);
105     wattrset (dialog, dialog_attr);
106     wbkgdset (dialog, dialog_attr & A_COLOR);
107     waddch (dialog, ACS_RTEE);
108
109     if (title != NULL && strlen(title) >= width-2 ) {
110     /* truncate long title -- mec */
111     char * title2 = malloc(width-2+1);
112     memcpy( title2, title, width-2 );
113     title2[width-2] = '\0';
114     title = title2;
115     }
116
117     if (title != NULL) {
118     wattrset (dialog, title_attr);
119     mvwaddch (dialog, 0, (width - strlen(title))/2 - 1, ' ');
120     waddstr (dialog, (char *)title);
121     waddch (dialog, ' ');
122     }
123     print_button (dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
124     wnoutrefresh (dialog);
125     getyx (dialog, cur_y, cur_x);    /* Save cursor position */
126
127     /* Print first page of text */
128     attr_clear (text, height - 4, width - 2, dialog_attr);
129     print_page (text, height - 4, width - 2);
130     print_position (dialog, height, width);
131     wmove (dialog, cur_y, cur_x);    /* Restore cursor position */
132     wrefresh (dialog);
133
134     while ((key != ESC) && (key != '\n')) {
135     key = wgetch (dialog);
136     switch (key) {
137     case 'E':        /* Exit */
138     case 'e':
139     case 'X':
140     case 'x':
141         delwin (dialog);
142         free (buf);
143         close (fd);
144         return 0;
145     case 'g':        /* First page */
146     case KEY_HOME:
147         if (!begin_reached) {
148         begin_reached = 1;
149         /* First page not in buffer? */
150         if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
151             endwin ();
152             fprintf (stderr,
153               "\nError moving file pointer in dialog_textbox().\n");
154             exit (-1);
155         }
156         if (fpos > bytes_read) {    /* Yes, we have to read it in */
157             if (lseek (fd, 0, SEEK_SET) == -1) {
158             endwin ();
159             fprintf (stderr, "\nError moving file pointer in "
160                  "dialog_textbox().\n");
161             exit (-1);
162             }
163             if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
164             endwin ();
165             fprintf (stderr,
166                  "\nError reading file in dialog_textbox().\n");
167             exit (-1);
168             }
169             buf[bytes_read] = '\0';
170         }
171         page = buf;
172         print_page (text, height - 4, width - 2);
173         print_position (dialog, height, width);
174         wmove (dialog, cur_y, cur_x);    /* Restore cursor position */
175         wrefresh (dialog);
176         }
177         break;
178     case 'G':        /* Last page */
179     case KEY_END:
180
181         end_reached = 1;
182         /* Last page not in buffer? */
183         if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
184         endwin ();
185         fprintf (stderr,
186               "\nError moving file pointer in dialog_textbox().\n");
187         exit (-1);
188         }
189         if (fpos < file_size) {    /* Yes, we have to read it in */
190         if (lseek (fd, -BUF_SIZE, SEEK_END) == -1) {
191             endwin ();
192             fprintf (stderr,
193               "\nError moving file pointer in dialog_textbox().\n");
194             exit (-1);
195         }
196         if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
197             endwin ();
198             fprintf (stderr,
199                  "\nError reading file in dialog_textbox().\n");
200             exit (-1);
201         }
202         buf[bytes_read] = '\0';
203         }
204         page = buf + bytes_read;
205         back_lines (height - 4);
206         print_page (text, height - 4, width - 2);
207         print_position (dialog, height, width);
208         wmove (dialog, cur_y, cur_x);    /* Restore cursor position */
209         wrefresh (dialog);
210         break;
211     case 'K':        /* Previous line */
212     case 'k':
213     case KEY_UP:
214         if (!begin_reached) {
215         back_lines (page_length + 1);
216
217         /* We don't call print_page() here but use scrolling to ensure
218            faster screen update. However, 'end_reached' and
219            'page_length' should still be updated, and 'page' should
220            point to start of next page. This is done by calling
221            get_line() in the following 'for' loop. */
222         scrollok (text, TRUE);
223         wscrl (text, -1);    /* Scroll text region down one line */
224         scrollok (text, FALSE);
225         page_length = 0;
226         passed_end = 0;
227         for (i = 0; i < height - 4; i++) {
228             if (!i) {
229             /* print first line of page */
230             print_line (text, 0, width - 2);
231             wnoutrefresh (text);
232             } else
233             /* Called to update 'end_reached' and 'page' */
234             get_line ();
235             if (!passed_end)
236             page_length++;
237             if (end_reached && !passed_end)
238             passed_end = 1;
239         }
240
241         print_position (dialog, height, width);
242         wmove (dialog, cur_y, cur_x);    /* Restore cursor position */
243         wrefresh (dialog);
244         }
245         break;
246     case 'B':        /* Previous page */
247     case 'b':
248     case KEY_PPAGE:
249         if (begin_reached)
250         break;
251         back_lines (page_length + height - 4);
252         print_page (text, height - 4, width - 2);
253         print_position (dialog, height, width);
254         wmove (dialog, cur_y, cur_x);
255         wrefresh (dialog);
256         break;
257     case 'J':        /* Next line */
258     case 'j':
259     case KEY_DOWN:
260         if (!end_reached) {
261         begin_reached = 0;
262         scrollok (text, TRUE);
263         scroll (text);    /* Scroll text region up one line */
264         scrollok (text, FALSE);
265         print_line (text, height - 5, width - 2);
266         wnoutrefresh (text);
267         print_position (dialog, height, width);
268         wmove (dialog, cur_y, cur_x);    /* Restore cursor position */
269         wrefresh (dialog);
270         }
271         break;
272     case KEY_NPAGE:        /* Next page */
273     case ' ':
274         if (end_reached)
275         break;
276
277         begin_reached = 0;
278         print_page (text, height - 4, width - 2);
279         print_position (dialog, height, width);
280         wmove (dialog, cur_y, cur_x);
281         wrefresh (dialog);
282         break;
283     case '0':        /* Beginning of line */
284     case 'H':        /* Scroll left */
285     case 'h':
286     case KEY_LEFT:
287         if (hscroll <= 0)
288         break;
289
290         if (key == '0')
291         hscroll = 0;
292         else
293         hscroll--;
294         /* Reprint current page to scroll horizontally */
295         back_lines (page_length);
296         print_page (text, height - 4, width - 2);
297         wmove (dialog, cur_y, cur_x);
298         wrefresh (dialog);
299         break;
300     case 'L':        /* Scroll right */
301     case 'l':
302     case KEY_RIGHT:
303         if (hscroll >= MAX_LEN)
304         break;
305         hscroll++;
306         /* Reprint current page to scroll horizontally */
307         back_lines (page_length);
308         print_page (text, height - 4, width - 2);
309         wmove (dialog, cur_y, cur_x);
310         wrefresh (dialog);
311         break;
312     case ESC:
313         break;
314     }
315     }
316
317     delwin (dialog);
318     free (buf);
319     close (fd);
320     return -1;            /* ESC pressed */
321 }
322
323 /*
324  * Go back 'n' lines in text file. Called by dialog_textbox().
325  * 'page' will be updated to point to the desired line in 'buf'.
326  */
327 static void
328 back_lines (int n)
329 {
330     int i, fpos;
331
332     begin_reached = 0;
333     /* We have to distinguish between end_reached and !end_reached
334        since at end of file, the line is not ended by a '\n'.
335        The code inside 'if' basically does a '--page' to move one
336        character backward so as to skip '\n' of the previous line */
337     if (!end_reached) {
338     /* Either beginning of buffer or beginning of file reached? */
339     if (page == buf) {
340         if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
341         endwin ();
342         fprintf (stderr, "\nError moving file pointer in "
343              "back_lines().\n");
344         exit (-1);
345         }
346         if (fpos > bytes_read) {    /* Not beginning of file yet */
347         /* We've reached beginning of buffer, but not beginning of
348            file yet, so read previous part of file into buffer.
349            Note that we only move backward for BUF_SIZE/2 bytes,
350            but not BUF_SIZE bytes to avoid re-reading again in
351            print_page() later */
352         /* Really possible to move backward BUF_SIZE/2 bytes? */
353         if (fpos < BUF_SIZE / 2 + bytes_read) {
354             /* No, move less then */
355             if (lseek (fd, 0, SEEK_SET) == -1) {
356             endwin ();
357             fprintf (stderr, "\nError moving file pointer in "
358                  "back_lines().\n");
359             exit (-1);
360             }
361             page = buf + fpos - bytes_read;
362         } else {    /* Move backward BUF_SIZE/2 bytes */
363             if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR)
364             == -1) {
365             endwin ();
366             fprintf (stderr, "\nError moving file pointer "
367                  "in back_lines().\n");
368             exit (-1);
369             }
370             page = buf + BUF_SIZE / 2;
371         }
372         if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
373             endwin ();
374             fprintf (stderr, "\nError reading file in back_lines().\n");
375             exit (-1);
376         }
377         buf[bytes_read] = '\0';
378         } else {        /* Beginning of file reached */
379         begin_reached = 1;
380         return;
381         }
382     }
383     if (*(--page) != '\n') {    /* '--page' here */
384         /* Something's wrong... */
385         endwin ();
386         fprintf (stderr, "\nInternal error in back_lines().\n");
387         exit (-1);
388     }
389     }
390     /* Go back 'n' lines */
391     for (i = 0; i < n; i++)
392     do {
393         if (page == buf) {
394         if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
395             endwin ();
396             fprintf (stderr,
397               "\nError moving file pointer in back_lines().\n");
398             exit (-1);
399         }
400         if (fpos > bytes_read) {
401             /* Really possible to move backward BUF_SIZE/2 bytes? */
402             if (fpos < BUF_SIZE / 2 + bytes_read) {
403             /* No, move less then */
404             if (lseek (fd, 0, SEEK_SET) == -1) {
405                 endwin ();
406                 fprintf (stderr, "\nError moving file pointer "
407                      "in back_lines().\n");
408                 exit (-1);
409             }
410             page = buf + fpos - bytes_read;
411             } else {    /* Move backward BUF_SIZE/2 bytes */
412             if (lseek (fd, -(BUF_SIZE / 2 + bytes_read),
413                    SEEK_CUR) == -1) {
414                 endwin ();
415                 fprintf (stderr, "\nError moving file pointer"
416                      " in back_lines().\n");
417                 exit (-1);
418             }
419             page = buf + BUF_SIZE / 2;
420             }
421             if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
422             endwin ();
423             fprintf (stderr, "\nError reading file in "
424                  "back_lines().\n");
425             exit (-1);
426             }
427             buf[bytes_read] = '\0';
428         } else {    /* Beginning of file reached */
429             begin_reached = 1;
430             return;
431         }
432         }
433     } while (*(--page) != '\n');
434     page++;
435 }
436
437 /*
438  * Print a new page of text. Called by dialog_textbox().
439  */
440 static void
441 print_page (WINDOW * win, int height, int width)
442 {
443     int i, passed_end = 0;
444
445     page_length = 0;
446     for (i = 0; i < height; i++) {
447     print_line (win, i, width);
448     if (!passed_end)
449         page_length++;
450     if (end_reached && !passed_end)
451         passed_end = 1;
452     }
453     wnoutrefresh (win);
454 }
455
456 /*
457  * Print a new line of text. Called by dialog_textbox() and print_page().
458  */
459 static void
460 print_line (WINDOW * win, int row, int width)
461 {
462     int y, x;
463     char *line;
464
465     line = get_line ();
466     line += MIN (strlen (line), hscroll);    /* Scroll horizontally */
467     wmove (win, row, 0);    /* move cursor to correct line */
468     waddch (win, ' ');
469     waddnstr (win, line, MIN (strlen (line), width - 2));
470
471     getyx (win, y, x);
472     /* Clear 'residue' of previous line */
473 #if OLD_NCURSES
474     {
475         int i;
476         for (i = 0; i < width - x; i++)
477         waddch (win, ' ');
478     }
479 #else
480     wclrtoeol(win);
481 #endif
482 }
483
484 /*
485  * Return current line of text. Called by dialog_textbox() and print_line().
486  * 'page' should point to start of current line before calling, and will be
487  * updated to point to start of next line.
488  */
489 static char *
490 get_line (void)
491 {
492     int i = 0, fpos;
493     static char line[MAX_LEN + 1];
494
495     end_reached = 0;
496     while (*page != '\n') {
497     if (*page == '\0') {
498         /* Either end of file or end of buffer reached */
499         if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
500         endwin ();
501         fprintf (stderr, "\nError moving file pointer in "
502              "get_line().\n");
503         exit (-1);
504         }
505         if (fpos < file_size) {    /* Not end of file yet */
506         /* We've reached end of buffer, but not end of file yet,
507            so read next part of file into buffer */
508         if ((bytes_read = read (fd, buf, BUF_SIZE)) == -1) {
509             endwin ();
510             fprintf (stderr, "\nError reading file in get_line().\n");
511             exit (-1);
512         }
513         buf[bytes_read] = '\0';
514         page = buf;
515         } else {
516         if (!end_reached)
517             end_reached = 1;
518         break;
519         }
520     } else if (i < MAX_LEN)
521         line[i++] = *(page++);
522     else {
523         /* Truncate lines longer than MAX_LEN characters */
524         if (i == MAX_LEN)
525         line[i++] = '\0';
526         page++;
527     }
528     }
529     if (i <= MAX_LEN)
530     line[i] = '\0';
531     if (!end_reached)
532     page++;            /* move pass '\n' */
533
534     return line;
535 }
536
537 /*
538  * Print current position
539  */
540 static void
541 print_position (WINDOW * win, int height, int width)
542 {
543     int fpos, percent;
544
545     if ((fpos = lseek (fd, 0, SEEK_CUR)) == -1) {
546     endwin ();
547     fprintf (stderr, "\nError moving file pointer in print_position().\n");
548     exit (-1);
549     }
550     wattrset (win, position_indicator_attr);
551     wbkgdset (win, position_indicator_attr & A_COLOR);
552     percent = !file_size ?
553     100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
554     wmove (win, height - 3, width - 9);
555     wprintw (win, "(%3d%%)", percent);
556 }