2 * textbox.c -- implements the text box
4 * AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * $FreeBSD: src/gnu/lib/libdialog/textbox.c,v 1.18.6.2 2002/06/18 07:59:59 dougb Exp $
21 * $DragonFly: src/gnu/lib/libdialog/textbox.c,v 1.2 2003/06/17 04:25:43 dillon Exp $
25 #include "dialog.priv.h"
28 static void back_lines(int n
);
29 static void print_page(WINDOW
*win
, int height
, int width
);
30 static void print_line(WINDOW
*win
, int row
, int width
);
31 static unsigned char *get_line(void);
32 static int get_search_term(WINDOW
*win
, unsigned char *search_term
, int height
, int width
);
33 static void print_position(WINDOW
*win
, int height
, int width
);
36 static int hscroll
= 0, fd
, file_size
, bytes_read
, begin_reached
= 1,
37 end_reached
= 0, page_length
;
38 static unsigned char *buf
, *page
;
42 * Display text from a file in a dialog box.
44 int dialog_textbox(unsigned char *title
, unsigned char *file
, int height
, int width
)
46 int i
, x
, y
, cur_x
, cur_y
, fpos
, key
= 0, dir
, temp
, temp1
;
50 unsigned char search_term
[MAX_LEN
+1], *tempptr
, *found
;
51 WINDOW
*dialog
, *text
;
53 if (height
< 0 || width
< 0) {
54 fprintf(stderr
, "\nAutosizing is impossible in dialog_textbox().\n");
58 search_term
[0] = '\0'; /* no search term entered yet */
60 /* Open input file for reading */
61 if ((fd
= open(file
, O_RDONLY
)) == -1) {
62 fprintf(stderr
, "\nCan't open input file <%s>in dialog_textbox().\n", file
);
65 /* Get file size. Actually, 'file_size' is the real file size - 1,
66 since it's only the last byte offset from the beginning */
67 if ((file_size
= lseek(fd
, 0, SEEK_END
)) == -1) {
68 fprintf(stderr
, "\nError getting file size in dialog_textbox().\n");
71 /* Restore file pointer to beginning of file after getting file size */
72 if (lseek(fd
, 0, SEEK_SET
) == -1) {
73 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
76 /* Allocate space for read buffer */
77 if ((buf
= malloc(BUF_SIZE
+1)) == NULL
) {
79 fprintf(stderr
, "\nCan't allocate memory in dialog_textbox().\n");
82 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
83 fprintf(stderr
, "\nError reading file in dialog_textbox().\n");
86 buf
[bytes_read
] = '\0'; /* mark end of valid data */
87 page
= buf
; /* page is pointer to start of page to be displayed */
93 /* center dialog box on screen */
94 x
= DialogX
? DialogX
: (COLS
- width
)/2;
95 y
= DialogY
? DialogY
: (LINES
- height
)/2;
99 draw_shadow(stdscr
, y
, x
, height
, width
);
101 dialog
= newwin(height
, width
, y
, x
);
102 if (dialog
== NULL
) {
104 fprintf(stderr
, "\nnewwin(%d,%d,%d,%d) failed, maybe wrong dims\n", height
,width
,y
,x
);
107 keypad(dialog
, TRUE
);
109 /* Create window for text region, used for scrolling text */
110 /* text = newwin(height-4, width-2, y+1, x+1); */
111 text
= subwin(dialog
, height
-4, width
-2, y
+1, x
+1);
114 fprintf(stderr
, "\nsubwin(dialog,%d,%d,%d,%d) failed, maybe wrong dims\n", height
-4,width
-2,y
+1,x
+1);
119 draw_box(dialog
, 0, 0, height
, width
, dialog_attr
, border_attr
);
121 wattrset(dialog
, border_attr
);
122 wmove(dialog
, height
-3, 0);
123 waddch(dialog
, ACS_LTEE
);
124 for (i
= 0; i
< width
-2; i
++)
125 waddch(dialog
, ACS_HLINE
);
126 wattrset(dialog
, dialog_attr
);
127 waddch(dialog
, ACS_RTEE
);
128 wmove(dialog
, height
-2, 1);
129 for (i
= 0; i
< width
-2; i
++)
133 wattrset(dialog
, title_attr
);
134 wmove(dialog
, 0, (width
- strlen(title
))/2 - 1);
136 waddstr(dialog
, title
);
139 display_helpline(dialog
, height
-1, width
);
141 print_button(dialog
, " OK ", height
-2, width
/2-6, TRUE
);
142 wnoutrefresh(dialog
);
143 getyx(dialog
, cur_y
, cur_x
); /* Save cursor position */
145 /* Print first page of text */
146 attr_clear(text
, height
-4, width
-2, dialog_attr
);
147 print_page(text
, height
-4, width
-2);
148 print_position(dialog
, height
, width
);
149 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
152 while ((key
!= ESC
) && (key
!= '\n') && (key
!= '\r') && (key
!= ' ')) {
153 key
= wgetch(dialog
);
161 case 'g': /* First page */
163 if (!begin_reached
) {
165 /* First page not in buffer? */
166 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
168 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
171 if (fpos
> bytes_read
) { /* Yes, we have to read it in */
172 if (lseek(fd
, 0, SEEK_SET
) == -1) {
174 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
177 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
179 fprintf(stderr
, "\nError reading file in dialog_textbox().\n");
182 buf
[bytes_read
] = '\0';
185 print_page(text
, height
-4, width
-2);
186 print_position(dialog
, height
, width
);
187 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
191 case 'G': /* Last page */
196 /* Last page not in buffer? */
197 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
199 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
202 if (fpos
< file_size
) { /* Yes, we have to read it in */
203 if (lseek(fd
, -BUF_SIZE
, SEEK_END
) == -1) {
205 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
208 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
210 fprintf(stderr
, "\nError reading file in dialog_textbox().\n");
213 buf
[bytes_read
] = '\0';
215 page
= buf
+ bytes_read
;
216 back_lines(height
-4);
217 print_page(text
, height
-4, width
-2);
218 print_position(dialog
, height
, width
);
219 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
222 case 'K': /* Previous line */
224 case '\020': /* ^P */
226 if (!begin_reached
) {
227 back_lines(page_length
+1);
229 /* We don't call print_page() here but use scrolling to ensure
230 faster screen update. However, 'end_reached' and 'page_length'
231 should still be updated, and 'page' should point to start of
232 next page. This is done by calling get_line() in the following
234 scrollok(text
, TRUE
);
235 wscrl(text
, -1); /* Scroll text region down one line */
236 scrollok(text
, FALSE
);
239 for (i
= 0; i
< height
-4; i
++) {
241 print_line(text
, 0, width
-2); /* print first line of page */
245 get_line(); /* Called to update 'end_reached' and 'page' */
248 if (end_reached
&& !passed_end
)
252 print_page(text
, height
-4, width
-2);
254 print_position(dialog
, height
, width
);
255 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
259 case 'B': /* Previous page */
262 if (!begin_reached
) {
263 back_lines(page_length
+ height
-4);
264 print_page(text
, height
-4, width
-2);
265 print_position(dialog
, height
, width
);
266 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
270 case 'J': /* Next line */
272 case '\016': /* ^N */
276 scrollok(text
, TRUE
);
277 scroll(text
); /* Scroll text region up one line */
278 scrollok(text
, FALSE
);
279 print_line(text
, height
-5, width
-2);
281 wmove(text
, height
-5, 0);
283 wmove(text
, height
-5, width
-3);
287 print_position(dialog
, height
, width
);
288 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
292 case 'F': /* Next page */
297 print_page(text
, height
-4, width
-2);
298 print_position(dialog
, height
, width
);
299 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
303 case '0': /* Beginning of line */
304 case 'H': /* Scroll left */
312 /* Reprint current page to scroll horizontally */
313 back_lines(page_length
);
314 print_page(text
, height
-4, width
-2);
315 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
319 case 'L': /* Scroll right */
322 if (hscroll
< MAX_LEN
) {
324 /* Reprint current page to scroll horizontally */
325 back_lines(page_length
);
326 print_page(text
, height
-4, width
-2);
327 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
331 case '/': /* Forward search */
332 case 'n': /* Repeat forward search */
333 case '?': /* Backward search */
334 case 'N': /* Repeat backward search */
335 /* set search direction */
336 dir
= (key
== '/' || key
== 'n') ? 1 : 0;
337 if (dir
? !end_reached
: !begin_reached
) {
338 if (key
== 'n' || key
== 'N') {
339 if (search_term
[0] == '\0') { /* No search term yet */
340 fprintf(stderr
, "\a"); /* beep */
344 else /* Get search term from user */
345 if (get_search_term(text
, search_term
, height
-4, width
-2) == -1) {
346 /* ESC pressed in get_search_term(). Reprint page to clear box */
347 wattrset(text
, dialog_attr
);
348 back_lines(page_length
);
349 print_page(text
, height
-4, width
-2);
350 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
354 /* Save variables for restoring in case search term can't be found */
356 temp
= begin_reached
;
358 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
360 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
364 /* update 'page' to point to next (previous) line before
365 forward (backward) searching */
366 back_lines(dir
? page_length
-1 : page_length
+1);
368 if (dir
) /* Forward search */
369 while((found
= strstr(get_line(), search_term
)) == NULL
) {
373 else /* Backward search */
374 while((found
= strstr(get_line(), search_term
)) == NULL
) {
379 if (found
== NULL
) { /* not found */
380 fprintf(stderr
, "\a"); /* beep */
381 /* Restore program state to that before searching */
382 if (lseek(fd
, fpos
, SEEK_SET
) == -1) {
384 fprintf(stderr
, "\nError moving file pointer in dialog_textbox().\n");
387 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
389 fprintf(stderr
, "\nError reading file in dialog_textbox().\n");
392 buf
[bytes_read
] = '\0';
394 begin_reached
= temp
;
396 /* move 'page' to point to start of current page in order to
397 re-print current page. Note that 'page' always points to
398 start of next page, so this is necessary */
399 back_lines(page_length
);
401 else /* Search term found */
404 wattrset(text
, dialog_attr
);
405 print_page(text
, height
-4, width
-2);
407 print_position(dialog
, height
, width
);
408 wmove(dialog
, cur_y
, cur_x
); /* Restore cursor position */
411 else /* no need to find */
412 fprintf(stderr
, "\a"); /* beep */
425 return (key
== ESC
? -1 : 0);
427 /* End of dialog_textbox() */
431 * Go back 'n' lines in text file. Called by dialog_textbox().
432 * 'page' will be updated to point to the desired line in 'buf'.
434 static void back_lines(int n
)
439 /* We have to distinguish between end_reached and !end_reached since at end
440 of file, the line is not ended by a '\n'. The code inside 'if' basically
441 does a '--page' to move one character backward so as to skip '\n' of the
444 /* Either beginning of buffer or beginning of file reached? */
446 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
448 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
451 if (fpos
> bytes_read
) { /* Not beginning of file yet */
452 /* We've reached beginning of buffer, but not beginning of file yet,
453 so read previous part of file into buffer. Note that we only
454 move backward for BUF_SIZE/2 bytes, but not BUF_SIZE bytes to
455 avoid re-reading again in print_page() later */
456 /* Really possible to move backward BUF_SIZE/2 bytes? */
457 if (fpos
< BUF_SIZE
/2 + bytes_read
) {
458 /* No, move less then */
459 if (lseek(fd
, 0, SEEK_SET
) == -1) {
461 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
464 page
= buf
+ fpos
- bytes_read
;
466 else { /* Move backward BUF_SIZE/2 bytes */
467 if (lseek(fd
, -(BUF_SIZE
/2 + bytes_read
), SEEK_CUR
) == -1) {
469 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
472 page
= buf
+ BUF_SIZE
/2;
474 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
476 fprintf(stderr
, "\nError reading file in back_lines().\n");
479 buf
[bytes_read
] = '\0';
481 else { /* Beginning of file reached */
486 if (*(--page
) != '\n') { /* '--page' here */
487 /* Something's wrong... */
489 fprintf(stderr
, "\nInternal error in back_lines().\n");
494 /* Go back 'n' lines */
495 for (i
= 0; i
< n
; i
++)
498 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
500 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
503 if (fpos
> bytes_read
) {
504 /* Really possible to move backward BUF_SIZE/2 bytes? */
505 if (fpos
< BUF_SIZE
/2 + bytes_read
) {
506 /* No, move less then */
507 if (lseek(fd
, 0, SEEK_SET
) == -1) {
509 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
512 page
= buf
+ fpos
- bytes_read
;
514 else { /* Move backward BUF_SIZE/2 bytes */
515 if (lseek(fd
, -(BUF_SIZE
/2 + bytes_read
), SEEK_CUR
) == -1) {
517 fprintf(stderr
, "\nError moving file pointer in back_lines().\n");
520 page
= buf
+ BUF_SIZE
/2;
522 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
524 fprintf(stderr
, "\nError reading file in back_lines().\n");
527 buf
[bytes_read
] = '\0';
529 else { /* Beginning of file reached */
534 } while (*(--page
) != '\n');
537 /* End of back_lines() */
541 * Print a new page of text. Called by dialog_textbox().
543 static void print_page(WINDOW
*win
, int height
, int width
)
545 int i
, passed_end
= 0;
548 for (i
= 0; i
< height
; i
++) {
549 print_line(win
, i
, width
);
552 if (end_reached
&& !passed_end
)
557 /* End of print_page() */
561 * Print a new line of text. Called by dialog_textbox() and print_page().
563 static void print_line(WINDOW
*win
, int row
, int width
)
569 line
+= MIN(strlen(line
),hscroll
); /* Scroll horizontally */
570 wmove(win
, row
, 0); /* move cursor to correct line */
573 waddnstr(win
, line
, MIN(strlen(line
),width
-2));
575 line
[MIN(strlen(line
),width
-2)] = '\0';
580 /* Clear 'residue' of previous line */
581 for (i
= 0; i
< width
-x
; i
++)
584 /* End of print_line() */
588 * Return current line of text. Called by dialog_textbox() and print_line().
589 * 'page' should point to start of current line before calling, and will be
590 * updated to point to start of next line.
592 static unsigned char *get_line(void)
595 static unsigned char line
[MAX_LEN
+1];
598 while (*page
!= '\n') {
599 if (*page
== '\0') { /* Either end of file or end of buffer reached */
600 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
602 fprintf(stderr
, "\nError moving file pointer in get_line().\n");
605 if (fpos
< file_size
) { /* Not end of file yet */
606 /* We've reached end of buffer, but not end of file yet, so read next
607 part of file into buffer */
608 if ((bytes_read
= read(fd
, buf
, BUF_SIZE
)) == -1) {
610 fprintf(stderr
, "\nError reading file in get_line().\n");
613 buf
[bytes_read
] = '\0';
624 line
[i
++] = *(page
++);
626 if (i
== MAX_LEN
) /* Truncate lines longer than MAX_LEN characters */
634 page
++; /* move pass '\n' */
638 /* End of get_line() */
642 * Display a dialog box and get the search term from user
644 static int get_search_term(WINDOW
*win
, unsigned char *search_term
, int height
, int width
)
646 int x
, y
, key
= 0, first
,
647 box_height
= 3, box_width
= 30;
649 x
= (width
- box_width
)/2;
650 y
= (height
- box_height
)/2;
653 draw_shadow(win
, y
, x
, box_height
, box_width
);
655 draw_box(win
, y
, x
, box_height
, box_width
, dialog_attr
, searchbox_border_attr
);
656 wattrset(win
, searchbox_title_attr
);
657 wmove(win
, y
, x
+box_width
/2-4);
658 waddstr(win
, " Search ");
659 wattrset(win
, dialog_attr
);
661 search_term
[0] = '\0';
665 key
= line_edit(win
, y
+1, x
+1, -1, box_width
-2, searchbox_attr
, first
, search_term
, 0);
669 if (search_term
[0] != '\0')
677 return -1; /* ESC pressed */
679 /* End of get_search_term() */
683 * Print current position
685 static void print_position(WINDOW
*win
, int height
, int width
)
689 if ((fpos
= lseek(fd
, 0, SEEK_CUR
)) == -1) {
691 fprintf(stderr
, "\nError moving file pointer in print_position().\n");
694 wattrset(win
, position_indicator_attr
);
695 percent
= !file_size
? 100 : ((fpos
-bytes_read
+page
-buf
)*100)/file_size
;
696 wmove(win
, height
-3, width
-9);
697 wprintw(win
, "(%3d%%)", percent
);
699 /* End of print_position() */