textbox.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * textbox.c -- implements the text box
  4. *
  5. * ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
  6. * MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
  7. */
  8. #include "dialog.h"
  9. static void back_lines(int n);
  10. static void print_page(WINDOW *win, int height, int width, update_text_fn
  11. update_text, void *data);
  12. static void print_line(WINDOW *win, int row, int width);
  13. static char *get_line(void);
  14. static void print_position(WINDOW * win);
  15. static int hscroll;
  16. static int begin_reached, end_reached, page_length;
  17. static char *buf;
  18. static char *page;
  19. /*
  20. * refresh window content
  21. */
  22. static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
  23. int cur_y, int cur_x, update_text_fn update_text,
  24. void *data)
  25. {
  26. print_page(box, boxh, boxw, update_text, data);
  27. print_position(dialog);
  28. wmove(dialog, cur_y, cur_x); /* Restore cursor position */
  29. wrefresh(dialog);
  30. }
  31. /*
  32. * Display text from a file in a dialog box.
  33. *
  34. * keys is a null-terminated array
  35. * update_text() may not add or remove any '\n' or '\0' in tbuf
  36. */
  37. int dialog_textbox(const char *title, char *tbuf, int initial_height,
  38. int initial_width, int *keys, int *_vscroll, int *_hscroll,
  39. update_text_fn update_text, void *data)
  40. {
  41. int i, x, y, cur_x, cur_y, key = 0;
  42. int height, width, boxh, boxw;
  43. WINDOW *dialog, *box;
  44. bool done = false;
  45. begin_reached = 1;
  46. end_reached = 0;
  47. page_length = 0;
  48. hscroll = 0;
  49. buf = tbuf;
  50. page = buf; /* page is pointer to start of page to be displayed */
  51. if (_vscroll && *_vscroll) {
  52. begin_reached = 0;
  53. for (i = 0; i < *_vscroll; i++)
  54. get_line();
  55. }
  56. if (_hscroll)
  57. hscroll = *_hscroll;
  58. do_resize:
  59. getmaxyx(stdscr, height, width);
  60. if (height < TEXTBOX_HEIGTH_MIN || width < TEXTBOX_WIDTH_MIN)
  61. return -ERRDISPLAYTOOSMALL;
  62. if (initial_height != 0)
  63. height = initial_height;
  64. else
  65. if (height > 4)
  66. height -= 4;
  67. else
  68. height = 0;
  69. if (initial_width != 0)
  70. width = initial_width;
  71. else
  72. if (width > 5)
  73. width -= 5;
  74. else
  75. width = 0;
  76. /* center dialog box on screen */
  77. x = (getmaxx(stdscr) - width) / 2;
  78. y = (getmaxy(stdscr) - height) / 2;
  79. draw_shadow(stdscr, y, x, height, width);
  80. dialog = newwin(height, width, y, x);
  81. keypad(dialog, TRUE);
  82. /* Create window for box region, used for scrolling text */
  83. boxh = height - 4;
  84. boxw = width - 2;
  85. box = subwin(dialog, boxh, boxw, y + 1, x + 1);
  86. wattrset(box, dlg.dialog.atr);
  87. wbkgdset(box, dlg.dialog.atr & A_COLOR);
  88. keypad(box, TRUE);
  89. /* register the new window, along with its borders */
  90. draw_box(dialog, 0, 0, height, width,
  91. dlg.dialog.atr, dlg.border.atr);
  92. wattrset(dialog, dlg.border.atr);
  93. mvwaddch(dialog, height - 3, 0, ACS_LTEE);
  94. for (i = 0; i < width - 2; i++)
  95. waddch(dialog, ACS_HLINE);
  96. wattrset(dialog, dlg.dialog.atr);
  97. wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
  98. waddch(dialog, ACS_RTEE);
  99. print_title(dialog, title, width);
  100. print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
  101. wnoutrefresh(dialog);
  102. getyx(dialog, cur_y, cur_x); /* Save cursor position */
  103. /* Print first page of text */
  104. attr_clear(box, boxh, boxw, dlg.dialog.atr);
  105. refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
  106. data);
  107. while (!done) {
  108. key = wgetch(dialog);
  109. switch (key) {
  110. case 'E': /* Exit */
  111. case 'e':
  112. case 'X':
  113. case 'x':
  114. case 'q':
  115. case '\n':
  116. done = true;
  117. break;
  118. case 'g': /* First page */
  119. case KEY_HOME:
  120. if (!begin_reached) {
  121. begin_reached = 1;
  122. page = buf;
  123. refresh_text_box(dialog, box, boxh, boxw,
  124. cur_y, cur_x, update_text,
  125. data);
  126. }
  127. break;
  128. case 'G': /* Last page */
  129. case KEY_END:
  130. end_reached = 1;
  131. /* point to last char in buf */
  132. page = buf + strlen(buf);
  133. back_lines(boxh);
  134. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  135. cur_x, update_text, data);
  136. break;
  137. case 'K': /* Previous line */
  138. case 'k':
  139. case KEY_UP:
  140. if (begin_reached)
  141. break;
  142. back_lines(page_length + 1);
  143. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  144. cur_x, update_text, data);
  145. break;
  146. case 'B': /* Previous page */
  147. case 'b':
  148. case 'u':
  149. case KEY_PPAGE:
  150. if (begin_reached)
  151. break;
  152. back_lines(page_length + boxh);
  153. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  154. cur_x, update_text, data);
  155. break;
  156. case 'J': /* Next line */
  157. case 'j':
  158. case KEY_DOWN:
  159. if (end_reached)
  160. break;
  161. back_lines(page_length - 1);
  162. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  163. cur_x, update_text, data);
  164. break;
  165. case KEY_NPAGE: /* Next page */
  166. case ' ':
  167. case 'd':
  168. if (end_reached)
  169. break;
  170. begin_reached = 0;
  171. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  172. cur_x, update_text, data);
  173. break;
  174. case '0': /* Beginning of line */
  175. case 'H': /* Scroll left */
  176. case 'h':
  177. case KEY_LEFT:
  178. if (hscroll <= 0)
  179. break;
  180. if (key == '0')
  181. hscroll = 0;
  182. else
  183. hscroll--;
  184. /* Reprint current page to scroll horizontally */
  185. back_lines(page_length);
  186. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  187. cur_x, update_text, data);
  188. break;
  189. case 'L': /* Scroll right */
  190. case 'l':
  191. case KEY_RIGHT:
  192. if (hscroll >= MAX_LEN)
  193. break;
  194. hscroll++;
  195. /* Reprint current page to scroll horizontally */
  196. back_lines(page_length);
  197. refresh_text_box(dialog, box, boxh, boxw, cur_y,
  198. cur_x, update_text, data);
  199. break;
  200. case KEY_ESC:
  201. if (on_key_esc(dialog) == KEY_ESC)
  202. done = true;
  203. break;
  204. case KEY_RESIZE:
  205. back_lines(height);
  206. delwin(box);
  207. delwin(dialog);
  208. on_key_resize();
  209. goto do_resize;
  210. default:
  211. for (i = 0; keys[i]; i++) {
  212. if (key == keys[i]) {
  213. done = true;
  214. break;
  215. }
  216. }
  217. }
  218. }
  219. delwin(box);
  220. delwin(dialog);
  221. if (_vscroll) {
  222. const char *s;
  223. s = buf;
  224. *_vscroll = 0;
  225. back_lines(page_length);
  226. while (s < page && (s = strchr(s, '\n'))) {
  227. (*_vscroll)++;
  228. s++;
  229. }
  230. }
  231. if (_hscroll)
  232. *_hscroll = hscroll;
  233. return key;
  234. }
  235. /*
  236. * Go back 'n' lines in text. Called by dialog_textbox().
  237. * 'page' will be updated to point to the desired line in 'buf'.
  238. */
  239. static void back_lines(int n)
  240. {
  241. int i;
  242. begin_reached = 0;
  243. /* Go back 'n' lines */
  244. for (i = 0; i < n; i++) {
  245. if (*page == '\0') {
  246. if (end_reached) {
  247. end_reached = 0;
  248. continue;
  249. }
  250. }
  251. if (page == buf) {
  252. begin_reached = 1;
  253. return;
  254. }
  255. page--;
  256. do {
  257. if (page == buf) {
  258. begin_reached = 1;
  259. return;
  260. }
  261. page--;
  262. } while (*page != '\n');
  263. page++;
  264. }
  265. }
  266. /*
  267. * Print a new page of text.
  268. */
  269. static void print_page(WINDOW *win, int height, int width, update_text_fn
  270. update_text, void *data)
  271. {
  272. int i, passed_end = 0;
  273. if (update_text) {
  274. char *end;
  275. for (i = 0; i < height; i++)
  276. get_line();
  277. end = page;
  278. back_lines(height);
  279. update_text(buf, page - buf, end - buf, data);
  280. }
  281. page_length = 0;
  282. for (i = 0; i < height; i++) {
  283. print_line(win, i, width);
  284. if (!passed_end)
  285. page_length++;
  286. if (end_reached && !passed_end)
  287. passed_end = 1;
  288. }
  289. wnoutrefresh(win);
  290. }
  291. /*
  292. * Print a new line of text.
  293. */
  294. static void print_line(WINDOW * win, int row, int width)
  295. {
  296. char *line;
  297. line = get_line();
  298. line += MIN(strlen(line), hscroll); /* Scroll horizontally */
  299. wmove(win, row, 0); /* move cursor to correct line */
  300. waddch(win, ' ');
  301. waddnstr(win, line, MIN(strlen(line), width - 2));
  302. /* Clear 'residue' of previous line */
  303. #if OLD_NCURSES
  304. {
  305. int x = getcurx(win);
  306. int i;
  307. for (i = 0; i < width - x; i++)
  308. waddch(win, ' ');
  309. }
  310. #else
  311. wclrtoeol(win);
  312. #endif
  313. }
  314. /*
  315. * Return current line of text. Called by dialog_textbox() and print_line().
  316. * 'page' should point to start of current line before calling, and will be
  317. * updated to point to start of next line.
  318. */
  319. static char *get_line(void)
  320. {
  321. int i = 0;
  322. static char line[MAX_LEN + 1];
  323. end_reached = 0;
  324. while (*page != '\n') {
  325. if (*page == '\0') {
  326. end_reached = 1;
  327. break;
  328. } else if (i < MAX_LEN)
  329. line[i++] = *(page++);
  330. else {
  331. /* Truncate lines longer than MAX_LEN characters */
  332. if (i == MAX_LEN)
  333. line[i++] = '\0';
  334. page++;
  335. }
  336. }
  337. if (i <= MAX_LEN)
  338. line[i] = '\0';
  339. if (!end_reached)
  340. page++; /* move past '\n' */
  341. return line;
  342. }
  343. /*
  344. * Print current position
  345. */
  346. static void print_position(WINDOW * win)
  347. {
  348. int percent;
  349. wattrset(win, dlg.position_indicator.atr);
  350. wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
  351. percent = (page - buf) * 100 / strlen(buf);
  352. wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
  353. wprintw(win, "(%3d%%)", percent);
  354. }