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 const char *buf, *page; 14 static size_t start, end; 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) 102 { 103 int i, passed_end = 0; 104 105 page_length = 0; 106 for (i = 0; i < height; i++) { 107 print_line(win, i, width); 108 if (!passed_end) 109 page_length++; 110 if (end_reached && !passed_end) 111 passed_end = 1; 112 } 113 wnoutrefresh(win); 114 } 115 116 /* 117 * Print current position 118 */ 119 static void print_position(WINDOW *win) 120 { 121 int percent; 122 123 wattrset(win, dlg.position_indicator.atr); 124 wbkgdset(win, dlg.position_indicator.atr & A_COLOR); 125 percent = (page - buf) * 100 / strlen(buf); 126 wmove(win, getmaxy(win) - 3, getmaxx(win) - 9); 127 wprintw(win, "(%3d%%)", percent); 128 } 129 130 /* 131 * refresh window content 132 */ 133 static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, 134 int cur_y, int cur_x) 135 { 136 start = page - buf; 137 138 print_page(box, boxh, boxw); 139 print_position(dialog); 140 wmove(dialog, cur_y, cur_x); /* Restore cursor position */ 141 wrefresh(dialog); 142 143 end = page - buf; 144 } 145 146 /* 147 * Display text from a file in a dialog box. 148 */ 149 int dialog_textbox(const char *title, const char *tbuf, int initial_height, 150 int initial_width, int *_vscroll, int *_hscroll, 151 int (*extra_key_cb)(int, size_t, size_t, void *), void *data) 152 { 153 int i, x, y, cur_x, cur_y, key = 0; 154 int height, width, boxh, boxw; 155 WINDOW *dialog, *box; 156 bool done = false; 157 158 begin_reached = 1; 159 end_reached = 0; 160 page_length = 0; 161 hscroll = 0; 162 buf = tbuf; 163 page = buf; /* page is pointer to start of page to be displayed */ 164 165 if (_vscroll && *_vscroll) { 166 begin_reached = 0; 167 168 for (i = 0; i < *_vscroll; i++) 169 get_line(); 170 } 171 if (_hscroll) 172 hscroll = *_hscroll; 173 174 do_resize: 175 getmaxyx(stdscr, height, width); 176 if (height < TEXTBOX_HEIGHT_MIN || width < TEXTBOX_WIDTH_MIN) 177 return -ERRDISPLAYTOOSMALL; 178 if (initial_height != 0) 179 height = initial_height; 180 else 181 if (height > 4) 182 height -= 4; 183 else 184 height = 0; 185 if (initial_width != 0) 186 width = initial_width; 187 else 188 if (width > 5) 189 width -= 5; 190 else 191 width = 0; 192 193 /* center dialog box on screen */ 194 x = (getmaxx(stdscr) - width) / 2; 195 y = (getmaxy(stdscr) - height) / 2; 196 197 draw_shadow(stdscr, y, x, height, width); 198 199 dialog = newwin(height, width, y, x); 200 keypad(dialog, TRUE); 201 202 /* Create window for box region, used for scrolling text */ 203 boxh = height - 4; 204 boxw = width - 2; 205 box = subwin(dialog, boxh, boxw, y + 1, x + 1); 206 wattrset(box, dlg.dialog.atr); 207 wbkgdset(box, dlg.dialog.atr & A_COLOR); 208 209 keypad(box, TRUE); 210 211 /* register the new window, along with its borders */ 212 draw_box(dialog, 0, 0, height, width, 213 dlg.dialog.atr, dlg.border.atr); 214 215 wattrset(dialog, dlg.border.atr); 216 mvwaddch(dialog, height - 3, 0, ACS_LTEE); 217 for (i = 0; i < width - 2; i++) 218 waddch(dialog, ACS_HLINE); 219 wattrset(dialog, dlg.dialog.atr); 220 wbkgdset(dialog, dlg.dialog.atr & A_COLOR); 221 waddch(dialog, ACS_RTEE); 222 223 print_title(dialog, title, width); 224 225 print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE); 226 wnoutrefresh(dialog); 227 getyx(dialog, cur_y, cur_x); /* Save cursor position */ 228 229 /* Print first page of text */ 230 attr_clear(box, boxh, boxw, dlg.dialog.atr); 231 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 232 233 while (!done) { 234 key = wgetch(dialog); 235 switch (key) { 236 case 'E': /* Exit */ 237 case 'e': 238 case 'X': 239 case 'x': 240 case 'q': 241 case '\n': 242 done = true; 243 break; 244 case 'g': /* First page */ 245 case KEY_HOME: 246 if (!begin_reached) { 247 begin_reached = 1; 248 page = buf; 249 refresh_text_box(dialog, box, boxh, boxw, 250 cur_y, cur_x); 251 } 252 break; 253 case 'G': /* Last page */ 254 case KEY_END: 255 256 end_reached = 1; 257 /* point to last char in buf */ 258 page = buf + strlen(buf); 259 back_lines(boxh); 260 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 261 break; 262 case 'K': /* Previous line */ 263 case 'k': 264 case KEY_UP: 265 if (begin_reached) 266 break; 267 268 back_lines(page_length + 1); 269 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 270 break; 271 case 'B': /* Previous page */ 272 case 'b': 273 case 'u': 274 case KEY_PPAGE: 275 if (begin_reached) 276 break; 277 back_lines(page_length + boxh); 278 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 279 break; 280 case 'J': /* Next line */ 281 case 'j': 282 case KEY_DOWN: 283 if (end_reached) 284 break; 285 286 back_lines(page_length - 1); 287 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 288 break; 289 case KEY_NPAGE: /* Next page */ 290 case ' ': 291 case 'd': 292 if (end_reached) 293 break; 294 295 begin_reached = 0; 296 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 297 break; 298 case '0': /* Beginning of line */ 299 case 'H': /* Scroll left */ 300 case 'h': 301 case KEY_LEFT: 302 if (hscroll <= 0) 303 break; 304 305 if (key == '0') 306 hscroll = 0; 307 else 308 hscroll--; 309 /* Reprint current page to scroll horizontally */ 310 back_lines(page_length); 311 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 312 break; 313 case 'L': /* Scroll right */ 314 case 'l': 315 case KEY_RIGHT: 316 if (hscroll >= MAX_LEN) 317 break; 318 hscroll++; 319 /* Reprint current page to scroll horizontally */ 320 back_lines(page_length); 321 refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); 322 break; 323 case KEY_ESC: 324 if (on_key_esc(dialog) == KEY_ESC) 325 done = true; 326 break; 327 case KEY_RESIZE: 328 back_lines(height); 329 delwin(box); 330 delwin(dialog); 331 on_key_resize(); 332 goto do_resize; 333 default: 334 if (extra_key_cb && extra_key_cb(key, start, end, data)) { 335 done = true; 336 break; 337 } 338 } 339 } 340 delwin(box); 341 delwin(dialog); 342 if (_vscroll) { 343 const char *s; 344 345 s = buf; 346 *_vscroll = 0; 347 back_lines(page_length); 348 while (s < page && (s = strchr(s, '\n'))) { 349 (*_vscroll)++; 350 s++; 351 } 352 } 353 if (_hscroll) 354 *_hscroll = hscroll; 355 return key; 356 } 357