static int hscroll;
 static int begin_reached, end_reached, page_length;
-static char *buf;
-static char *page;
+static const char *buf, *page;
+static size_t start, end;
 
 /*
  * Go back 'n' lines in text. Called by dialog_textbox().
 /*
  * Print a new page of text.
  */
-static void print_page(WINDOW *win, int height, int width, update_text_fn
-                      update_text, void *data)
+static void print_page(WINDOW *win, int height, int width)
 {
        int i, passed_end = 0;
 
-       if (update_text) {
-               char *end;
-
-               for (i = 0; i < height; i++)
-                       get_line();
-               end = page;
-               back_lines(height);
-               update_text(buf, page - buf, end - buf, data);
-       }
-
        page_length = 0;
        for (i = 0; i < height; i++) {
                print_line(win, i, width);
  * refresh window content
  */
 static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
-                            int cur_y, int cur_x, update_text_fn update_text,
-                            void *data)
+                            int cur_y, int cur_x)
 {
-       print_page(box, boxh, boxw, update_text, data);
+       start = page - buf;
+
+       print_page(box, boxh, boxw);
        print_position(dialog);
        wmove(dialog, cur_y, cur_x);    /* Restore cursor position */
        wrefresh(dialog);
+
+       end = page - buf;
 }
 
 /*
  * Display text from a file in a dialog box.
  *
  * keys is a null-terminated array
- * update_text() may not add or remove any '\n' or '\0' in tbuf
  */
-int dialog_textbox(const char *title, char *tbuf, int initial_height,
-                  int initial_width, int *keys, int *_vscroll, int *_hscroll,
-                  update_text_fn update_text, void *data)
+int dialog_textbox(const char *title, const char *tbuf, int initial_height,
+                  int initial_width, int *_vscroll, int *_hscroll,
+                  int (*extra_key_cb)(int, size_t, size_t, void *), void *data)
 {
        int i, x, y, cur_x, cur_y, key = 0;
        int height, width, boxh, boxw;
 
        /* Print first page of text */
        attr_clear(box, boxh, boxw, dlg.dialog.atr);
-       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text,
-                        data);
+       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
 
        while (!done) {
                key = wgetch(dialog);
                                begin_reached = 1;
                                page = buf;
                                refresh_text_box(dialog, box, boxh, boxw,
-                                                cur_y, cur_x, update_text,
-                                                data);
+                                                cur_y, cur_x);
                        }
                        break;
                case 'G':       /* Last page */
                        /* point to last char in buf */
                        page = buf + strlen(buf);
                        back_lines(boxh);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case 'K':       /* Previous line */
                case 'k':
                                break;
 
                        back_lines(page_length + 1);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case 'B':       /* Previous page */
                case 'b':
                        if (begin_reached)
                                break;
                        back_lines(page_length + boxh);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case 'J':       /* Next line */
                case 'j':
                                break;
 
                        back_lines(page_length - 1);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case KEY_NPAGE: /* Next page */
                case ' ':
                                break;
 
                        begin_reached = 0;
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case '0':       /* Beginning of line */
                case 'H':       /* Scroll left */
                                hscroll--;
                        /* Reprint current page to scroll horizontally */
                        back_lines(page_length);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case 'L':       /* Scroll right */
                case 'l':
                        hscroll++;
                        /* Reprint current page to scroll horizontally */
                        back_lines(page_length);
-                       refresh_text_box(dialog, box, boxh, boxw, cur_y,
-                                        cur_x, update_text, data);
+                       refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
                        break;
                case KEY_ESC:
                        if (on_key_esc(dialog) == KEY_ESC)
                        on_key_resize();
                        goto do_resize;
                default:
-                       for (i = 0; keys[i]; i++) {
-                               if (key == keys[i]) {
-                                       done = true;
-                                       break;
-                               }
+                       if (extra_key_cb && extra_key_cb(key, start, end, data)) {
+                               done = true;
+                               break;
                        }
                }
        }
 
 static int show_all_options;
 static int save_and_exit;
 static int silent;
