1 /* 2 * $Id: textbox.c,v 1.99 2011/01/16 22:20:34 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 ENTERKEY_BINDINGS, 456 END_KEYS_BINDING 457 }; 458 /* *INDENT-ON* */ 459 460 int old_x, old_y; 461 int box_x, box_y; 462 int box_height, box_width; 463 int offset = 0; 464 int key = 0; 465 int fkey = 0; 466 bool first = TRUE; 467 int result = DLG_EXIT_UNKNOWN; 468 const char *caption = _("Search"); 469 int len_caption = dlg_count_columns(caption); 470 const int *indx; 471 int limit; 472 WINDOW *widget; 473 474 getbegyx(dialog, old_y, old_x); 475 476 box_height = 1 + (2 * MARGIN); 477 box_width = len_caption + (2 * (MARGIN + 2)); 478 box_width = MAX(box_width, 30); 479 box_width = MIN(box_width, getmaxx(dialog) - 2 * MARGIN); 480 len_caption = MIN(len_caption, box_width - (2 * (MARGIN + 1))); 481 482 box_x = (width - box_width) / 2; 483 box_y = (height - box_height) / 2; 484 widget = dlg_new_modal_window(dialog, 485 box_height, box_width, 486 old_y + box_y, old_x + box_x); 487 keypad(widget, TRUE); 488 dlg_register_window(widget, "searchbox", binding); 489 490 dlg_draw_box(widget, 0, 0, box_height, box_width, 491 searchbox_attr, 492 searchbox_border_attr); 493 wattrset(widget, searchbox_title_attr); 494 (void) wmove(widget, 0, (box_width - len_caption) / 2); 495 496 indx = dlg_index_wchars(caption); 497 limit = dlg_limit_columns(caption, len_caption, 0); 498 (void) waddnstr(widget, caption + indx[0], indx[limit] - indx[0]); 499 500 box_y++; 501 box_x++; 502 box_width -= 2; 503 offset = dlg_count_columns(input); 504 505 while (result == DLG_EXIT_UNKNOWN) { 506 if (!first) { 507 key = dlg_getc(widget, &fkey); 508 if (fkey) { 509 switch (fkey) { 510 #ifdef KEY_RESIZE 511 case KEY_RESIZE: 512 result = DLG_EXIT_CANCEL; 513 continue; 514 #endif 515 case DLGK_ENTER: 516 result = DLG_EXIT_OK; 517 continue; 518 } 519 } else if (key == ESC) { 520 result = DLG_EXIT_ESC; 521 continue; 522 } else if (key == ERR) { 523 napms(50); 524 continue; 525 } 526 } 527 if (dlg_edit_string(input, &offset, key, fkey, first)) { 528 dlg_show_string(widget, input, offset, searchbox_attr, 529 1, 1, box_width, FALSE, first); 530 first = FALSE; 531 } 532 } 533 dlg_del_window(widget); 534 return result; 535 } 536 537 static bool 538 perform_search(MY_OBJ * obj, int height, int width, int key, char *search_term) 539 { 540 int dir; 541 long tempinx; 542 long fpos; 543 int result; 544 bool found; 545 bool temp, temp1; 546 bool moved = FALSE; 547 548 /* set search direction */ 549 dir = (key == '/' || key == 'n') ? 1 : 0; 550 if (dir ? !obj->end_reached : !obj->begin_reached) { 551 if (key == 'n' || key == 'N') { 552 if (search_term[0] == '\0') { /* No search term yet */ 553 (void) beep(); 554 return FALSE; 555 } 556 /* Get search term from user */ 557 } else if ((result = get_search_term(obj->text, search_term, 558 PAGE_LENGTH, 559 PAGE_WIDTH)) != DLG_EXIT_OK 560 || search_term[0] == '\0') { 561 #ifdef KEY_RESIZE 562 if (result == DLG_EXIT_CANCEL) { 563 ungetch(key); 564 ungetch(KEY_RESIZE); 565 /* FALLTHRU */ 566 } 567 #endif 568 /* ESC pressed, or no search term, reprint page to clear box */ 569 wattrset(obj->text, dialog_attr); 570 back_lines(obj, obj->page_length); 571 return TRUE; 572 } 573 /* Save variables for restoring in case search term can't be found */ 574 tempinx = obj->in_buf; 575 temp = obj->begin_reached; 576 temp1 = obj->end_reached; 577 fpos = ftell_obj(obj) - obj->fd_bytes_read; 578 /* update 'in_buf' to point to next (previous) line before 579 forward (backward) searching */ 580 back_lines(obj, (dir 581 ? obj->page_length - 1 582 : obj->page_length + 1)); 583 found = FALSE; 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 ENTERKEY_BINDINGS, 632 DLG_KEYS_DATA( DLGK_GRID_DOWN, 'J' ), 633 DLG_KEYS_DATA( DLGK_GRID_DOWN, 'j' ), 634 DLG_KEYS_DATA( DLGK_GRID_DOWN, KEY_DOWN ), 635 DLG_KEYS_DATA( DLGK_GRID_LEFT, 'H' ), 636 DLG_KEYS_DATA( DLGK_GRID_LEFT, 'h' ), 637 DLG_KEYS_DATA( DLGK_GRID_LEFT, KEY_LEFT ), 638 DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'L' ), 639 DLG_KEYS_DATA( DLGK_GRID_RIGHT, 'l' ), 640 DLG_KEYS_DATA( DLGK_GRID_RIGHT, KEY_RIGHT ), 641 DLG_KEYS_DATA( DLGK_GRID_UP, 'K' ), 642 DLG_KEYS_DATA( DLGK_GRID_UP, 'k' ), 643 DLG_KEYS_DATA( DLGK_GRID_UP, KEY_UP ), 644 DLG_KEYS_DATA( DLGK_PAGE_FIRST, 'g' ), 645 DLG_KEYS_DATA( DLGK_PAGE_FIRST, KEY_HOME ), 646 DLG_KEYS_DATA( DLGK_PAGE_LAST, 'G' ), 647 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_END ), 648 DLG_KEYS_DATA( DLGK_PAGE_LAST, KEY_LL ), 649 DLG_KEYS_DATA( DLGK_PAGE_NEXT, ' ' ), 650 DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), 651 DLG_KEYS_DATA( DLGK_PAGE_PREV, 'B' ), 652 DLG_KEYS_DATA( DLGK_PAGE_PREV, 'b' ), 653 DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ), 654 DLG_KEYS_DATA( DLGK_BEGIN, '0' ), 655 DLG_KEYS_DATA( DLGK_BEGIN, KEY_BEG ), 656 DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), 657 DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), 658 END_KEYS_BINDING 659 }; 660 /* *INDENT-ON* */ 661 662 #ifdef KEY_RESIZE 663 int old_height = height; 664 int old_width = width; 665 #endif 666 long fpos; 667 int x, y, cur_x, cur_y; 668 int key = 0, fkey; 669 int next = 0; 670 int i, code, passed_end; 671 char search_term[MAX_LEN + 1]; 672 MY_OBJ obj; 673 WINDOW *dialog; 674 bool moved; 675 int result = DLG_EXIT_UNKNOWN; 676 int button = dialog_vars.extra_button ? dlg_defaultno_button() : 0; 677 int min_width = 12; 678 679 search_term[0] = '\0'; /* no search term entered yet */ 680 681 memset(&obj, 0, sizeof(obj)); 682 683 obj.begin_reached = TRUE; 684 obj.buffer_first = TRUE; 685 obj.end_reached = FALSE; 686 obj.buttons = dlg_exit_label(); 687 688 /* Open input file for reading */ 689 if ((obj.fd = open(file, O_RDONLY)) == -1) 690 dlg_exiterr("Can't open input file %s", file); 691 692 /* Get file size. Actually, 'file_size' is the real file size - 1, 693 since it's only the last byte offset from the beginning */ 694 obj.file_size = lseek_obj(&obj, 0L, SEEK_END); 695 696 /* Restore file pointer to beginning of file after getting file size */ 697 lseek_obj(&obj, 0L, SEEK_SET); 698 699 read_high(&obj, BUF_SIZE); 700 701 dlg_button_layout(obj.buttons, &min_width); 702 703 #ifdef KEY_RESIZE 704 retry: 705 #endif 706 moved = TRUE; 707 708 dlg_auto_sizefile(title, file, &height, &width, 2, min_width); 709 dlg_print_size(height, width); 710 dlg_ctl_size(height, width); 711 712 x = dlg_box_x_ordinate(width); 713 y = dlg_box_y_ordinate(height); 714 715 dialog = dlg_new_window(height, width, y, x); 716 dlg_register_window(dialog, "textbox", binding); 717 dlg_register_buttons(dialog, "textbox", obj.buttons); 718 719 dlg_mouse_setbase(x, y); 720 721 /* Create window for text region, used for scrolling text */ 722 obj.text = dlg_sub_window(dialog, PAGE_LENGTH, PAGE_WIDTH, y + 1, x + 1); 723 724 /* register the new window, along with its borders */ 725 dlg_mouse_mkbigregion(0, 0, PAGE_LENGTH + 2, width, KEY_MAX, 1, 1, 1 /* lines */ ); 726 dlg_draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 727 dlg_draw_bottom_box(dialog); 728 dlg_draw_title(dialog, title); 729 730 dlg_draw_buttons(dialog, PAGE_LENGTH + 2, 0, obj.buttons, button, FALSE, width); 731 (void) wnoutrefresh(dialog); 732 getyx(dialog, cur_y, cur_x); /* Save cursor position */ 733 734 dlg_attr_clear(obj.text, PAGE_LENGTH, PAGE_WIDTH, dialog_attr); 735 736 while (result == DLG_EXIT_UNKNOWN) { 737 738 /* 739 * Update the screen according to whether we shifted up/down by a line 740 * or not. 741 */ 742 if (moved) { 743 if (next < 0) { 744 (void) scrollok(obj.text, TRUE); 745 (void) scroll(obj.text); /* Scroll text region up one line */ 746 (void) scrollok(obj.text, FALSE); 747 print_line(&obj, PAGE_LENGTH - 1, PAGE_WIDTH); 748 (void) wnoutrefresh(obj.text); 749 } else if (next > 0) { 750 /* 751 * We don't call print_page() here but use scrolling to ensure 752 * faster screen update. However, 'end_reached' and 753 * 'page_length' should still be updated, and 'in_buf' should 754 * point to start of next page. This is done by calling 755 * get_line() in the following 'for' loop. 756 */ 757 (void) scrollok(obj.text, TRUE); 758 (void) wscrl(obj.text, -1); /* Scroll text region down one line */ 759 (void) scrollok(obj.text, FALSE); 760 obj.page_length = 0; 761 passed_end = 0; 762 for (i = 0; i < PAGE_LENGTH; i++) { 763 if (!i) { 764 print_line(&obj, 0, PAGE_WIDTH); /* print first line of page */ 765 (void) wnoutrefresh(obj.text); 766 } else 767 (void) get_line(&obj); /* Called to update 'end_reached' and 'in_buf' */ 768 if (!passed_end) 769 obj.page_length++; 770 if (obj.end_reached && !passed_end) 771 passed_end = 1; 772 } 773 } else { 774 print_page(&obj, PAGE_LENGTH, PAGE_WIDTH); 775 } 776 print_position(&obj, dialog, height, width); 777 (void) wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 778 wrefresh(dialog); 779 } 780 moved = FALSE; /* assume we'll not move */ 781 next = 0; /* ...but not scroll by a line */ 782 783 key = dlg_mouse_wgetch(dialog, &fkey); 784 if (dlg_result_key(key, fkey, &result)) 785 break; 786 787 if (!fkey && (code = dlg_char_to_button(key, obj.buttons)) >= 0) { 788 result = dlg_ok_buttoncode(code); 789 break; 790 } 791 792 if (fkey) { 793 switch (key) { 794 default: 795 if (is_DLGK_MOUSE(key)) { 796 result = dlg_exit_buttoncode(key - M_EVENT); 797 if (result < 0) 798 result = DLG_EXIT_OK; 799 } else { 800 beep(); 801 } 802 break; 803 case DLGK_FIELD_NEXT: 804 button = dlg_next_button(obj.buttons, button); 805 if (button < 0) 806 button = 0; 807 dlg_draw_buttons(dialog, 808 height - 2, 0, 809 obj.buttons, button, 810 FALSE, width); 811 break; 812 case DLGK_FIELD_PREV: 813 button = dlg_prev_button(obj.buttons, button); 814 if (button < 0) 815 button = 0; 816 dlg_draw_buttons(dialog, 817 height - 2, 0, 818 obj.buttons, button, 819 FALSE, width); 820 break; 821 case DLGK_ENTER: 822 result = dlg_exit_buttoncode(button); 823 break; 824 case DLGK_PAGE_FIRST: 825 if (!obj.begin_reached) { 826 obj.begin_reached = 1; 827 /* First page not in buffer? */ 828 fpos = ftell_obj(&obj); 829 830 if (fpos > obj.fd_bytes_read) { 831 /* Yes, we have to read it in */ 832 lseek_obj(&obj, 0L, SEEK_SET); 833 834 read_high(&obj, BUF_SIZE); 835 } 836 obj.in_buf = 0; 837 moved = TRUE; 838 } 839 break; 840 case DLGK_PAGE_LAST: 841 obj.end_reached = TRUE; 842 /* Last page not in buffer? */ 843 fpos = ftell_obj(&obj); 844 845 if (fpos < obj.file_size) { 846 /* Yes, we have to read it in */ 847 lseek_obj(&obj, -BUF_SIZE, SEEK_END); 848 849 read_high(&obj, BUF_SIZE); 850 } 851 obj.in_buf = obj.bytes_read; 852 back_lines(&obj, (long) PAGE_LENGTH); 853 moved = TRUE; 854 break; 855 case DLGK_GRID_UP: /* Previous line */ 856 if (!obj.begin_reached) { 857 back_lines(&obj, obj.page_length + 1); 858 next = 1; 859 moved = TRUE; 860 } 861 break; 862 case DLGK_PAGE_PREV: /* Previous page */ 863 case DLGK_MOUSE(KEY_PPAGE): 864 if (!obj.begin_reached) { 865 back_lines(&obj, obj.page_length + PAGE_LENGTH); 866 moved = TRUE; 867 } 868 break; 869 case DLGK_GRID_DOWN: /* Next line */ 870 if (!obj.end_reached) { 871 obj.begin_reached = 0; 872 next = -1; 873 moved = TRUE; 874 } 875 break; 876 case DLGK_PAGE_NEXT: /* Next page */ 877 case DLGK_MOUSE(KEY_NPAGE): 878 if (!obj.end_reached) { 879 obj.begin_reached = 0; 880 moved = TRUE; 881 } 882 break; 883 case DLGK_BEGIN: /* Beginning of line */ 884 if (obj.hscroll > 0) { 885 obj.hscroll = 0; 886 /* Reprint current page to scroll horizontally */ 887 back_lines(&obj, obj.page_length); 888 moved = TRUE; 889 } 890 break; 891 case DLGK_GRID_LEFT: /* Scroll left */ 892 if (obj.hscroll > 0) { 893 obj.hscroll--; 894 /* Reprint current page to scroll horizontally */ 895 back_lines(&obj, obj.page_length); 896 moved = TRUE; 897 } 898 break; 899 case DLGK_GRID_RIGHT: /* Scroll right */ 900 if (obj.hscroll < MAX_LEN) { 901 obj.hscroll++; 902 /* Reprint current page to scroll horizontally */ 903 back_lines(&obj, obj.page_length); 904 moved = TRUE; 905 } 906 break; 907 #ifdef KEY_RESIZE 908 case KEY_RESIZE: 909 /* reset data */ 910 height = old_height; 911 width = old_width; 912 back_lines(&obj, obj.page_length); 913 moved = TRUE; 914 /* repaint */ 915 dlg_clear(); 916 dlg_del_window(dialog); 917 refresh(); 918 dlg_mouse_free_regions(); 919 goto retry; 920 #endif 921 } 922 } else { 923 switch (key) { 924 case '/': /* Forward search */ 925 case 'n': /* Repeat forward search */ 926 case '?': /* Backward search */ 927 case 'N': /* Repeat backward search */ 928 moved = perform_search(&obj, height, width, key, search_term); 929 fkey = FALSE; 930 break; 931 default: 932 beep(); 933 break; 934 } 935 } 936 } 937 938 dlg_del_window(dialog); 939 free(obj.buf); 940 (void) close(obj.fd); 941 dlg_mouse_free_regions(); 942 return result; 943 } 944