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 } 949