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