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