textbox.c 14 KB

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