]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/dialog/textbox.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / dialog / textbox.c
1 /*
2  *  $Id: textbox.c,v 1.101 2011/06/29 09:53:03 tom Exp $
3  *
4  *  textbox.c -- implements the text box
5  *
6  *  Copyright 2000-2010,2011    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 }
418
419 /*
420  * Print current position
421  */
422 static void
423 print_position(MY_OBJ * obj, WINDOW *win, int height, int width)
424 {
425     long fpos;
426     long size;
427     long first = -1;
428
429     fpos = ftell_obj(obj);
430     if (dialog_vars.tab_correct)
431         size = tabize(obj, obj->in_buf, &first);
432     else
433         first = find_first(obj, obj->buf, size = obj->in_buf);
434
435     dlg_draw_scrollbar(win,
436                        first,
437                        fpos - obj->fd_bytes_read + size,
438                        fpos - obj->fd_bytes_read + size,
439                        obj->file_size,
440                        0, PAGE_WIDTH,
441                        0, PAGE_LENGTH + 1,
442                        border_attr,
443                        border_attr);
444 }
445
446 /*
447  * Display a dialog box and get the search term from user.
448  */
449 static int
450 get_search_term(WINDOW *dialog, char *input, int height, int width)
451 {
452     /* *INDENT-OFF* */
453     static DLG_KEYS_BINDING binding[] = {
454         INPUTSTR_BINDINGS,
455         HELPKEY_BINDINGS,
456         ENTERKEY_BINDINGS,
457         END_KEYS_BINDING
458     };
459     /* *INDENT-ON* */
460
461     int old_x, old_y;
462     int box_x, box_y;
463     int box_height, box_width;
464     int offset = 0;
465     int key = 0;
466     int fkey = 0;
467     bool first = TRUE;
468     int result = DLG_EXIT_UNKNOWN;
469     const char *caption = _("Search");
470     int len_caption = dlg_count_columns(caption);
471     const int *indx;
472     int limit;
473     WINDOW *widget;
474
475     getbegyx(dialog, old_y, old_x);
476
477     box_height = 1 + (2 * MARGIN);
478     box_width = len_caption + (2 * (MARGIN + 2));
479     box_width = MAX(box_width, 30);
480     box_width = MIN(box_width, getmaxx(dialog) - 2 * MARGIN);
481     len_caption = MIN(len_caption, box_width - (2 * (MARGIN + 1)));
482
483     box_x = (width - box_width) / 2;
484     box_y = (height - box_height) / 2;
485     widget = dlg_new_modal_window(dialog,
486                                   box_height, box_width,
487                                   old_y + box_y, old_x + box_x);
488     keypad(widget, TRUE);
489     dlg_register_window(widget, "searchbox", binding);
490
491     dlg_draw_box(widget, 0, 0, box_height, box_width,
492                  searchbox_attr,
493                  searchbox_border_attr);
494     wattrset(widget, searchbox_title_attr);
495     (void) wmove(widget, 0, (box_width - len_caption) / 2);
496
497     indx = dlg_index_wchars(caption);
498     limit = dlg_limit_columns(caption, len_caption, 0);
499     (void) waddnstr(widget, caption + indx[0], indx[limit] - indx[0]);
500
501     box_y++;
502     box_x++;
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         found = FALSE;
585         if (dir) {              /* Forward search */
586             while ((found = match_string(obj, search_term)) == FALSE) {
587                 if (obj->end_reached)
588                     break;
589             }
590         } else {                /* Backward search */
591             while ((found = match_string(obj, search_term)) == FALSE) {
592                 if (obj->begin_reached)
593                     break;
594                 back_lines(obj, 2L);
595             }
596         }
597         if (found == FALSE) {   /* not found */
598             (void) beep();
599             /* Restore program state to that before searching */
600             lseek_obj(obj, fpos, SEEK_SET);
601
602             read_high(obj, BUF_SIZE);
603
604             obj->in_buf = tempinx;
605             obj->begin_reached = temp;
606             obj->end_reached = temp1;
607             /* move 'in_buf' to point to start of current page to
608              * re-print current page.  Note that 'in_buf' always points
609              * to start of next page, so this is necessary
610              */
611             back_lines(obj, obj->page_length);
612         } else {                /* Search term found */
613             back_lines(obj, 1L);
614         }
615         /* Reprint page */
616         wattrset(obj->text, dialog_attr);
617         moved = TRUE;
618     } else {                    /* no need to find */
619         (void) beep();
620     }
621     return moved;
622 }
623
624 /*
625  * Display text from a file in a dialog box.
626  */
627 int
628 dialog_textbox(const char *title, const char *file, int height, int width)
629 {
630     /* *INDENT-OFF* */
631     static DLG_KEYS_BINDING binding[] = {
632         HELPKEY_BINDINGS,
633         ENTERKEY_BINDINGS,
634         DLG_KEYS_DATA( DLGK_GRID_DOWN,  'J' ),
635         DLG_KEYS_DATA( DLGK_GRID_DOWN,  'j' ),
636         DLG_KEYS_DATA( DLGK_GRID_DOWN,  KEY_DOWN ),
637         DLG_KEYS_DATA( DLGK_GRID_LEFT,  'H' ),
638         DLG_KEYS_DATA( DLGK_GRID_LEFT,  'h' ),
639         DLG_KEYS_DATA( DLGK_GRID_LEFT,  KEY_LEFT ),
640         DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ),
641         DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ),
642         DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ),
643         DLG_KEYS_DATA( DLGK_GRID_UP,    'K' ),
644         DLG_KEYS_DATA( DLGK_GRID_UP,    'k' ),
645         DLG_KEYS_DATA( DLGK_GRID_UP,    KEY_UP ),
646         DLG_KEYS_DATA( DLGK_PAGE_FIRST, 'g' ),
647         DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ),
648         DLG_KEYS_DATA( DLGK_PAGE_LAST,  'G' ),
649         DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_END ),
650         DLG_KEYS_DATA( DLGK_PAGE_LAST,  KEY_LL ),
651         DLG_KEYS_DATA( DLGK_PAGE_NEXT,  ' ' ),
652         DLG_KEYS_DATA( DLGK_PAGE_NEXT,  KEY_NPAGE ),
653         DLG_KEYS_DATA( DLGK_PAGE_PREV,  'B' ),
654         DLG_KEYS_DATA( DLGK_PAGE_PREV,  'b' ),
655         DLG_KEYS_DATA( DLGK_PAGE_PREV,  KEY_PPAGE ),
656         DLG_KEYS_DATA( DLGK_BEGIN,      '0' ),
657         DLG_KEYS_DATA( DLGK_BEGIN,      KEY_BEG ),
658         DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ),
659         DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ),
660         END_KEYS_BINDING
661     };
662     /* *INDENT-ON* */
663
664 #ifdef KEY_RESIZE
665     int old_height = height;
666     int old_width = width;
667 #endif
668     long fpos;
669     int x, y, cur_x, cur_y;
670     int key = 0, fkey;
671     int next = 0;
672     int i, code, passed_end;
673     char search_term[MAX_LEN + 1];
674     MY_OBJ obj;
675     WINDOW *dialog;
676     bool moved;
677     int result = DLG_EXIT_UNKNOWN;
678     int button = dialog_vars.extra_button ? dlg_defaultno_button() : 0;
679     int min_width = 12;
680
681     search_term[0] = '\0';      /* no search term entered yet */
682
683     memset(&obj, 0, sizeof(obj));
684
685     obj.begin_reached = TRUE;
686     obj.buffer_first = TRUE;
687     obj.end_reached = FALSE;
688     obj.buttons = dlg_exit_label();
689
690     /* Open input file for reading */
691     if ((obj.fd = open(file, O_RDONLY)) == -1)
692         dlg_exiterr("Can't open input file %s", file);
693
694     /* Get file size. Actually, 'file_size' is the real file size - 1,
695        since it's only the last byte offset from the beginning */
696     obj.file_size = lseek_obj(&obj, 0L, SEEK_END);
697
698     /* Restore file pointer to beginning of file after getting file size */
699     lseek_obj(&obj, 0L, SEEK_SET);
700
701     read_high(&obj, BUF_SIZE);
702
703     dlg_button_layout(obj.buttons, &min_width);
704
705 #ifdef KEY_RESIZE
706   retry:
707 #endif
708     moved = TRUE;
709
710     dlg_auto_sizefile(title, file, &height, &width, 2, min_width);
711     dlg_print_size(height, width);
712     dlg_ctl_size(height, width);
713
714     x = dlg_box_x_ordinate(width);
715     y = dlg_box_y_ordinate(height);
716
717     dialog = dlg_new_window(height, width, y, x);
718     dlg_register_window(dialog, "textbox", binding);
719     dlg_register_buttons(dialog, "textbox", obj.buttons);
720
721     dlg_mouse_setbase(x, y);
722
723     /* Create window for text region, used for scrolling text */
724     obj.text = dlg_sub_window(dialog, PAGE_LENGTH, PAGE_WIDTH, y + 1, x + 1);
725
726     /* register the new window, along with its borders */
727     dlg_mouse_mkbigregion(0, 0, PAGE_LENGTH + 2, width, KEY_MAX, 1, 1, 1 /* lines */ );
728     dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
729     dlg_draw_bottom_box(dialog);
730     dlg_draw_title(dialog, title);
731
732     dlg_draw_buttons(dialog, PAGE_LENGTH + 2, 0, obj.buttons, button, FALSE, width);
733     (void) wnoutrefresh(dialog);
734     getyx(dialog, cur_y, cur_x);        /* Save cursor position */
735
736     dlg_attr_clear(obj.text, PAGE_LENGTH, PAGE_WIDTH, dialog_attr);
737
738     while (result == DLG_EXIT_UNKNOWN) {
739
740         /*
741          * Update the screen according to whether we shifted up/down by a line
742          * or not.
743          */
744         if (moved) {
745             if (next < 0) {
746                 (void) scrollok(obj.text, TRUE);
747                 (void) scroll(obj.text);        /* Scroll text region up one line */
748                 (void) scrollok(obj.text, FALSE);
749                 print_line(&obj, PAGE_LENGTH - 1, PAGE_WIDTH);
750                 (void) wnoutrefresh(obj.text);
751             } else if (next > 0) {
752                 /*
753                  * We don't call print_page() here but use scrolling to ensure
754                  * faster screen update.  However, 'end_reached' and
755                  * 'page_length' should still be updated, and 'in_buf' should
756                  * point to start of next page.  This is done by calling
757                  * get_line() in the following 'for' loop.
758                  */
759                 (void) scrollok(obj.text, TRUE);
760                 (void) wscrl(obj.text, -1);     /* Scroll text region down one line */
761                 (void) scrollok(obj.text, FALSE);
762                 obj.page_length = 0;
763                 passed_end = 0;
764                 for (i = 0; i < PAGE_LENGTH; i++) {
765                     if (!i) {
766                         print_line(&obj, 0, PAGE_WIDTH);        /* print first line of page */
767                         (void) wnoutrefresh(obj.text);
768                     } else
769                         (void) get_line(&obj);  /* Called to update 'end_reached' and 'in_buf' */
770                     if (!passed_end)
771                         obj.page_length++;
772                     if (obj.end_reached && !passed_end)
773                         passed_end = 1;
774                 }
775             } else {
776                 print_page(&obj, PAGE_LENGTH, PAGE_WIDTH);
777             }
778             print_position(&obj, dialog, height, width);
779             (void) wmove(dialog, cur_y, cur_x);         /* Restore cursor position */
780             wrefresh(dialog);
781         }
782         moved = FALSE;          /* assume we'll not move */
783         next = 0;               /* ...but not scroll by a line */
784
785         key = dlg_mouse_wgetch(dialog, &fkey);
786         if (dlg_result_key(key, fkey, &result))
787             break;
788
789         if (!fkey && (code = dlg_char_to_button(key, obj.buttons)) >= 0) {
790             result = dlg_ok_buttoncode(code);
791             break;
792         }
793
794         if (fkey) {
795             switch (key) {
796             default:
797                 if (is_DLGK_MOUSE(key)) {
798                     result = dlg_exit_buttoncode(key - M_EVENT);
799                     if (result < 0)
800                         result = DLG_EXIT_OK;
801                 } else {
802                     beep();
803                 }
804                 break;
805             case DLGK_FIELD_NEXT:
806                 button = dlg_next_button(obj.buttons, button);
807                 if (button < 0)
808                     button = 0;
809                 dlg_draw_buttons(dialog,
810                                  height - 2, 0,
811                                  obj.buttons, button,
812                                  FALSE, width);
813                 break;
814             case DLGK_FIELD_PREV:
815                 button = dlg_prev_button(obj.buttons, button);
816                 if (button < 0)
817                     button = 0;
818                 dlg_draw_buttons(dialog,
819                                  height - 2, 0,
820                                  obj.buttons, button,
821                                  FALSE, width);
822                 break;
823             case DLGK_ENTER:
824                 if (dialog_vars.nook)
825                     result = DLG_EXIT_OK;
826                 else
827                     result = dlg_exit_buttoncode(button);
828                 break;
829             case DLGK_PAGE_FIRST:
830                 if (!obj.begin_reached) {
831                     obj.begin_reached = 1;
832                     /* First page not in buffer? */
833                     fpos = ftell_obj(&obj);
834
835                     if (fpos > obj.fd_bytes_read) {
836                         /* Yes, we have to read it in */
837                         lseek_obj(&obj, 0L, SEEK_SET);
838
839                         read_high(&obj, BUF_SIZE);
840                     }
841                     obj.in_buf = 0;
842                     moved = TRUE;
843                 }
844                 break;
845             case DLGK_PAGE_LAST:
846                 obj.end_reached = TRUE;
847                 /* Last page not in buffer? */
848                 fpos = ftell_obj(&obj);
849
850                 if (fpos < obj.file_size) {
851                     /* Yes, we have to read it in */
852                     lseek_obj(&obj, -BUF_SIZE, SEEK_END);
853
854                     read_high(&obj, BUF_SIZE);
855                 }
856                 obj.in_buf = obj.bytes_read;
857                 back_lines(&obj, (long) PAGE_LENGTH);
858                 moved = TRUE;
859                 break;
860             case DLGK_GRID_UP:  /* Previous line */
861                 if (!obj.begin_reached) {
862                     back_lines(&obj, obj.page_length + 1);
863                     next = 1;
864                     moved = TRUE;
865                 }
866                 break;
867             case DLGK_PAGE_PREV:        /* Previous page */
868             case DLGK_MOUSE(KEY_PPAGE):
869                 if (!obj.begin_reached) {
870                     back_lines(&obj, obj.page_length + PAGE_LENGTH);
871                     moved = TRUE;
872                 }
873                 break;
874             case DLGK_GRID_DOWN:        /* Next line */
875                 if (!obj.end_reached) {
876                     obj.begin_reached = 0;
877                     next = -1;
878                     moved = TRUE;
879                 }
880                 break;
881             case DLGK_PAGE_NEXT:        /* Next page */
882             case DLGK_MOUSE(KEY_NPAGE):
883                 if (!obj.end_reached) {
884                     obj.begin_reached = 0;
885                     moved = TRUE;
886                 }
887                 break;
888             case DLGK_BEGIN:    /* Beginning of line */
889                 if (obj.hscroll > 0) {
890                     obj.hscroll = 0;
891                     /* Reprint current page to scroll horizontally */
892                     back_lines(&obj, obj.page_length);
893                     moved = TRUE;
894                 }
895                 break;
896             case DLGK_GRID_LEFT:        /* Scroll left */
897                 if (obj.hscroll > 0) {
898                     obj.hscroll--;
899                     /* Reprint current page to scroll horizontally */
900                     back_lines(&obj, obj.page_length);
901                     moved = TRUE;
902                 }
903                 break;
904             case DLGK_GRID_RIGHT:       /* Scroll right */
905                 if (obj.hscroll < MAX_LEN) {
906                     obj.hscroll++;
907                     /* Reprint current page to scroll horizontally */
908                     back_lines(&obj, obj.page_length);
909                     moved = TRUE;
910                 }
911                 break;
912 #ifdef KEY_RESIZE
913             case KEY_RESIZE:
914                 /* reset data */
915                 height = old_height;
916                 width = old_width;
917                 back_lines(&obj, obj.page_length);
918                 moved = TRUE;
919                 /* repaint */
920                 dlg_clear();
921                 dlg_del_window(dialog);
922                 refresh();
923                 dlg_mouse_free_regions();
924                 goto retry;
925 #endif
926             }
927         } else {
928             switch (key) {
929             case '/':           /* Forward search */
930             case 'n':           /* Repeat forward search */
931             case '?':           /* Backward search */
932             case 'N':           /* Repeat backward search */
933                 moved = perform_search(&obj, height, width, key, search_term);
934                 fkey = FALSE;
935                 break;
936             default:
937                 beep();
938                 break;
939             }
940         }
941     }
942
943     dlg_del_window(dialog);
944     free(obj.buf);
945     (void) close(obj.fd);
946     dlg_mouse_free_regions();
947     return result;
948 }