2 * $Id: tailbox.c,v 1.80 2022/04/03 22:38:16 tom Exp $
4 * tailbox.c -- implements the tail box
6 * Copyright 2000-2020,2022 Thomas E. Dickey
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.
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.
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.
23 * An earlier version of this program lists as authors
24 * Pasquale De Marco (demarco_p@abramo.it)
27 #include <dlg_internals.h>
36 char line
[MAX_LEN
+ 2];
41 * Return current line of text.
44 get_line(MY_OBJ
* obj
)
46 FILE *fp
= obj
->obj
.input
;
47 int col
= -(obj
->hscroll
);
51 if (((ch
= getc(fp
)) == EOF
) && !feof(fp
))
52 dlg_exiterr("Error moving file pointer in get_line().");
53 else if (!feof(fp
) && (ch
!= '\n')) {
54 if ((ch
== TAB
) && (dialog_vars
.tab_correct
)) {
55 tmpint
= dialog_state
.tab_len
56 - ((col
+ obj
->hscroll
) % dialog_state
.tab_len
);
57 for (j
= 0; j
< tmpint
; j
++) {
58 if (col
>= 0 && col
< MAX_LEN
)
64 obj
->line
[col
] = (char) ch
;
70 } while (!feof(fp
) && (ch
!= '\n'));
74 obj
->line
[col
] = '\0';
80 * Print a new line of text.
83 print_line(MY_OBJ
* obj
, WINDOW
*win
, int row
, int width
)
86 char *line
= get_line(obj
);
88 (void) wmove(win
, row
, 0); /* move cursor to correct line */
89 (void) waddch(win
, ' ');
90 (void) waddnstr(win
, line
, MIN((int) strlen(line
), width
- 2));
94 /* Clear 'residue' of previous line */
95 for (i
= 0; i
< width
- x
; i
++)
96 (void) waddch(win
, ' ');
100 * Go back 'target' lines in text file. BUFSIZ has to be in 'size_t' range.
103 last_lines(MY_OBJ
* obj
, int target
)
105 FILE *fp
= obj
->obj
.input
;
108 if (fseek(fp
, 0L, SEEK_END
) == -1
109 || (fpos
= ftell(fp
)) < 0)
110 dlg_exiterr("Error moving file pointer in last_lines().");
121 char buf
[BUFSIZ
+ 1];
123 if (fpos
>= BUFSIZ
) {
124 size_to_read
= BUFSIZ
;
126 size_to_read
= (size_t) fpos
;
128 fpos
= fpos
- (long) size_to_read
;
129 if (fseek(fp
, fpos
, SEEK_SET
) == -1)
130 dlg_exiterr("Error moving file pointer in last_lines().");
131 size_as_read
= fread(buf
, sizeof(char), size_to_read
, fp
);
133 dlg_exiterr("Error reading file in last_lines().");
135 if (size_as_read
== 0) {
141 offset
+= (long) size_as_read
;
142 for (inx
= size_as_read
- 1; inx
!= 0; --inx
) {
143 if (buf
[inx
] == '\n') {
144 if (++count
> target
)
146 offset
= (long) (inx
+ 1);
150 if (count
> target
) {
152 } else if (fpos
== 0) {
158 if (fseek(fp
, fpos
+ offset
, SEEK_SET
) == -1)
159 dlg_exiterr("Error moving file pointer in last_lines().");
164 * Print a new page of text.
167 print_page(MY_OBJ
* obj
, int height
, int width
)
171 for (i
= 0; i
< height
; i
++) {
172 print_line(obj
, obj
->text
, i
, width
);
174 (void) wnoutrefresh(obj
->text
);
178 print_last_page(MY_OBJ
* obj
)
180 int high
= getmaxy(obj
->obj
.win
) - (2 * MARGIN
+ (obj
->obj
.bg_task
? 1 : 3));
181 int wide
= getmaxx(obj
->text
);
183 last_lines(obj
, high
);
184 print_page(obj
, high
, wide
);
188 repaint_text(MY_OBJ
* obj
)
190 FILE *fp
= obj
->obj
.input
;
193 getyx(obj
->obj
.win
, cur_y
, cur_x
);
194 obj
->old_hscroll
= obj
->hscroll
;
196 print_last_page(obj
);
197 obj
->last_pos
= ftell(fp
);
199 (void) wmove(obj
->obj
.win
, cur_y
, cur_x
); /* Restore cursor position */
200 wrefresh(obj
->obj
.win
);
204 handle_input(DIALOG_CALLBACK
* cb
)
206 MY_OBJ
*obj
= (MY_OBJ
*) cb
;
207 FILE *fp
= obj
->obj
.input
;
210 if (fstat(fileno(fp
), &sb
) == 0
211 && sb
.st_size
!= obj
->last_pos
) {
219 valid_callback(DIALOG_CALLBACK
* cb
)
223 for (p
= dialog_state
.getc_callbacks
; p
!= 0; p
= p
->next
) {
233 handle_my_getc(DIALOG_CALLBACK
* cb
, int ch
, int fkey
, int *result
)
235 MY_OBJ
*obj
= (MY_OBJ
*) cb
;
238 if (!valid_callback(cb
))
241 if (!fkey
&& dlg_char_to_button(ch
, obj
->buttons
) == 0) {
249 *result
= DLG_EXIT_OK
;
252 case DLGK_BEGIN
: /* Beginning of line */
255 case DLGK_GRID_LEFT
: /* Scroll left */
256 if (obj
->hscroll
> 0) {
260 case DLGK_GRID_RIGHT
: /* Scroll right */
261 if (obj
->hscroll
< MAX_LEN
)
265 if (is_DLGK_MOUSE(ch
)) {
266 *result
= dlg_ok_buttoncode(ch
- M_EVENT
);
267 if (*result
!= DLG_EXIT_ERROR
) {
277 if ((obj
->hscroll
!= obj
->old_hscroll
))
283 ch
= getc(cb
->input
);
284 (void) ungetc(ch
, cb
->input
);
291 *result
= DLG_EXIT_ESC
;
303 * Display text from a file in a dialog box, like in a "tail -f".
306 dialog_tailbox(const char *title
,
307 const char *filename
,
313 static DLG_KEYS_BINDING binding
[] = {
316 DLG_KEYS_DATA( DLGK_BEGIN
, '0' ),
317 DLG_KEYS_DATA( DLGK_BEGIN
, KEY_BEG
),
318 DLG_KEYS_DATA( DLGK_GRID_LEFT
, 'H' ),
319 DLG_KEYS_DATA( DLGK_GRID_LEFT
, 'h' ),
320 DLG_KEYS_DATA( DLGK_GRID_LEFT
, KEY_LEFT
),
321 DLG_KEYS_DATA( DLGK_GRID_RIGHT
, 'L' ),
322 DLG_KEYS_DATA( DLGK_GRID_RIGHT
, 'l' ),
323 DLG_KEYS_DATA( DLGK_GRID_RIGHT
, KEY_RIGHT
),
329 int old_height
= height
;
330 int old_width
= width
;
333 int x
, y
, result
= DLG_EXIT_UNKNOWN
, thigh
;
334 WINDOW
*dialog
, *text
;
335 const char **buttons
= 0;
340 DLG_TRACE(("# tailbox args:\n"));
341 DLG_TRACE2S("title", title
);
342 DLG_TRACE2S("filename", filename
);
343 DLG_TRACE2N("height", height
);
344 DLG_TRACE2N("width", width
);
345 DLG_TRACE2N("bg_task", bg_task
);
347 /* Open input file for reading */
348 if ((fd
= fopen(filename
, "rb")) == NULL
)
349 dlg_exiterr("Can't open input file in dialog_tailbox().");
354 dlg_auto_sizefile(title
, filename
, &height
, &width
, 2, min_width
);
355 dlg_print_size(height
, width
);
356 dlg_ctl_size(height
, width
);
358 x
= dlg_box_x_ordinate(width
);
359 y
= dlg_box_y_ordinate(height
);
360 thigh
= height
- ((2 * MARGIN
) + (bg_task
? 0 : 2));
362 dialog
= dlg_new_window(height
, width
, y
, x
);
364 dlg_mouse_setbase(x
, y
);
366 /* Create window for text region, used for scrolling text */
367 text
= dlg_sub_window(dialog
,
369 width
- (2 * MARGIN
),
373 dlg_draw_box2(dialog
, 0, 0, height
, width
, dialog_attr
, border_attr
, border2_attr
);
374 dlg_draw_bottom_box2(dialog
, border_attr
, border2_attr
, dialog_attr
);
375 dlg_draw_title(dialog
, title
);
376 dlg_draw_helpline(dialog
, FALSE
);
379 buttons
= dlg_exit_label();
380 dlg_button_layout(buttons
, &min_width
);
381 dlg_draw_buttons(dialog
, height
- (2 * MARGIN
), 0, buttons
, FALSE
,
385 (void) wmove(dialog
, thigh
, (MARGIN
+ 1));
386 (void) wnoutrefresh(dialog
);
388 obj
= dlg_calloc(MY_OBJ
, 1);
389 assert_ptr(obj
, "dialog_tailbox");
392 obj
->obj
.win
= dialog
;
393 obj
->obj
.handle_getc
= handle_my_getc
;
394 obj
->obj
.handle_input
= bg_task
? handle_input
: 0;
395 obj
->obj
.keep_bg
= bg_task
&& dialog_vars
.cant_kill
;
396 obj
->obj
.bg_task
= (bool) bg_task
;
398 obj
->buttons
= buttons
;
399 dlg_add_callback(&(obj
->obj
));
401 dlg_register_window(dialog
, "tailbox", binding
);
402 dlg_register_buttons(dialog
, "tailbox", buttons
);
404 /* Print last page of text */
405 dlg_attr_clear(text
, thigh
, getmaxx(text
), dialog_attr
);
408 dlg_trace_win(dialog
);
410 result
= DLG_EXIT_OK
;
414 ch
= dlg_mouse_wgetch(dialog
, &fkey
);
416 if (fkey
&& ch
== KEY_RESIZE
) {
417 dlg_will_resize(dialog
);
422 _dlg_resize_cleanup(dialog
);
423 dlg_button_layout(buttons
, &min_width
);
428 while (handle_my_getc(&(obj
->obj
), ch
, fkey
, &result
));
430 dlg_mouse_free_regions();