+static int jump_key_char;
 
 static void conf(struct menu *menu, struct menu *active_menu);
 
        set_dialog_subtitles(subtitles);
 }
 
-static int show_textbox_ext(const char *title, char *text, int r, int c, int
-                           *keys, int *vscroll, int *hscroll, update_text_fn
-                           update_text, void *data)
+static int show_textbox_ext(const char *title, const char *text, int r, int c,
+                           int *vscroll, int *hscroll,
+                           int (*extra_key_cb)(int, size_t, size_t, void *),
+                           void *data)
 {
        dialog_clear();
-       return dialog_textbox(title, text, r, c, keys, vscroll, hscroll,
-                             update_text, data);
+       return dialog_textbox(title, text, r, c, vscroll, hscroll,
+                             extra_key_cb, data);
 }
 
 static void show_textbox(const char *title, const char *text, int r, int c)
 {
-       show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL,
-                        NULL, NULL);
+       show_textbox_ext(title, text, r, c, NULL, NULL, NULL, NULL);
 }
 
 static void show_helptext(const char *title, const char *text)
 
 struct search_data {
        struct list_head *head;
-       struct menu **targets;
-       int *keys;
+       struct menu *target;
 };
 
-static void update_text(char *buf, size_t start, size_t end, void *_data)
+static int next_jump_key(int key)
+{
+       if (key < '1' || key > '9')
+               return '1';
+
+       key++;
+
+       if (key > '9')
+               key = '1';
+
+       return key;
+}
+
+static int handle_search_keys(int key, size_t start, size_t end, void *_data)
 {
        struct search_data *data = _data;
        struct jump_key *pos;
-       int k = 0;
 
-       list_for_each_entry(pos, data->head, entries) {
-               if (pos->offset >= start && pos->offset < end) {
-                       char header[4];
+       if (key < '1' || key > '9')
+               return 0;
 
-                       if (k < JUMP_NB) {
-                               int key = '0' + (pos->index % JUMP_NB) + 1;
+       list_for_each_entry(pos, data->head, entries) {
+               if (pos->offset < start)
+                       continue;
 
-                               sprintf(header, "(%c)", key);
-                               data->keys[k] = key;
-                               data->targets[k] = pos->target;
-                               k++;
-                       } else {
-                               sprintf(header, "   ");
-                       }
+               if (pos->offset >= end)
+                       break;
 
-                       memcpy(buf + pos->offset, header, sizeof(header) - 1);
+               if (key == '1' + (pos->index % JUMP_NB)) {
+                       data->target = pos->target;
+                       return 1;
                }
        }
-       data->keys[k] = 0;
+
+       return 0;
+}
+
+int get_jump_key_char(void)
+{
+       jump_key_char = next_jump_key(jump_key_char);
+
+       return jump_key_char;
 }
 
 static void search_conf(void)
        sym_arr = sym_re_search(dialog_input);
        do {
                LIST_HEAD(head);
-               struct menu *targets[JUMP_NB];
-               int keys[JUMP_NB + 1], i;
                struct search_data data = {
                        .head = &head,
-                       .targets = targets,
-                       .keys = keys,
                };
                struct jump_key *pos, *tmp;
 
+               jump_key_char = 0;
                res = get_relations_str(sym_arr, &head);
                set_subtitle();
                dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
-                                       keys, &vscroll, &hscroll, &update_text,
-                                       &data);
+                                       &vscroll, &hscroll,
+                                       handle_search_keys, &data);
                again = false;
-               for (i = 0; i < JUMP_NB && keys[i]; i++)
-                       if (dres == keys[i]) {
-                               conf(targets[i]->parent, targets[i]);
-                               again = true;
-                       }
+               if (dres >= '1' && dres <= '9') {
+                       assert(data.target != NULL);
+                       conf(data.target->parent, data.target);
+                       again = true;
+               }
                str_free(&res);
                list_for_each_entry_safe(pos, tmp, &head, entries)
                        free(pos);