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