xref: /dragonfly/contrib/dialog/editbox.c (revision f9993810)
1 /*
2  *  $Id: editbox.c,v 1.85 2022/04/06 08:01:23 tom Exp $
3  *
4  *  editbox.c -- implements the edit box
5  *
6  *  Copyright 2007-2020,2022 Thomas E. Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *	Free Software Foundation, Inc.
20  *	51 Franklin St., Fifth Floor
21  *	Boston, MA 02110, USA.
22  */
23 
24 #include <dlg_internals.h>
25 #include <dlg_keys.h>
26 
27 #define sTEXT -1
28 
29 static void
30 fail_list(void)
31 {
32     dlg_exiterr("File too large");
33 }
34 
35 static void
36 grow_list(char ***list, int *have, int want)
37 {
38     if (want > *have) {
39 	size_t last = (size_t) *have;
40 	size_t need = (size_t) (want | 31) + 3;
41 	*have = (int) need;
42 	(*list) = dlg_realloc(char *, need, *list);
43 	if ((*list) == 0) {
44 	    fail_list();
45 	} else {
46 	    while (++last < need) {
47 		(*list)[last] = 0;
48 	    }
49 	}
50     }
51 }
52 
53 static void
54 load_list(const char *file, char ***list, int *rows)
55 {
56     char *blob = 0;
57     struct stat sb;
58     size_t size;
59 
60     *list = 0;
61     *rows = 0;
62 
63     if (stat(file, &sb) < 0 ||
64 	(sb.st_mode & S_IFMT) != S_IFREG)
65 	dlg_exiterr("Not a file: %s", file);
66 
67     size = (size_t) sb.st_size;
68     if ((blob = dlg_malloc(char, size + 2)) == 0) {
69 	fail_list();
70     } else {
71 	FILE *fp;
72 	unsigned n, pass;
73 
74 	blob[size] = '\0';
75 
76 	if ((fp = fopen(file, "r")) == 0)
77 	    dlg_exiterr("Cannot open: %s", file);
78 	size = fread(blob, sizeof(char), size, fp);
79 	fclose(fp);
80 
81 	/*
82 	 * If the file is not empty, ensure that it ends with a newline.
83 	 */
84 	if (size != 0 && blob[size - 1] != '\n') {
85 	    blob[++size - 1] = '\n';
86 	    blob[size] = '\0';
87 	}
88 
89 	for (pass = 0; pass < 2; ++pass) {
90 	    int first = TRUE;
91 	    unsigned need = 0;
92 
93 	    for (n = 0; n < size; ++n) {
94 		if (first && pass) {
95 		    (*list)[need] = blob + n;
96 		    first = FALSE;
97 		}
98 		if (blob[n] == '\n') {
99 		    first = TRUE;
100 		    ++need;
101 		    if (pass)
102 			blob[n] = '\0';
103 		}
104 	    }
105 	    if (pass) {
106 		if (need == 0) {
107 		    (*list)[0] = dlg_strclone("");
108 		    (*list)[1] = 0;
109 		} else {
110 		    for (n = 0; n < need; ++n) {
111 			(*list)[n] = dlg_strclone((*list)[n]);
112 		    }
113 		    (*list)[need] = 0;
114 		}
115 	    } else {
116 		grow_list(list, rows, (int) need + 1);
117 	    }
118 	}
119 	free(blob);
120     }
121 }
122 
123 static void
124 free_list(char ***list, int *rows)
125 {
126     if (*list != 0) {
127 	int n;
128 	for (n = 0; n < (*rows); ++n) {
129 	    if ((*list)[n] != 0)
130 		free((*list)[n]);
131 	}
132 	free(*list);
133 	*list = 0;
134     }
135     *rows = 0;
136 }
137 
138 /*
139  * Display a single row in the editing window:
140  * thisrow is the actual row number that's being displayed.
141  * show_row is the row number that's highlighted for edit.
142  * base_row is the first row number in the window
143  */
144 static bool
145 display_one(WINDOW *win,
146 	    char *text,
147 	    int thisrow,
148 	    int show_row,
149 	    int base_row,
150 	    int chr_offset)
151 {
152     bool result;
153 
154     if (text != 0) {
155 	dlg_show_string(win,
156 			text,
157 			chr_offset,
158 			((thisrow == show_row)
159 			 ? form_active_text_attr
160 			 : form_text_attr),
161 			thisrow - base_row,
162 			0,
163 			getmaxx(win),
164 			FALSE,
165 			FALSE);
166 	result = TRUE;
167     } else {
168 	result = FALSE;
169     }
170     return result;
171 }
172 
173 static void
174 display_all(WINDOW *win,
175 	    char **list,
176 	    int show_row,
177 	    int firstrow,
178 	    int lastrow,
179 	    int chr_offset)
180 {
181     int limit = getmaxy(win);
182     int row;
183 
184     dlg_attr_clear(win, getmaxy(win), getmaxx(win), dialog_attr);
185     if (lastrow - firstrow >= limit)
186 	lastrow = firstrow + limit;
187     for (row = firstrow; row < lastrow; ++row) {
188 	if (!display_one(win, list[row],
189 			 row, show_row, firstrow,
190 			 (row == show_row) ? chr_offset : 0))
191 	    break;
192     }
193 }
194 
195 static int
196 size_list(char **list)
197 {
198     int result = 0;
199 
200     if (list != 0) {
201 	while (*list++ != 0) {
202 	    ++result;
203 	}
204     }
205     return result;
206 }
207 
208 static bool
209 scroll_to(int pagesize, int rows, int *base_row, int *this_row, int target)
210 {
211     bool result = FALSE;
212 
213     if (target < *base_row) {
214 	if (target < 0) {
215 	    if (*base_row == 0 && *this_row == 0) {
216 		beep();
217 	    } else {
218 		*this_row = 0;
219 		*base_row = 0;
220 		result = TRUE;
221 	    }
222 	} else {
223 	    *this_row = target;
224 	    *base_row = target;
225 	    result = TRUE;
226 	}
227     } else if (target >= rows) {
228 	if (*this_row < rows - 1) {
229 	    *this_row = rows - 1;
230 	    *base_row = rows - 1;
231 	    result = TRUE;
232 	} else {
233 	    beep();
234 	}
235     } else if (target >= *base_row + pagesize) {
236 	*this_row = target;
237 	*base_row = target;
238 	result = TRUE;
239     } else {
240 	*this_row = target;
241 	result = FALSE;
242     }
243     if (pagesize < rows) {
244 	if (*base_row + pagesize >= rows) {
245 	    *base_row = rows - pagesize;
246 	}
247     } else {
248 	*base_row = 0;
249     }
250     return result;
251 }
252 
253 static int
254 col_to_chr_offset(const char *text, int col)
255 {
256     const int *cols = dlg_index_columns(text);
257     const int *indx = dlg_index_wchars(text);
258     bool found = FALSE;
259     int result = 0;
260     unsigned n;
261     unsigned len = (unsigned) dlg_count_wchars(text);
262 
263     for (n = 0; n < len; ++n) {
264 	if (cols[n] <= col && cols[n + 1] > col) {
265 	    result = indx[n];
266 	    found = TRUE;
267 	    break;
268 	}
269     }
270     if (!found && len && cols[len] == col) {
271 	result = indx[len];
272     }
273     return result;
274 }
275 
276 #define Scroll_To(target) scroll_to(pagesize, listsize, &base_row, &thisrow, target)
277 #define SCROLL_TO(target) show_all = Scroll_To(target)
278 
279 #define PREV_ROW (*list)[thisrow - 1]
280 #define THIS_ROW (*list)[thisrow]
281 #define NEXT_ROW (*list)[thisrow + 1]
282 
283 #define UPDATE_COL(input) col_offset = dlg_edit_offset(input, chr_offset, box_width)
284 
285 static int
286 widest_line(char **list)
287 {
288     int result = dlg_max_input(-1);
289 
290     if (list != 0) {
291 	char *value;
292 
293 	while ((value = *list++) != 0) {
294 	    int check = (int) strlen(value);
295 	    if (check > result)
296 		result = check;
297 	}
298     }
299     return result;
300 }
301 
302 #define NAVIGATE_BINDINGS \
303 	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_DOWN ), \
304 	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	KEY_RIGHT ), \
305 	DLG_KEYS_DATA( DLGK_GRID_LEFT,	KEY_LEFT ), \
306 	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_UP ), \
307 	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	TAB ), \
308 	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_BTAB ), \
309 	DLG_KEYS_DATA( DLGK_PAGE_FIRST,	KEY_HOME ), \
310 	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_END ), \
311 	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_LL ), \
312 	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ), \
313 	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ), \
314 	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ), \
315 	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) )
316 /*
317  * Display a dialog box for editing a copy of a file
318  */
319 int
320 dlg_editbox(const char *title,
321 	    char ***list,
322 	    int *rows,
323 	    int height,
324 	    int width)
325 {
326     /* *INDENT-OFF* */
327     static DLG_KEYS_BINDING binding[] = {
328 	HELPKEY_BINDINGS,
329 	ENTERKEY_BINDINGS,
330 	NAVIGATE_BINDINGS,
331 	TOGGLEKEY_BINDINGS,
332 	END_KEYS_BINDING
333     };
334     static DLG_KEYS_BINDING binding2[] = {
335 	INPUTSTR_BINDINGS,
336 	HELPKEY_BINDINGS,
337 	ENTERKEY_BINDINGS,
338 	NAVIGATE_BINDINGS,
339 	/* no TOGGLEKEY_BINDINGS, since that includes space... */
340 	END_KEYS_BINDING
341     };
342     /* *INDENT-ON* */
343 
344 #ifdef KEY_RESIZE
345     int old_height = height;
346     int old_width = width;
347 #endif
348     int x, y, box_y, box_x, box_height, box_width;
349     int show_buttons;
350     int thisrow, base_row, lastrow;
351     int goal_col = -1;
352     int col_offset = 0;
353     int chr_offset = 0;
354     int key, fkey, code;
355     int pagesize;
356     int listsize = size_list(*list);
357     int result = DLG_EXIT_UNKNOWN;
358     int state;
359     size_t max_len = (size_t) dlg_max_input(widest_line(*list));
360     char *buffer;
361     bool show_all, show_one;
362     bool first_trace = TRUE;
363     WINDOW *dialog;
364     WINDOW *editing;
365     DIALOG_VARS save_vars;
366     const char **buttons = dlg_ok_labels();
367     int mincols = (3 * COLS / 4);
368 
369     DLG_TRACE(("# editbox args:\n"));
370     DLG_TRACE2S("title", title);
371     /* FIXME dump the rows & list */
372     DLG_TRACE2N("height", height);
373     DLG_TRACE2N("width", width);
374 
375     dlg_save_vars(&save_vars);
376     dialog_vars.separate_output = TRUE;
377 
378     dlg_does_output();
379 
380     buffer = dlg_malloc(char, max_len + 1);
381     assert_ptr(buffer, "dlg_editbox");
382 
383     thisrow = base_row = lastrow = 0;
384 
385 #ifdef KEY_RESIZE
386   retry:
387 #endif
388     show_buttons = TRUE;
389     state = dialog_vars.default_button >= 0 ? dlg_default_button() : sTEXT;
390     fkey = 0;
391 
392     dlg_button_layout(buttons, &mincols);
393     dlg_auto_size(title, "", &height, &width, 3 * LINES / 4, mincols);
394     dlg_print_size(height, width);
395     dlg_ctl_size(height, width);
396 
397     x = dlg_box_x_ordinate(width);
398     y = dlg_box_y_ordinate(height);
399 
400     dialog = dlg_new_window(height, width, y, x);
401     dlg_register_window(dialog, "editbox", binding);
402     dlg_register_buttons(dialog, "editbox", buttons);
403 
404     dlg_mouse_setbase(x, y);
405 
406     dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
407     dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
408     dlg_draw_title(dialog, title);
409 
410     dlg_attrset(dialog, dialog_attr);
411 
412     /* Draw the editing field in a box */
413     box_y = MARGIN + 0;
414     box_x = MARGIN + 1;
415     box_width = width - 2 - (2 * MARGIN);
416     box_height = height - (4 * MARGIN);
417 
418     dlg_draw_box(dialog,
419 		 box_y,
420 		 box_x,
421 		 box_height,
422 		 box_width,
423 		 border_attr, border2_attr);
424     dlg_mouse_mkbigregion(box_y + MARGIN,
425 			  box_x + MARGIN,
426 			  box_height - (2 * MARGIN),
427 			  box_width - (2 * MARGIN),
428 			  KEY_MAX, 1, 1, 3);
429     editing = dlg_sub_window(dialog,
430 			     box_height - (2 * MARGIN),
431 			     box_width - (2 * MARGIN),
432 			     getbegy(dialog) + box_y + 1,
433 			     getbegx(dialog) + box_x + 1);
434     dlg_register_window(editing, "editbox2", binding2);
435 
436     show_all = TRUE;
437     show_one = FALSE;
438     pagesize = getmaxy(editing);
439 
440     while (result == DLG_EXIT_UNKNOWN) {
441 	bool was_mouse;
442 	char *input;
443 
444 	if (show_all) {
445 	    display_all(editing, *list, thisrow, base_row, listsize, chr_offset);
446 	    display_one(editing, THIS_ROW,
447 			thisrow, thisrow, base_row, chr_offset);
448 	    show_all = FALSE;
449 	    show_one = TRUE;
450 	} else {
451 	    if (thisrow != lastrow) {
452 		display_one(editing, (*list)[lastrow],
453 			    lastrow, thisrow, base_row, 0);
454 		show_one = TRUE;
455 	    }
456 	}
457 	if (show_one) {
458 	    display_one(editing, THIS_ROW,
459 			thisrow, thisrow, base_row, chr_offset);
460 	    getyx(editing, y, x);
461 	    dlg_draw_scrollbar(dialog,
462 			       base_row,
463 			       base_row,
464 			       base_row + pagesize,
465 			       listsize,
466 			       box_x,
467 			       box_x + getmaxx(editing),
468 			       box_y + 0,
469 			       box_y + getmaxy(editing) + 1,
470 			       border2_attr,
471 			       border_attr);
472 	    wmove(editing, y, x);
473 	    show_one = FALSE;
474 	}
475 	lastrow = thisrow;
476 	input = THIS_ROW;
477 
478 	/*
479 	 * The last field drawn determines where the cursor is shown:
480 	 */
481 	if (show_buttons) {
482 	    show_buttons = FALSE;
483 	    UPDATE_COL(input);
484 	    if (state != sTEXT) {
485 		display_one(editing, input, thisrow,
486 			    -1, base_row, 0);
487 		wrefresh(editing);
488 	    }
489 	    dlg_draw_buttons(dialog,
490 			     height - 2,
491 			     0,
492 			     buttons,
493 			     (state != sTEXT) ? state : 99,
494 			     FALSE,
495 			     width);
496 	    if (state == sTEXT) {
497 		display_one(editing, input, thisrow,
498 			    thisrow, base_row, chr_offset);
499 	    }
500 	}
501 
502 	if (first_trace) {
503 	    first_trace = FALSE;
504 	    dlg_trace_win(dialog);
505 	}
506 
507 	key = dlg_mouse_wgetch((state == sTEXT) ? editing : dialog, &fkey);
508 	if (key == ERR) {
509 	    result = DLG_EXIT_ERROR;
510 	    break;
511 	} else if (key == ESC) {
512 	    result = DLG_EXIT_ESC;
513 	    break;
514 	}
515 	if (state != sTEXT) {
516 	    if (dlg_result_key(key, fkey, &result)) {
517 		if (!dlg_button_key(result, &code, &key, &fkey))
518 		    break;
519 	    }
520 	}
521 
522 	was_mouse = (fkey && is_DLGK_MOUSE(key));
523 	if (was_mouse)
524 	    key -= M_EVENT;
525 
526 	/*
527 	 * Handle mouse clicks first, since we want to know if this is a
528 	 * button, or something that dlg_edit_string() should handle.
529 	 */
530 	if (fkey
531 	    && was_mouse
532 	    && (code = dlg_ok_buttoncode(key)) >= 0) {
533 	    result = code;
534 	    continue;
535 	}
536 
537 	if (was_mouse
538 	    && (key >= KEY_MAX)) {
539 	    int wide = getmaxx(editing);
540 	    int cell = key - KEY_MAX;
541 	    int check = (cell / wide) + base_row;
542 	    if (check < listsize) {
543 		thisrow = check;
544 		col_offset = (cell % wide);
545 		chr_offset = col_to_chr_offset(THIS_ROW, col_offset);
546 		show_one = TRUE;
547 		if (state != sTEXT) {
548 		    state = sTEXT;
549 		    show_buttons = TRUE;
550 		}
551 	    } else {
552 		beep();
553 	    }
554 	    continue;
555 	} else if (was_mouse && key >= KEY_MIN) {
556 	    key = dlg_lookup_key(dialog, key, &fkey);
557 	}
558 
559 	if (state == sTEXT) {	/* editing box selected */
560 	    int edit = 0;
561 
562 	    /*
563 	     * Intercept scrolling keys that dlg_edit_string() does not
564 	     * understand.
565 	     */
566 	    if (fkey) {
567 		bool moved = TRUE;
568 
569 		switch (key) {
570 		case DLGK_GRID_UP:
571 		    SCROLL_TO(thisrow - 1);
572 		    break;
573 		case DLGK_GRID_DOWN:
574 		    SCROLL_TO(thisrow + 1);
575 		    break;
576 		case DLGK_PAGE_FIRST:
577 		    SCROLL_TO(0);
578 		    break;
579 		case DLGK_PAGE_LAST:
580 		    SCROLL_TO(listsize);
581 		    break;
582 		case DLGK_PAGE_NEXT:
583 		    SCROLL_TO(base_row + pagesize);
584 		    break;
585 		case DLGK_PAGE_PREV:
586 		    if (thisrow > base_row) {
587 			SCROLL_TO(base_row);
588 		    } else {
589 			SCROLL_TO(base_row - pagesize);
590 		    }
591 		    break;
592 		case DLGK_DELETE_LEFT:
593 		    if (chr_offset == 0) {
594 			if (thisrow == 0) {
595 			    beep();
596 			} else {
597 			    size_t len = (strlen(THIS_ROW) +
598 					  strlen(PREV_ROW) + 1);
599 			    char *tmp = dlg_malloc(char, len);
600 
601 			    assert_ptr(tmp, "dlg_editbox");
602 
603 			    chr_offset = dlg_count_wchars(PREV_ROW);
604 			    UPDATE_COL(PREV_ROW);
605 			    goal_col = col_offset;
606 
607 			    sprintf(tmp, "%s%s", PREV_ROW, THIS_ROW);
608 			    if (len > max_len)
609 				tmp[max_len] = '\0';
610 
611 			    free(PREV_ROW);
612 			    PREV_ROW = tmp;
613 			    for (y = thisrow; y < listsize; ++y) {
614 				(*list)[y] = (*list)[y + 1];
615 			    }
616 			    --listsize;
617 			    --thisrow;
618 			    (void) Scroll_To(thisrow);
619 
620 			    show_all = TRUE;
621 			}
622 		    } else {
623 			/* dlg_edit_string() can handle this case */
624 			moved = FALSE;
625 		    }
626 		    break;
627 		default:
628 		    moved = FALSE;
629 		    break;
630 		}
631 		if (moved) {
632 		    if (thisrow != lastrow) {
633 			if (goal_col < 0)
634 			    goal_col = col_offset;
635 			chr_offset = col_to_chr_offset(THIS_ROW, goal_col);
636 		    } else {
637 			UPDATE_COL(THIS_ROW);
638 		    }
639 		    continue;
640 		}
641 	    }
642 	    strncpy(buffer, input, max_len - 1)[max_len - 1] = '\0';
643 	    if (chr_offset > (int) (max_len - 1))
644 		chr_offset = (int) (max_len - 1);
645 	    edit = dlg_edit_string(buffer, &chr_offset, key, fkey, FALSE);
646 
647 	    if (edit) {
648 		goal_col = UPDATE_COL(input);
649 		if (strcmp(input, buffer)) {
650 		    free(input);
651 		    THIS_ROW = dlg_strclone(buffer);
652 		    input = THIS_ROW;
653 		}
654 		display_one(editing, input, thisrow,
655 			    thisrow, base_row, chr_offset);
656 		continue;
657 	    }
658 	}
659 
660 	/* handle non-functionkeys */
661 	if (!fkey && (code = dlg_char_to_button(key, buttons)) >= 0) {
662 	    dlg_del_window(dialog);
663 	    result = dlg_ok_buttoncode(code);
664 	    continue;
665 	}
666 
667 	/* handle functionkeys */
668 	if (fkey) {
669 	    switch (key) {
670 	    case DLGK_GRID_UP:
671 	    case DLGK_GRID_LEFT:
672 	    case DLGK_FIELD_PREV:
673 		show_buttons = TRUE;
674 		state = dlg_prev_ok_buttonindex(state, sTEXT);
675 		break;
676 	    case DLGK_GRID_RIGHT:
677 	    case DLGK_GRID_DOWN:
678 	    case DLGK_FIELD_NEXT:
679 		show_buttons = TRUE;
680 		state = dlg_next_ok_buttonindex(state, sTEXT);
681 		break;
682 	    case DLGK_ENTER:
683 		if (state == sTEXT) {
684 		    const int *indx = dlg_index_wchars(THIS_ROW);
685 		    int split = indx[chr_offset];
686 		    char *tmp = dlg_strclone(THIS_ROW + split);
687 
688 		    assert_ptr(tmp, "dlg_editbox");
689 		    grow_list(list, rows, listsize + 1);
690 		    ++listsize;
691 		    for (y = listsize; y > thisrow; --y) {
692 			(*list)[y] = (*list)[y - 1];
693 		    }
694 		    THIS_ROW[split] = '\0';
695 		    ++thisrow;
696 		    chr_offset = 0;
697 		    col_offset = 0;
698 		    THIS_ROW = tmp;
699 		    (void) Scroll_To(thisrow);
700 		    show_all = TRUE;
701 		} else {
702 		    result = dlg_enter_buttoncode(state);
703 		}
704 		break;
705 	    case DLGK_LEAVE:
706 		if (state >= 0)
707 		    result = dlg_ok_buttoncode(state);
708 		break;
709 #ifdef KEY_RESIZE
710 	    case KEY_RESIZE:
711 		dlg_will_resize(dialog);
712 		/* reset data */
713 		height = old_height;
714 		width = old_width;
715 		/* repaint */
716 		dlg_del_window(editing);
717 		dlg_unregister_window(editing);
718 		_dlg_resize_cleanup(dialog);
719 		goto retry;
720 #endif
721 	    case DLGK_TOGGLE:
722 		if (state != sTEXT) {
723 		    result = dlg_ok_buttoncode(state);
724 		} else {
725 		    beep();
726 		}
727 		break;
728 	    default:
729 		beep();
730 		break;
731 	    }
732 	} else if (key > 0) {
733 	    beep();
734 	}
735     }
736 
737     dlg_unregister_window(editing);
738     dlg_del_window(editing);
739     dlg_del_window(dialog);
740     dlg_mouse_free_regions();
741 
742     /*
743      * The caller's copy of the (*list)[] array has been updated, but for
744      * consistency with the other widgets, we put the "real" result in
745      * the output buffer.
746      */
747     if (result == DLG_EXIT_OK) {
748 	int n;
749 	for (n = 0; n < listsize; ++n) {
750 	    dlg_add_result((*list)[n]);
751 	    dlg_add_separator();
752 	}
753 	dlg_add_last_key(-1);
754     }
755     free(buffer);
756     dlg_restore_vars(&save_vars);
757     return result;
758 }
759 
760 int
761 dialog_editbox(const char *title, const char *file, int height, int width)
762 {
763     int result;
764     char **list;
765     int rows;
766 
767     load_list(file, &list, &rows);
768     result = dlg_editbox(title, &list, &rows, height, width);
769     free_list(&list, &rows);
770     return result;
771 }
772