linux/scripts/kconfig/lxdialog/textbox.c
<<
>>
Prefs
   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
  11static void back_lines(int n);
  12static void print_page(WINDOW *win, int height, int width, update_text_fn
  13                       update_text, void *data);
  14static void print_line(WINDOW *win, int row, int width);
  15static char *get_line(void);
  16static void print_position(WINDOW * win);
  17
  18static int hscroll;
  19static int begin_reached, end_reached, page_length;
  20static char *buf;
  21static char *page;
  22
  23/*
  24 * refresh window content
  25 */
  26static 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 */
  43int 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
  68do_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 */
 267static 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 */
 299static 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 */
 328static 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 */
 356static 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 */
 386static 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