1 /* 2 * textbox.c -- implements the text box 3 * 4 * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk) 5 * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com) 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22 #include "dialog.h" 23 24 static void back_lines(int n); 25 static void print_page(WINDOW * win, int height, int width); 26 static void print_line(WINDOW * win, int row, int width); 27 static char *get_line(void); 28 static void print_position(WINDOW * win, int height, int width); 29 30 static int hscroll, fd, file_size, bytes_read; 31 static int begin_reached = 1, end_reached, page_length; 32 static char *buf, *page; 33 34 /* 35 * Display text from a file in a dialog box. 36 */ 37 int dialog_textbox(const char *title, const char *file, int height, int width) 38 { 39 int i, x, y, cur_x, cur_y, fpos, key = 0; 40 int passed_end; 41 char search_term[MAX_LEN + 1]; 42 WINDOW *dialog, *text; 43 44 search_term[0] = '\0'; /* no search term entered yet */ 45 46 /* Open input file for reading */ 47 if ((fd = open(file, O_RDONLY)) == -1) { 48 endwin(); 49 fprintf(stderr, "\nCan't open input file in dialog_textbox().\n"); 50 exit(-1); 51 } 52 /* Get file size. Actually, 'file_size' is the real file size - 1, 53 since it's only the last byte offset from the beginning */ 54 if ((file_size = lseek(fd, 0, SEEK_END)) == -1) { 55 endwin(); 56 fprintf(stderr, "\nError getting file size in dialog_textbox().\n"); 57 exit(-1); 58 } 59 /* Restore file pointer to beginning of file after getting file size */ 60 if (lseek(fd, 0, SEEK_SET) == -1) { 61 endwin(); 62 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); 63 exit(-1); 64 } 65 /* Allocate space for read buffer */ 66 if ((buf = malloc(BUF_SIZE + 1)) == NULL) { 67 endwin(); 68 fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n"); 69 exit(-1); 70 } 71 if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) { 72 endwin(); 73 fprintf(stderr, "\nError reading file in dialog_textbox().\n"); 74 exit(-1); 75 } 76 buf[bytes_read] = '\0'; /* mark end of valid data */ 77 page = buf; /* page is pointer to start of page to be displayed */ 78 79 /* center dialog box on screen */ 80 x = (COLS - width) / 2; 81 y = (LINES - height) / 2; 82 83 draw_shadow(stdscr, y, x, height, width); 84 85 dialog = newwin(height, width, y, x); 86 keypad(dialog, TRUE); 87 88 /* Create window for text region, used for scrolling text */ 89 text = subwin(dialog, height - 4, width - 2, y + 1, x + 1); 90 wattrset(text, dialog_attr); 91 wbkgdset(text, dialog_attr & A_COLOR); 92 93 keypad(text, TRUE); 94 95 /* register the new window, along with its borders */ 96 draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr); 97 98 wattrset(dialog, border_attr); 99 mvwaddch(dialog, height - 3, 0, ACS_LTEE); 100 for (i = 0; i < width - 2; i++) 101 waddch(dialog, ACS_HLINE); 102 wattrset(dialog, dialog_attr); 103 wbkgdset(dialog, dialog_attr & A_COLOR); 104 waddch(dialog, ACS_RTEE); 105 106 print_title(dialog, title, width); 107 108 print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE); 109 wnoutrefresh(dialog); 110 getyx(dialog, cur_y, cur_x); /* Save cursor position */ 111 112 /* Print first page of text */ 113 attr_clear(text, height - 4, width - 2, dialog_attr); 114 print_page(text, height - 4, width - 2); 115 print_position(dialog, height, width); 116 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 117 wrefresh(dialog); 118 119 while ((key != ESC) && (key != '\n')) { 120 key = wgetch(dialog); 121 switch (key) { 122 case 'E': /* Exit */ 123 case 'e': 124 case 'X': 125 case 'x': 126 delwin(dialog); 127 free(buf); 128 close(fd); 129 return 0; 130 case 'g': /* First page */ 131 case KEY_HOME: 132 if (!begin_reached) { 133 begin_reached = 1; 134 /* First page not in buffer? */ 135 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 136 endwin(); 137 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); 138 exit(-1); 139 } 140 if (fpos > bytes_read) { /* Yes, we have to read it in */ 141 if (lseek(fd, 0, SEEK_SET) == -1) { 142 endwin(); 143 fprintf(stderr, "\nError moving file pointer in " 144 "dialog_textbox().\n"); 145 exit(-1); 146 } 147 if ((bytes_read = 148 read(fd, buf, BUF_SIZE)) == -1) { 149 endwin(); 150 fprintf(stderr, "\nError reading file in dialog_textbox().\n"); 151 exit(-1); 152 } 153 buf[bytes_read] = '\0'; 154 } 155 page = buf; 156 print_page(text, height - 4, width - 2); 157 print_position(dialog, height, width); 158 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 159 wrefresh(dialog); 160 } 161 break; 162 case 'G': /* Last page */ 163 case KEY_END: 164 165 end_reached = 1; 166 /* Last page not in buffer? */ 167 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 168 endwin(); 169 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); 170 exit(-1); 171 } 172 if (fpos < file_size) { /* Yes, we have to read it in */ 173 if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) { 174 endwin(); 175 fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n"); 176 exit(-1); 177 } 178 if ((bytes_read = 179 read(fd, buf, BUF_SIZE)) == -1) { 180 endwin(); 181 fprintf(stderr, "\nError reading file in dialog_textbox().\n"); 182 exit(-1); 183 } 184 buf[bytes_read] = '\0'; 185 } 186 page = buf + bytes_read; 187 back_lines(height - 4); 188 print_page(text, height - 4, width - 2); 189 print_position(dialog, height, width); 190 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 191 wrefresh(dialog); 192 break; 193 case 'K': /* Previous line */ 194 case 'k': 195 case KEY_UP: 196 if (!begin_reached) { 197 back_lines(page_length + 1); 198 199 /* We don't call print_page() here but use scrolling to ensure 200 faster screen update. However, 'end_reached' and 201 'page_length' should still be updated, and 'page' should 202 point to start of next page. This is done by calling 203 get_line() in the following 'for' loop. */ 204 scrollok(text, TRUE); 205 wscrl(text, -1); /* Scroll text region down one line */ 206 scrollok(text, FALSE); 207 page_length = 0; 208 passed_end = 0; 209 for (i = 0; i < height - 4; i++) { 210 if (!i) { 211 /* print first line of page */ 212 print_line(text, 0, width - 2); 213 wnoutrefresh(text); 214 } else 215 /* Called to update 'end_reached' and 'page' */ 216 get_line(); 217 if (!passed_end) 218 page_length++; 219 if (end_reached && !passed_end) 220 passed_end = 1; 221 } 222 223 print_position(dialog, height, width); 224 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 225 wrefresh(dialog); 226 } 227 break; 228 case 'B': /* Previous page */ 229 case 'b': 230 case KEY_PPAGE: 231 if (begin_reached) 232 break; 233 back_lines(page_length + height - 4); 234 print_page(text, height - 4, width - 2); 235 print_position(dialog, height, width); 236 wmove(dialog, cur_y, cur_x); 237 wrefresh(dialog); 238 break; 239 case 'J': /* Next line */ 240 case 'j': 241 case KEY_DOWN: 242 if (!end_reached) { 243 begin_reached = 0; 244 scrollok(text, TRUE); 245 scroll(text); /* Scroll text region up one line */ 246 scrollok(text, FALSE); 247 print_line(text, height - 5, width - 2); 248 wnoutrefresh(text); 249 print_position(dialog, height, width); 250 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 251 wrefresh(dialog); 252 } 253 break; 254 case KEY_NPAGE: /* Next page */ 255 case ' ': 256 if (end_reached) 257 break; 258 259 begin_reached = 0; 260 print_page(text, height - 4, width - 2); 261 print_position(dialog, height, width); 262 wmove(dialog, cur_y, cur_x); 263 wrefresh(dialog); 264 break; 265 case '0': /* Beginning of line */ 266 case 'H': /* Scroll left */ 267 case 'h': 268 case KEY_LEFT: 269 if (hscroll <= 0) 270 break; 271 272 if (key == '0') 273 hscroll = 0; 274 else 275 hscroll--; 276 /* Reprint current page to scroll horizontally */ 277 back_lines(page_length); 278 print_page(text, height - 4, width - 2); 279 wmove(dialog, cur_y, cur_x); 280 wrefresh(dialog); 281 break; 282 case 'L': /* Scroll right */ 283 case 'l': 284 case KEY_RIGHT: 285 if (hscroll >= MAX_LEN) 286 break; 287 hscroll++; 288 /* Reprint current page to scroll horizontally */ 289 back_lines(page_length); 290 print_page(text, height - 4, width - 2); 291 wmove(dialog, cur_y, cur_x); 292 wrefresh(dialog); 293 break; 294 case ESC: 295 break; 296 } 297 } 298 299 delwin(dialog); 300 free(buf); 301 close(fd); 302 return -1; /* ESC pressed */ 303 } 304 305 /* 306 * Go back 'n' lines in text file. Called by dialog_textbox(). 307 * 'page' will be updated to point to the desired line in 'buf'. 308 */ 309 static void back_lines(int n) 310 { 311 int i, fpos; 312 313 begin_reached = 0; 314 /* We have to distinguish between end_reached and !end_reached 315 since at end of file, the line is not ended by a '\n'. 316 The code inside 'if' basically does a '--page' to move one 317 character backward so as to skip '\n' of the previous line */ 318 if (!end_reached) { 319 /* Either beginning of buffer or beginning of file reached? */ 320 if (page == buf) { 321 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 322 endwin(); 323 fprintf(stderr, "\nError moving file pointer in " 324 "back_lines().\n"); 325 exit(-1); 326 } 327 if (fpos > bytes_read) { /* Not beginning of file yet */ 328 /* We've reached beginning of buffer, but not beginning of 329 file yet, so read previous part of file into buffer. 330 Note that we only move backward for BUF_SIZE/2 bytes, 331 but not BUF_SIZE bytes to avoid re-reading again in 332 print_page() later */ 333 /* Really possible to move backward BUF_SIZE/2 bytes? */ 334 if (fpos < BUF_SIZE / 2 + bytes_read) { 335 /* No, move less then */ 336 if (lseek(fd, 0, SEEK_SET) == -1) { 337 endwin(); 338 fprintf(stderr, "\nError moving file pointer in " 339 "back_lines().\n"); 340 exit(-1); 341 } 342 page = buf + fpos - bytes_read; 343 } else { /* Move backward BUF_SIZE/2 bytes */ 344 if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) { 345 endwin(); 346 fprintf(stderr, "\nError moving file pointer " 347 "in back_lines().\n"); 348 exit(-1); 349 } 350 page = buf + BUF_SIZE / 2; 351 } 352 if ((bytes_read = 353 read(fd, buf, BUF_SIZE)) == -1) { 354 endwin(); 355 fprintf(stderr, "\nError reading file in back_lines().\n"); 356 exit(-1); 357 } 358 buf[bytes_read] = '\0'; 359 } else { /* Beginning of file reached */ 360 begin_reached = 1; 361 return; 362 } 363 } 364 if (*(--page) != '\n') { /* '--page' here */ 365 /* Something's wrong... */ 366 endwin(); 367 fprintf(stderr, "\nInternal error in back_lines().\n"); 368 exit(-1); 369 } 370 } 371 /* Go back 'n' lines */ 372 for (i = 0; i < n; i++) 373 do { 374 if (page == buf) { 375 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 376 endwin(); 377 fprintf(stderr, "\nError moving file pointer in back_lines().\n"); 378 exit(-1); 379 } 380 if (fpos > bytes_read) { 381 /* Really possible to move backward BUF_SIZE/2 bytes? */ 382 if (fpos < BUF_SIZE / 2 + bytes_read) { 383 /* No, move less then */ 384 if (lseek(fd, 0, SEEK_SET) == -1) { 385 endwin(); 386 fprintf(stderr, "\nError moving file pointer " 387 "in back_lines().\n"); 388 exit(-1); 389 } 390 page = buf + fpos - bytes_read; 391 } else { /* Move backward BUF_SIZE/2 bytes */ 392 if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) { 393 endwin(); 394 fprintf(stderr, "\nError moving file pointer" 395 " in back_lines().\n"); 396 exit(-1); 397 } 398 page = buf + BUF_SIZE / 2; 399 } 400 if ((bytes_read = 401 read(fd, buf, BUF_SIZE)) == -1) { 402 endwin(); 403 fprintf(stderr, "\nError reading file in " 404 "back_lines().\n"); 405 exit(-1); 406 } 407 buf[bytes_read] = '\0'; 408 } else { /* Beginning of file reached */ 409 begin_reached = 1; 410 return; 411 } 412 } 413 } while (*(--page) != '\n'); 414 page++; 415 } 416 417 /* 418 * Print a new page of text. Called by dialog_textbox(). 419 */ 420 static void print_page(WINDOW * win, int height, int width) 421 { 422 int i, passed_end = 0; 423 424 page_length = 0; 425 for (i = 0; i < height; i++) { 426 print_line(win, i, width); 427 if (!passed_end) 428 page_length++; 429 if (end_reached && !passed_end) 430 passed_end = 1; 431 } 432 wnoutrefresh(win); 433 } 434 435 /* 436 * Print a new line of text. Called by dialog_textbox() and print_page(). 437 */ 438 static void print_line(WINDOW * win, int row, int width) 439 { 440 int y, x; 441 char *line; 442 443 line = get_line(); 444 line += MIN(strlen(line), hscroll); /* Scroll horizontally */ 445 wmove(win, row, 0); /* move cursor to correct line */ 446 waddch(win, ' '); 447 waddnstr(win, line, MIN(strlen(line), width - 2)); 448 449 getyx(win, y, x); 450 /* Clear 'residue' of previous line */ 451 #if OLD_NCURSES 452 { 453 int i; 454 for (i = 0; i < width - x; i++) 455 waddch(win, ' '); 456 } 457 #else 458 wclrtoeol(win); 459 #endif 460 } 461 462 /* 463 * Return current line of text. Called by dialog_textbox() and print_line(). 464 * 'page' should point to start of current line before calling, and will be 465 * updated to point to start of next line. 466 */ 467 static char *get_line(void) 468 { 469 int i = 0, fpos; 470 static char line[MAX_LEN + 1]; 471 472 end_reached = 0; 473 while (*page != '\n') { 474 if (*page == '\0') { 475 /* Either end of file or end of buffer reached */ 476 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 477 endwin(); 478 fprintf(stderr, "\nError moving file pointer in " 479 "get_line().\n"); 480 exit(-1); 481 } 482 if (fpos < file_size) { /* Not end of file yet */ 483 /* We've reached end of buffer, but not end of file yet, 484 so read next part of file into buffer */ 485 if ((bytes_read = 486 read(fd, buf, BUF_SIZE)) == -1) { 487 endwin(); 488 fprintf(stderr, "\nError reading file in get_line().\n"); 489 exit(-1); 490 } 491 buf[bytes_read] = '\0'; 492 page = buf; 493 } else { 494 if (!end_reached) 495 end_reached = 1; 496 break; 497 } 498 } else if (i < MAX_LEN) 499 line[i++] = *(page++); 500 else { 501 /* Truncate lines longer than MAX_LEN characters */ 502 if (i == MAX_LEN) 503 line[i++] = '\0'; 504 page++; 505 } 506 } 507 if (i <= MAX_LEN) 508 line[i] = '\0'; 509 if (!end_reached) 510 page++; /* move pass '\n' */ 511 512 return line; 513 } 514 515 /* 516 * Print current position 517 */ 518 static void print_position(WINDOW * win, int height, int width) 519 { 520 int fpos, percent; 521 522 if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) { 523 endwin(); 524 fprintf(stderr, "\nError moving file pointer in print_position().\n"); 525 exit(-1); 526 } 527 wattrset(win, position_indicator_attr); 528 wbkgdset(win, position_indicator_attr & A_COLOR); 529 percent = !file_size ? 530 100 : ((fpos - bytes_read + page - buf) * 100) / file_size; 531 wmove(win, height - 3, width - 9); 532 wprintw(win, "(%3d%%)", percent); 533 } 534