textbox.c 7.5 KB

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