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