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