]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dialog/textbox.c
Import libc++ trunk r165949. Among other improvements and bug fixes,
[FreeBSD/FreeBSD.git] / contrib / dialog / textbox.c
1 /*
2  *  $Id: textbox.c,v 1.107 2012/07/01 18:13:24 Zoltan.Kelemen Exp $
3  *
4  *  textbox.c -- implements the text box
5  *
6  *  Copyright 2000-2011,2012    Thomas E.  Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *      Free Software Foundation, Inc.
20  *      51 Franklin St., Fifth Floor
21  *      Boston, MA 02110, USA.
22  *
23  *  An earlier version of this program lists as authors:
24  *      Savio Lam (lam836@cs.cuhk.hk)
25  */
26
27 #include <dialog.h>
28 #include <dlg_keys.h>
29
30 #define PAGE_LENGTH     (height - 4)
31 #define PAGE_WIDTH      (width - 2)
32
33 typedef struct {
34     DIALOG_CALLBACK obj;
35     WINDOW *text;
36     const char **buttons;
37     int hscroll;
38     char line[MAX_LEN + 1];
39     int fd;
40     long file_size;
41     long fd_bytes_read;
42     long bytes_read;
43     long buffer_len;
44     bool begin_reached;
45     bool buffer_first;
46     bool end_reached;
47     long page_length;           /* lines on the page which is shown */
48     long in_buf;                /* ending index into buf[] for page */
49     char *buf;
50 } MY_OBJ;
51
52 static long
53 lseek_obj(MY_OBJ * obj, long offset, int mode)
54 {
55     long fpos;
56     if ((fpos = (long) lseek(obj->fd, (off_t) offset, mode)) == -1) {
57         switch (mode) {
58         case SEEK_CUR:
59             dlg_exiterr("Cannot get file position");
60             break;
61         case SEEK_END:
62             dlg_exiterr("Cannot seek to end of file");
63             break;
64         case SEEK_SET:
65             dlg_exiterr("Cannot set file position to %ld", offset);
66             break;
67         }
68     }
69     return fpos;
70 }
71
72 static long
73 ftell_obj(MY_OBJ * obj)
74 {
75     return lseek_obj(obj, 0L, SEEK_CUR);
76 }
77
78 static char *
79 xalloc(size_t size)
80 {
81     char *result = dlg_malloc(char, size);
82     assert_ptr(result, "xalloc");
83     return result;
84 }
85
86 /*
87  * read_high() substitutes read() for tab->spaces conversion
88  *
89  * buffer_len, fd_bytes_read, bytes_read are modified
90  * buf is allocated
91  *
92  * fd_bytes_read is the effective number of bytes read from file
93  * bytes_read is the length of buf, that can be different if tab_correct
94  */
95 static void
96 read_high(MY_OBJ * obj, size_t size_read)
97 {
98     char *buftab, ch;
99     int i = 0, j, n, tmpint;
100     long begin_line;
101
102     /* Allocate space for read buffer */
103     buftab = xalloc(size_read + 1);
104
105     if ((obj->fd_bytes_read = read(obj->fd, buftab, size_read)) != -1) {
106
107         buftab[obj->fd_bytes_read] = '\0';      /* mark end of valid data */
108
109         if (dialog_vars.tab_correct) {
110
111             /* calculate bytes_read by buftab and fd_bytes_read */
112             obj->bytes_read = begin_line = 0;
113             for (j = 0; j < obj->fd_bytes_read; j++)
114                 if (buftab[j] == TAB)
115                     obj->bytes_read += dialog_state.tab_len
116                         - ((obj->bytes_read - begin_line)
117                            % dialog_state.tab_len);
118                 else if (buftab[j] == '\n') {
119                     obj->bytes_read++;
120                     begin_line = obj->bytes_read;
121                 } else
122                     obj->bytes_read++;
123
124             if (obj->bytes_read > obj->buffer_len) {
125                 if (obj->buffer_first)
126                     obj->buffer_first = FALSE;  /* disp = 0 */
127                 else {
128                     free(obj->buf);
129                 }
130
131                 obj->buffer_len = obj->bytes_read;
132
133                 /* Allocate space for read buffer */
134                 obj->buf = xalloc((size_t) obj->buffer_len + 1);
135             }
136
137         } else {
138             if (obj->buffer_first) {
139                 obj->buffer_first = FALSE;
140
141                 /* Allocate space for read buffer */
142                 obj->buf = xalloc(size_read + 1);
143             }
144
145             obj->bytes_read = obj->fd_bytes_read;
146         }
147
148         j = 0;
149         begin_line = 0;
150         while (j < obj->fd_bytes_read)
151             if (((ch = buftab[j++]) == TAB) && (dialog_vars.tab_correct != 0)) {
152                 tmpint = (dialog_state.tab_len
153                           - ((int) ((long) i - begin_line) % dialog_state.tab_len));
154                 for (n = 0; n < tmpint; n++)
155                     obj->buf[i++] = ' ';
156             } else {
157                 if (ch == '\n')
158                     begin_line = i + 1;
159                 obj->buf[i++] = ch;
160             }
161
162         obj->buf[i] = '\0';     /* mark end of valid data */
163
164     }
165     if (obj->bytes_read == -1)
166         dlg_exiterr("Error reading file");
167     free(buftab);
168 }
169
170 static long
171 find_first(MY_OBJ * obj, char *buffer, long length)
172 {
173     long recount = obj->page_length;
174     long result = 0;
175
176     while (length > 0) {
177         if (buffer[length] == '\n') {
178             if (--recount < 0) {
179                 result = length;
180                 break;
181             }
182         }
183         --length;
184     }
185     return result;
186 }
187
188 static long
189 tabize(MY_OBJ * obj, long val, long *first_pos)
190 {
191     long fpos;
192     long i, count, begin_line;
193     char *buftab;
194
195     if (!dialog_vars.tab_correct)
196         return val;
197
198     fpos = ftell_obj(obj);
199
200     lseek_obj(obj, fpos - obj->fd_bytes_read, SEEK_SET);
201
202     /* Allocate space for read buffer */
203     buftab = xalloc((size_t) val + 1);
204
205     if ((read(obj->fd, buftab, (size_t) val)) == -1)
206         dlg_exiterr("Error reading file in tabize().");
207
208     begin_line = count = 0;
209     if (first_pos != 0)
210         *first_pos = 0;
211
212     for (i = 0; i < val; i++) {
213         if ((first_pos != 0) && (count >= val)) {
214             *first_pos = find_first(obj, buftab, i);
215             break;
216         }
217         if (buftab[i] == TAB)
218             count += dialog_state.tab_len
219                 - ((count - begin_line) % dialog_state.tab_len);
220         else if (buftab[i] == '\n') {
221             count++;
222             begin_line = count;
223         } else
224             count++;
225     }
226
227     lseek_obj(obj, fpos, SEEK_SET);
228     free(buftab);
229     return count;
230 }
231 /*
232  * Return current line of text.
233  * 'page' should point to start of current line before calling, and will be
234  * updated to point to start of next line.
235  */
236 static char *
237 get_line(MY_OBJ * obj)
238 {
239     int i = 0;
240     long fpos;
241
242     obj->end_reached = FALSE;
243     while (obj->buf[obj->in_buf] != '\n') {
244         if (obj->buf[obj->in_buf] == '\0') {    /* Either end of file or end of buffer reached */
245             fpos = ftell_obj(obj);
246
247             if (fpos < obj->file_size) {        /* Not end of file yet */
248                 /* We've reached end of buffer, but not end of file yet, so
249                  * read next part of file into buffer
250                  */
251                 read_high(obj, BUF_SIZE);
252                 obj->in_buf = 0;
253             } else {
254                 if (!obj->end_reached)
255                     obj->end_reached = TRUE;
256                 break;
257             }
258         } else if (i < MAX_LEN)
259             obj->line[i++] = obj->buf[obj->in_buf++];
260         else {
261             if (i == MAX_LEN)   /* Truncate lines longer than MAX_LEN characters */
262                 obj->line[i++] = '\0';
263             obj->in_buf++;
264         }
265     }
266     if (i <= MAX_LEN)
267         obj->line[i] = '\0';
268     if (!obj->end_reached)
269         obj->in_buf++;          /* move past '\n' */
270
271     return obj->line;
272 }
273
274 static bool
275 match_string(MY_OBJ * obj, char *string)
276 {
277     char *match = get_line(obj);
278     return strstr(match, string) != 0;
279 }
280
281 /*
282  * Go back 'n' lines in text file. Called by dialog_textbox().
283  * 'in_buf' will be updated to point to the desired line in 'buf'.
284  */
285 static void
286 back_lines(MY_OBJ * obj, long n)
287 {
288     int i;
289     long fpos;
290     long val_to_tabize;
291
292     obj->begin_reached = FALSE;
293     /* We have to distinguish between end_reached and !end_reached since at end
294        * of file, the line is not ended by a '\n'.  The code inside 'if'
295        * basically does a '--in_buf' to move one character backward so as to
296        * skip '\n' of the previous line */
297     if (!obj->end_reached) {
298         /* Either beginning of buffer or beginning of file reached? */
299
300         if (obj->in_buf == 0) {
301             fpos = ftell_obj(obj);
302
303             if (fpos > obj->fd_bytes_read) {    /* Not beginning of file yet */
304                 /* We've reached beginning of buffer, but not beginning of file
305                  * yet, so read previous part of file into buffer.  Note that
306                  * we only move backward for BUF_SIZE/2 bytes, but not BUF_SIZE
307                  * bytes to avoid re-reading again in print_page() later
308                  */
309                 /* Really possible to move backward BUF_SIZE/2 bytes? */
310                 if (fpos < BUF_SIZE / 2 + obj->fd_bytes_read) {
311                     /* No, move less than */
312                     lseek_obj(obj, 0L, SEEK_SET);
313                     val_to_tabize = fpos - obj->fd_bytes_read;
314                 } else {        /* Move backward BUF_SIZE/2 bytes */
315                     lseek_obj(obj, -(BUF_SIZE / 2 + obj->fd_bytes_read), SEEK_CUR);
316                     val_to_tabize = BUF_SIZE / 2;
317                 }
318                 read_high(obj, BUF_SIZE);
319
320                 obj->in_buf = tabize(obj, val_to_tabize, (long *) 0);
321
322             } else {            /* Beginning of file reached */
323                 obj->begin_reached = TRUE;
324                 return;
325             }
326         }
327         obj->in_buf--;
328         if (obj->buf[obj->in_buf] != '\n')
329             /* Something's wrong... */
330             dlg_exiterr("Internal error in back_lines().");
331     }
332
333     /* Go back 'n' lines */
334     for (i = 0; i < n; i++) {
335         do {
336             if (obj->in_buf == 0) {
337                 fpos = ftell_obj(obj);
338
339                 if (fpos > obj->fd_bytes_read) {
340                     /* Really possible to move backward BUF_SIZE/2 bytes? */
341                     if (fpos < BUF_SIZE / 2 + obj->fd_bytes_read) {
342                         /* No, move less than */
343                         lseek_obj(obj, 0L, SEEK_SET);
344                         val_to_tabize = fpos - obj->fd_bytes_read;
345                     } else {    /* Move backward BUF_SIZE/2 bytes */
346                         lseek_obj(obj, -(BUF_SIZE / 2 + obj->fd_bytes_read), SEEK_CUR);
347                         val_to_tabize = BUF_SIZE / 2;
348                     }
349                     read_high(obj, BUF_SIZE);
350
351                     obj->in_buf = tabize(obj, val_to_tabize, (long *) 0);
352
353                 } else {        /* Beginning of file reached */
354                     obj->begin_reached = TRUE;
355                     return;
356                 }
357             }
358         } while (obj->buf[--(obj->in_buf)] != '\n');
359     }
360     obj->in_buf++;
361 }
362
363 /*
364  * Print a new line of text.
365  */
366 static void
367 print_line(MY_OBJ * obj, int row, int width)
368 {
369     if (wmove(obj->text, row, 0) != ERR) {
370         int i, y, x;
371         char *line = get_line(obj);
372         const int *cols = dlg_index_columns(line);
373         const int *indx = dlg_index_wchars(line);
374         int limit = dlg_count_wchars(line);
375         int first = 0;
376         int last = limit;
377
378         if (width > getmaxx(obj->text))
379             width = getmaxx(obj->text);
380         --width;                /* for the leading ' ' */
381
382         for (i = 0; i <= limit && cols[i] < obj->hscroll; ++i)
383             first = i;
384
385         for (i = first; (i <= limit) && ((cols[i] - cols[first]) < width); ++i)
386             last = i;
387
388         (void) waddch(obj->text, ' ');
389         (void) waddnstr(obj->text, line + indx[first], indx[last] - indx[first]);
390
391         getyx(obj->text, y, x);
392         if (y == row) {         /* Clear 'residue' of previous line */
393             for (i = 0; i <= width - x; i++) {
394                 (void) waddch(obj->text, ' ');
395             }
396         }
397     }
398 }
399
400 /*
401  * Print a new page of text.
402  */
403 static void
404 print_page(MY_OBJ * obj, int height, int width)
405 {
406     int i, passed_end = 0;
407
408     obj->page_length = 0;
409     for (i = 0; i < height; i++) {
410         print_line(obj, i, width);
411         if (!passed_end)
412             obj->page_length++;
413         if (obj->end_reached && !passed_end)
414             passed_end = 1;
415     }
416     (void) wnoutrefresh(obj->text);
417     dlg_trace_win(obj->text);
418 }
419
420 /*
421  * Print current position
422  */
423 static void
424 print_position(MY_OBJ * obj, WINDOW *win, int height, int width)
425 {
426     long fpos;
427     long size;
428     long first = -1;
429
430     fpos = ftell_obj(obj);
431     if (dialog_vars.tab_correct)
432         size = tabize(obj, obj->in_buf, &first);
433     else
434         first = find_first(obj, obj->buf, size = obj->in_buf);
435
436     dlg_draw_scrollbar(win,
437                        first,
438                        fpos - obj->fd_bytes_read + size,
439                        fpos - obj->fd_bytes_read + size,
440                        obj->file_size,
441                        0, PAGE_WIDTH,
442                        0, PAGE_LENGTH + 1,
443                        border_attr,
444                        border_attr);
445 }
446
447 /*
448  * Display a dialog box and get the search term from user.
449  */
450 static int
451 get_search_term(WINDOW *dialog, char *input, int height, int width)
452 {
453     /* *INDENT-OFF* */
454     static DLG_KEYS_BINDING binding[] = {
455         INPUTSTR_BINDINGS,
456         HELPKEY_BINDINGS,
457         ENTERKEY_BINDINGS,
458         END_KEYS_BINDING
459     };
460     /* *INDENT-ON* */
461
462     int old_x, old_y;
463     int box_x, box_y;
464     int box_height, box_width;
465     int offset = 0;
466     int key = 0;
467     int fkey = 0;
468     bool first = TRUE;
469     int result = DLG_EXIT_UNKNOWN;
470     const char *caption = _("Search");
471     int len_caption = dlg_count_columns(caption);
472     const int *indx;
473     int limit;
474     WINDOW *widget;
475
476     getbegyx(dialog, old_y, old_x);
477
478     box_height = 1 + (2 * MARGIN);
479     box_width = len_caption + (2 * (MARGIN + 2));
480     box_width = MAX(box_width, 30);
481     box_width = MIN(box_width, getmaxx(dialog) - 2 * MARGIN);
482     len_caption = MIN(len_caption, box_width - (2 * (MARGIN + 1)));
483
484     box_x = (width - box_width) / 2;
485     box_y = (height - box_height) / 2;
486     widget = dlg_new_modal_window(dialog,
487                                   box_height, box_width,
488                                   old_y + box_y, old_x + box_x);
489     keypad(widget, TRUE);
490     dlg_register_window(widget, "searchbox", binding);
491
492     dlg_draw_box2(widget, 0, 0, box_height, box_width,
493                   searchbox_attr,
494                   searchbox_border_attr,
495                   searchbox_border2_attr);
496     wattrset(widget, searchbox_title_attr);
497     (void) wmove(widget, 0, (box_width - len_caption) / 2);
498
499     indx = dlg_index_wchars(caption);
500     limit = dlg_limit_columns(caption, len_caption, 0);
501     (void) waddnstr(widget, caption + indx[0], indx[limit] - indx[0]);
502
503     box_width -= 2;
504     offset = dlg_count_columns(input);
505
506     while (result == DLG_EXIT_UNKNOWN) {
507         if (!first) {
508             key = dlg_getc(widget, &fkey);
509             if (fkey) {
510                 switch (fkey) {
511 #ifdef KEY_RESIZE
512                 case KEY_RESIZE:
513                     result = DLG_EXIT_CANCEL;
514                     continue;
515 #endif
516                 case DLGK_ENTER:
517                     result = DLG_EXIT_OK;
518                     continue;
519                 }
520             } else if (key == ESC) {
521                 result = DLG_EXIT_ESC;
522                 continue;
523             } else if (key == ERR) {
524                 napms(50);
525                 continue;
526             }
527         }
528         if (dlg_edit_string(input, &offset, key, fkey, first)) {
529             dlg_show_string(widget, input, offset, searchbox_attr,
530                             1, 1, box_width, FALSE, first);
531             first = FALSE;
532         }
533     }
534     dlg_del_window(widget);
535     return result;
536 }
537
538 static bool
539 perform_search(MY_OBJ * obj, int height, int width, int key, char *search_term)
540 {
541     int dir;
542     long tempinx;
543     long fpos;
544     int result;
545     bool found;
546     bool temp, temp1;
547     bool moved = FALSE;
548
549     /* set search direction */
550     dir = (key == '/' || key == 'n') ? 1 : 0;
551     if (dir ? !obj->end_reached : !obj->begin_reached) {
552         if (key == 'n' || key == 'N') {
553             if (search_term[0] == '\0') {       /* No search term yet */
554                 (void) beep();
555                 return FALSE;
556             }
557             /* Get search term from user */
558         } else if ((result = get_search_term(obj->text, search_term,
559                                              PAGE_LENGTH,
560                                              PAGE_WIDTH)) != DLG_EXIT_OK
561                    || search_term[0] == '\0') {
562 #ifdef KEY_RESIZE
563             if (result == DLG_EXIT_CANCEL) {
564                 ungetch(key);
565                 ungetch(KEY_RESIZE);
566                 /* FALLTHRU */
567             }
568 #endif
569             /* ESC pressed, or no search term, reprint page to clear box */
570             wattrset(obj->text, dialog_attr);
571             back_lines(obj, obj->page_length);
572             return TRUE;
573         }
574         /* Save variables for restoring in case search term can't be found */
575         tempinx = obj->in_buf;
576         temp = obj->begin_reached;
577         temp1 = obj->end_reached;
578         fpos = ftell_obj(obj) - obj->fd_bytes_read;
579         /* update 'in_buf' to point to next (previous) line before
580            forward (backward) searching */
581         back_lines(obj, (dir
582                          ? obj->page_length - 1
583                          : obj->page_length + 1));
584         if (dir) {              /* Forward search */
585             while ((found = match_string(obj, search_term)) == FALSE) {
586                 if (obj->end_reached)
587                     break;
588             }
589         } else {                /* Backward search */
590             while ((found = match_string(obj, search_term)) == FALSE) {
591                 if (obj->begin_reached)
592                     break;
593                 back_lines(obj, 2L);
594             }
595         }
596         if (found == FALSE) {   /* not found */
597             (void) beep();
598             /* Restore program state to that before searching */
599             lseek_obj(obj, fpos, SEEK_SET);
600
601             read_high(obj, BUF_SIZE);
602
603             obj->in_buf = tempinx;
604             obj->begin_reached = temp;
605             obj->end_reached = temp1;
606             /* move 'in_buf' to point to start of current page to
607              * re-print current page.  Note that 'in_buf' always points
608              * to start of next page, so this is necessary
609              */
610             back_lines(obj, obj->page_length);
611         } else {                /* Search term found */
612             back_lines(obj, 1L);
613         }
614         /* Reprint page */
615         wattrset(obj->text, dialog_attr);
616         moved = TRUE;
617     } else {                    /* no need to find */
618         (void) beep();
619     }
620     return moved;
621 }
622
623 /*
624  * Display text from a file in a dialog box.
625  */
626 int
627 dialog_textbox(const char *title, const char *file, int height, int width)
628 {
629     /* *INDENT-OFF* */
630     static DLG_KEYS_BINDING binding[] = {
631         HELPKEY_BINDINGS,
632         ENTERKEY_BINDINGS,
633         DLG_KEYS_DATA( DLGK_GRID_DOWN,  'J' ),
634         DLG_KEYS_DATA( DLGK_GRID_DOWN,  'j' ),
635         DLG_KEYS_DATA( DLGK_GRID_DOWN,  KEY_DOWN ),
636         DLG_KEYS_DATA( DLGK_GRID_LEFT,  'H' ),
637         DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
638         DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
639         DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ),
640         DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
641         DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
642         DLG_KEYS_DATA( DLGK_GRID_UP,    'K' ),
643         DLG_KEYS_DATA( DLGK_GRID_UP,    'k' ),
644         DLG_KEYS_DATA( DLGK_GRID_UP,    KEY_UP ),
645         DLG_KEYS_DATA( DLGK_PAGE_FIRST, 'g' ),
646         DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ),
647         DLG_KEYS_DATA( DLGK_PAGE_LAST,  'G' ),
648         DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_END ),
649         DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_LL ),
650         DLG_KEYS_DATA( DLGK_PAGE_NEXT,  ' ' ),
651         DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
652         DLG_KEYS_DATA( DLGK_PAGE_PREV,  'B' ),
653         DLG_KEYS_DATA( DLGK_PAGE_PREV,  'b' ),
654         DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
655         DLG_KEYS_DATA( DLGK_BEGIN,      '0' ),
656         DLG_KEYS_DATA( DLGK_BEGIN,      KEY_BEG ),
657         DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
658         DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
659         END_KEYS_BINDING
660     };
661     /* *INDENT-ON* */
662
663 #ifdef KEY_RESIZE
664     int old_height = height;
665     int old_width = width;
666 #endif
667     long fpos;
668     int x, y, cur_x, cur_y;
669     int key = 0, fkey;
670     int next = 0;
671     int i, code, passed_end;
672     char search_term[MAX_LEN + 1];
673     MY_OBJ obj;
674     WINDOW *dialog;
675     bool moved;
676     int result = DLG_EXIT_UNKNOWN;
677     int button = dlg_default_button();
678     int min_width = 12;
679
680     search_term[0] = '\0';      /* no search term entered yet */
681
682     memset(&obj, 0, sizeof(obj));
683
684     obj.begin_reached = TRUE;
685     obj.buffer_first = TRUE;
686     obj.end_reached = FALSE;
687     obj.buttons = dlg_exit_label();
688
689     /* Open input file for reading */
690     if ((obj.fd = open(file, O_RDONLY)) == -1)
691         dlg_exiterr("Can't open input file %s", file);
692
693     /* Get file size. Actually, 'file_size' is the real file size - 1,
694        since it's only the last byte offset from the beginning */
695     obj.file_size = lseek_obj(&obj, 0L, SEEK_END);
696
697     /* Restore file pointer to beginning of file after getting file size */
698     lseek_obj(&obj, 0L, SEEK_SET);
699
700     read_high(&obj, BUF_SIZE);
701
702     dlg_button_layout(obj.buttons, &min_width);
703
704 #ifdef KEY_RESIZE
705   retry:
706 #endif
707     moved = TRUE;
708
709     dlg_auto_sizefile(title, file, &height, &width, 2, min_width);
710     dlg_print_size(height, width);
711     dlg_ctl_size(height, width);
712
713     x = dlg_box_x_ordinate(width);
714     y = dlg_box_y_ordinate(height);
715
716     dialog = dlg_new_window(height, width, y, x);
717     dlg_register_window(dialog, "textbox", binding);
718     dlg_register_buttons(dialog, "textbox", obj.buttons);
719
720     dlg_mouse_setbase(x, y);
721
722     /* Create window for text region, used for scrolling text */
723     obj.text = dlg_sub_window(dialog, PAGE_LENGTH, PAGE_WIDTH, y + 1, x + 1);
724
725     /* register the new window, along with its borders */
726     dlg_mouse_mkbigregion(0, 0, PAGE_LENGTH + 2, width, KEY_MAX, 1, 1, 1 /* lines */ );
727     dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
728     dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
729     dlg_draw_title(dialog, title);
730
731     dlg_draw_buttons(dialog, PAGE_LENGTH + 2, 0, obj.buttons, button, FALSE, width);
732     (void) wnoutrefresh(dialog);
733     getyx(dialog, cur_y, cur_x);        /* Save cursor position */
734
735     dlg_attr_clear(obj.text, PAGE_LENGTH, PAGE_WIDTH, dialog_attr);
736
737     while (result == DLG_EXIT_UNKNOWN) {
738
739         /*
740          * Update the screen according to whether we shifted up/down by a line
741          * or not.
742          */
743         if (moved) {
744             if (next < 0) {
745                 (void) scrollok(obj.text, TRUE);
746                 (void) scroll(obj.text);        /* Scroll text region up one line */
747                 (void) scrollok(obj.text, FALSE);
748                 print_line(&obj, PAGE_LENGTH - 1, PAGE_WIDTH);
749                 (void) wnoutrefresh(obj.text);
750             } else if (next > 0) {
751                 /*
752                  * We don't call print_page() here but use scrolling to ensure
753                  * faster screen update.  However, 'end_reached' and
754                  * 'page_length' should still be updated, and 'in_buf' should
755                  * point to start of next page.  This is done by calling
756                  * get_line() in the following 'for' loop.
757                  */
758                 (void) scrollok(obj.text, TRUE);
759                 (void) wscrl(obj.text, -1);     /* Scroll text region down one line */
760                 (void) scrollok(obj.text, FALSE);
761                 obj.page_length = 0;
762                 passed_end = 0;
763                 for (i = 0; i < PAGE_LENGTH; i++) {
764                     if (!i) {
765                         print_line(&obj, 0, PAGE_WIDTH);        /* print first line of page */
766                         (void) wnoutrefresh(obj.text);
767                     } else
768                         (void) get_line(&obj);  /* Called to update 'end_reached' and 'in_buf' */
769                     if (!passed_end)
770                         obj.page_length++;
771                     if (obj.end_reached && !passed_end)
772                         passed_end = 1;
773                 }
774             } else {
775                 print_page(&obj, PAGE_LENGTH, PAGE_WIDTH);
776             }
777             print_position(&obj, dialog, height, width);
778             (void) wmove(dialog, cur_y, cur_x);         /* Restore cursor position */
779             wrefresh(dialog);
780         }
781         moved = FALSE;          /* assume we'll not move */
782         next = 0;               /* ...but not scroll by a line */
783
784         key = dlg_mouse_wgetch(dialog, &fkey);
785         if (dlg_result_key(key, fkey, &result))
786             break;
787
788         if (!fkey && (code = dlg_char_to_button(key, obj.buttons)) >= 0) {
789             result = dlg_ok_buttoncode(code);
790             break;
791         }
792
793         if (fkey) {
794             switch (key) {
795             default:
796                 if (is_DLGK_MOUSE(key)) {
797                     result = dlg_exit_buttoncode(key - M_EVENT);
798                     if (result < 0)
799                         result = DLG_EXIT_OK;
800                 } else {
801                     beep();
802                 }
803                 break;
804             case DLGK_FIELD_NEXT:
805                 button = dlg_next_button(obj.buttons, button);
806                 if (button < 0)
807                     button = 0;
808                 dlg_draw_buttons(dialog,
809                                  height - 2, 0,
810                                  obj.buttons, button,
811                                  FALSE, width);
812                 break;
813             case DLGK_FIELD_PREV:
814                 button = dlg_prev_button(obj.buttons, button);
815                 if (button < 0)
816                     button = 0;
817                 dlg_draw_buttons(dialog,
818                                  height - 2, 0,
819                                  obj.buttons, button,
820                                  FALSE, width);
821                 break;
822             case DLGK_ENTER:
823                 if (dialog_vars.nook)
824                     result = DLG_EXIT_OK;
825                 else
826                     result = dlg_exit_buttoncode(button);
827                 break;
828             case DLGK_PAGE_FIRST:
829                 if (!obj.begin_reached) {
830                     obj.begin_reached = 1;
831                     /* First page not in buffer? */
832                     fpos = ftell_obj(&obj);
833
834                     if (fpos > obj.fd_bytes_read) {
835                         /* Yes, we have to read it in */
836                         lseek_obj(&obj, 0L, SEEK_SET);
837
838                         read_high(&obj, BUF_SIZE);
839                     }
840                     obj.in_buf = 0;
841                     moved = TRUE;
842                 }
843                 break;
844             case DLGK_PAGE_LAST:
845                 obj.end_reached = TRUE;
846                 /* Last page not in buffer? */
847                 fpos = ftell_obj(&obj);
848
849                 if (fpos < obj.file_size) {
850                     /* Yes, we have to read it in */
851                     lseek_obj(&obj, -BUF_SIZE, SEEK_END);
852
853                     read_high(&obj, BUF_SIZE);
854                 }
855                 obj.in_buf = obj.bytes_read;
856                 back_lines(&obj, (long) PAGE_LENGTH);
857                 moved = TRUE;
858                 break;
859             case DLGK_GRID_UP:  /* Previous line */
860                 if (!obj.begin_reached) {
861                     back_lines(&obj, obj.page_length + 1);
862                     next = 1;
863                     moved = TRUE;
864                 }
865                 break;
866             case DLGK_PAGE_PREV:        /* Previous page */
867             case DLGK_MOUSE(KEY_PPAGE):
868                 if (!obj.begin_reached) {
869                     back_lines(&obj, obj.page_length + PAGE_LENGTH);
870                     moved = TRUE;
871                 }
872                 break;
873             case DLGK_GRID_DOWN:        /* Next line */
874                 if (!obj.end_reached) {
875                     obj.begin_reached = 0;
876                     next = -1;
877                     moved = TRUE;
878                 }
879                 break;
880             case DLGK_PAGE_NEXT:        /* Next page */
881             case DLGK_MOUSE(KEY_NPAGE):
882                 if (!obj.end_reached) {
883                     obj.begin_reached = 0;
884                     moved = TRUE;
885                 }
886                 break;
887             case DLGK_BEGIN:    /* Beginning of line */
888                 if (obj.hscroll > 0) {
889                     obj.hscroll = 0;
890                     /* Reprint current page to scroll horizontally */
891                     back_lines(&obj, obj.page_length);
892                     moved = TRUE;
893                 }
894                 break;
895             case DLGK_GRID_LEFT:        /* Scroll left */
896                 if (obj.hscroll > 0) {
897                     obj.hscroll--;
898                     /* Reprint current page to scroll horizontally */
899                     back_lines(&obj, obj.page_length);
900                     moved = TRUE;
901                 }
902                 break;
903             case DLGK_GRID_RIGHT:       /* Scroll right */
904                 if (obj.hscroll < MAX_LEN) {
905                     obj.hscroll++;
906                     /* Reprint current page to scroll horizontally */
907                     back_lines(&obj, obj.page_length);
908                     moved = TRUE;
909                 }
910                 break;
911 #ifdef KEY_RESIZE
912             case KEY_RESIZE:
913                 /* reset data */
914                 height = old_height;
915                 width = old_width;
916                 back_lines(&obj, obj.page_length);
917                 /* repaint */
918                 dlg_clear();
919                 dlg_del_window(dialog);
920                 refresh();
921                 dlg_mouse_free_regions();
922                 goto retry;
923 #endif
924             }
925         } else {
926             switch (key) {
927             case '/':           /* Forward search */
928             case 'n':           /* Repeat forward search */
929             case '?':           /* Backward search */
930             case 'N':           /* Repeat backward search */
931                 moved = perform_search(&obj, height, width, key, search_term);
932                 fkey = FALSE;
933                 break;
934             default:
935                 beep();
936                 break;
937             }
938         }
939     }
940
941     dlg_del_window(dialog);
942     free(obj.buf);
943     (void) close(obj.fd);
944     dlg_mouse_free_regions();
945     return result;
946 }