1217309Snwhitehorn/*
2251843Sbapt *  $Id: editbox.c,v 1.62 2013/03/17 15:03:41 tom Exp $
3217309Snwhitehorn *
4217309Snwhitehorn *  editbox.c -- implements the edit box
5217309Snwhitehorn *
6251843Sbapt *  Copyright 2007-2012,2013 Thomas E. Dickey
7217309Snwhitehorn *
8217309Snwhitehorn *  This program is free software; you can redistribute it and/or modify
9217309Snwhitehorn *  it under the terms of the GNU Lesser General Public License, version 2.1
10217309Snwhitehorn *
11217309Snwhitehorn *  This program is distributed in the hope that it will be useful, but
12217309Snwhitehorn *  WITHOUT ANY WARRANTY; without even the implied warranty of
13217309Snwhitehorn *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14217309Snwhitehorn *  Lesser General Public License for more details.
15217309Snwhitehorn *
16217309Snwhitehorn *  You should have received a copy of the GNU Lesser General Public
17217309Snwhitehorn *  License along with this program; if not, write to
18217309Snwhitehorn *	Free Software Foundation, Inc.
19217309Snwhitehorn *	51 Franklin St., Fifth Floor
20217309Snwhitehorn *	Boston, MA 02110, USA.
21217309Snwhitehorn */
22217309Snwhitehorn
23217309Snwhitehorn#include <dialog.h>
24217309Snwhitehorn#include <dlg_keys.h>
25217309Snwhitehorn
26217309Snwhitehorn#include <sys/stat.h>
27217309Snwhitehorn
28217309Snwhitehorn#define sTEXT -1
29217309Snwhitehorn
30217309Snwhitehornstatic void
31217309Snwhitehornfail_list(void)
32217309Snwhitehorn{
33217309Snwhitehorn    dlg_exiterr("File too large");
34217309Snwhitehorn}
35217309Snwhitehorn
36217309Snwhitehornstatic void
37217309Snwhitehorngrow_list(char ***list, int *have, int want)
38217309Snwhitehorn{
39217309Snwhitehorn    if (want > *have) {
40217309Snwhitehorn	size_t last = (size_t) *have;
41217309Snwhitehorn	size_t need = (size_t) (want | 31) + 3;
42217309Snwhitehorn	*have = (int) need;
43217309Snwhitehorn	(*list) = dlg_realloc(char *, need, *list);
44217309Snwhitehorn	if ((*list) == 0) {
45217309Snwhitehorn	    fail_list();
46251843Sbapt	} else {
47251843Sbapt	    while (++last < need) {
48251843Sbapt		(*list)[last] = 0;
49251843Sbapt	    }
50217309Snwhitehorn	}
51217309Snwhitehorn    }
52217309Snwhitehorn}
53217309Snwhitehorn
54217309Snwhitehornstatic void
55217309Snwhitehornload_list(const char *file, char ***list, int *rows)
56217309Snwhitehorn{
57217309Snwhitehorn    FILE *fp;
58217309Snwhitehorn    char *blob = 0;
59217309Snwhitehorn    struct stat sb;
60217309Snwhitehorn    unsigned n, pass;
61217309Snwhitehorn    unsigned need;
62217309Snwhitehorn    size_t size;
63217309Snwhitehorn
64217309Snwhitehorn    *list = 0;
65217309Snwhitehorn    *rows = 0;
66217309Snwhitehorn
67217309Snwhitehorn    if (stat(file, &sb) < 0 ||
68217309Snwhitehorn	(sb.st_mode & S_IFMT) != S_IFREG)
69217309Snwhitehorn	dlg_exiterr("Not a file: %s", file);
70217309Snwhitehorn
71217309Snwhitehorn    size = (size_t) sb.st_size;
72251843Sbapt    if ((blob = dlg_malloc(char, size + 1)) == 0) {
73251843Sbapt	fail_list();
74251843Sbapt    } else {
75251843Sbapt	blob[size] = '\0';
76217309Snwhitehorn
77251843Sbapt	if ((fp = fopen(file, "r")) == 0)
78251843Sbapt	    dlg_exiterr("Cannot open: %s", file);
79251843Sbapt	size = fread(blob, sizeof(char), size, fp);
80251843Sbapt	fclose(fp);
81217309Snwhitehorn
82251843Sbapt	for (pass = 0; pass < 2; ++pass) {
83251843Sbapt	    int first = TRUE;
84251843Sbapt	    need = 0;
85251843Sbapt	    for (n = 0; n < size; ++n) {
86251843Sbapt		if (first && pass) {
87251843Sbapt		    (*list)[need] = blob + n;
88251843Sbapt		    first = FALSE;
89251843Sbapt		}
90251843Sbapt		if (blob[n] == '\n') {
91251843Sbapt		    first = TRUE;
92251843Sbapt		    ++need;
93251843Sbapt		    if (pass)
94251843Sbapt			blob[n] = '\0';
95251843Sbapt		}
96217309Snwhitehorn	    }
97251843Sbapt	    if (pass) {
98251843Sbapt		if (need == 0) {
99251843Sbapt		    (*list)[0] = dlg_strclone("");
100251843Sbapt		    (*list)[1] = 0;
101251843Sbapt		} else {
102251843Sbapt		    for (n = 0; n < need; ++n) {
103251843Sbapt			(*list)[n] = dlg_strclone((*list)[n]);
104251843Sbapt		    }
105251843Sbapt		    (*list)[need] = 0;
106251843Sbapt		}
107244850Snwhitehorn	    } else {
108251843Sbapt		grow_list(list, rows, (int) need + 1);
109217309Snwhitehorn	    }
110217309Snwhitehorn	}
111251843Sbapt	free(blob);
112217309Snwhitehorn    }
113217309Snwhitehorn}
114217309Snwhitehorn
115217309Snwhitehornstatic void
116217309Snwhitehornfree_list(char ***list, int *rows)
117217309Snwhitehorn{
118217309Snwhitehorn    if (*list != 0) {
119217309Snwhitehorn	int n;
120217309Snwhitehorn	for (n = 0; n < (*rows); ++n) {
121217309Snwhitehorn	    if ((*list)[n] != 0)
122217309Snwhitehorn		free((*list)[n]);
123217309Snwhitehorn	}
124217309Snwhitehorn	free(*list);
125217309Snwhitehorn	*list = 0;
126217309Snwhitehorn    }
127217309Snwhitehorn    *rows = 0;
128217309Snwhitehorn}
129217309Snwhitehorn
130217309Snwhitehorn/*
131217309Snwhitehorn * Display a single row in the editing window:
132217309Snwhitehorn * thisrow is the actual row number that's being displayed.
133217309Snwhitehorn * show_row is the row number that's highlighted for edit.
134217309Snwhitehorn * base_row is the first row number in the window
135217309Snwhitehorn */
136217309Snwhitehornstatic bool
137217309Snwhitehorndisplay_one(WINDOW *win,
138217309Snwhitehorn	    char *text,
139217309Snwhitehorn	    int thisrow,
140217309Snwhitehorn	    int show_row,
141217309Snwhitehorn	    int base_row,
142217309Snwhitehorn	    int chr_offset)
143217309Snwhitehorn{
144217309Snwhitehorn    bool result;
145217309Snwhitehorn
146217309Snwhitehorn    if (text != 0) {
147217309Snwhitehorn	dlg_show_string(win,
148217309Snwhitehorn			text,
149217309Snwhitehorn			chr_offset,
150217309Snwhitehorn			((thisrow == show_row)
151217309Snwhitehorn			 ? form_active_text_attr
152217309Snwhitehorn			 : form_text_attr),
153217309Snwhitehorn			thisrow - base_row,
154217309Snwhitehorn			0,
155217309Snwhitehorn			getmaxx(win),
156217309Snwhitehorn			FALSE,
157217309Snwhitehorn			FALSE);
158217309Snwhitehorn	result = TRUE;
159217309Snwhitehorn    } else {
160217309Snwhitehorn	result = FALSE;
161217309Snwhitehorn    }
162217309Snwhitehorn    return result;
163217309Snwhitehorn}
164217309Snwhitehorn
165217309Snwhitehornstatic void
166217309Snwhitehorndisplay_all(WINDOW *win,
167217309Snwhitehorn	    char **list,
168217309Snwhitehorn	    int show_row,
169217309Snwhitehorn	    int firstrow,
170217309Snwhitehorn	    int lastrow,
171217309Snwhitehorn	    int chr_offset)
172217309Snwhitehorn{
173217309Snwhitehorn    int limit = getmaxy(win);
174217309Snwhitehorn    int row;
175217309Snwhitehorn
176217309Snwhitehorn    dlg_attr_clear(win, getmaxy(win), getmaxx(win), dialog_attr);
177217309Snwhitehorn    if (lastrow - firstrow >= limit)
178217309Snwhitehorn	lastrow = firstrow + limit;
179217309Snwhitehorn    for (row = firstrow; row < lastrow; ++row) {
180217309Snwhitehorn	if (!display_one(win, list[row],
181217309Snwhitehorn			 row, show_row, firstrow,
182217309Snwhitehorn			 (row == show_row) ? chr_offset : 0))
183217309Snwhitehorn	    break;
184217309Snwhitehorn    }
185217309Snwhitehorn}
186217309Snwhitehorn
187217309Snwhitehornstatic int
188217309Snwhitehornsize_list(char **list)
189217309Snwhitehorn{
190217309Snwhitehorn    int result = 0;
191217309Snwhitehorn
192217309Snwhitehorn    if (list != 0) {
193217309Snwhitehorn	while (*list++ != 0) {
194217309Snwhitehorn	    ++result;
195217309Snwhitehorn	}
196217309Snwhitehorn    }
197217309Snwhitehorn    return result;
198217309Snwhitehorn}
199217309Snwhitehorn
200217309Snwhitehornstatic bool
201217309Snwhitehornscroll_to(int pagesize, int rows, int *base_row, int *this_row, int target)
202217309Snwhitehorn{
203217309Snwhitehorn    bool result = FALSE;
204217309Snwhitehorn
205217309Snwhitehorn    if (target < *base_row) {
206217309Snwhitehorn	if (target < 0) {
207217309Snwhitehorn	    if (*base_row == 0 && *this_row == 0) {
208217309Snwhitehorn		beep();
209217309Snwhitehorn	    } else {
210217309Snwhitehorn		*this_row = 0;
211217309Snwhitehorn		*base_row = 0;
212217309Snwhitehorn		result = TRUE;
213217309Snwhitehorn	    }
214217309Snwhitehorn	} else {
215217309Snwhitehorn	    *this_row = target;
216217309Snwhitehorn	    *base_row = target;
217217309Snwhitehorn	    result = TRUE;
218217309Snwhitehorn	}
219217309Snwhitehorn    } else if (target >= rows) {
220217309Snwhitehorn	if (*this_row < rows - 1) {
221217309Snwhitehorn	    *this_row = rows - 1;
222217309Snwhitehorn	    *base_row = rows - 1;
223217309Snwhitehorn	    result = TRUE;
224217309Snwhitehorn	} else {
225217309Snwhitehorn	    beep();
226217309Snwhitehorn	}
227217309Snwhitehorn    } else if (target >= *base_row + pagesize) {
228217309Snwhitehorn	*this_row = target;
229217309Snwhitehorn	*base_row = target;
230217309Snwhitehorn	result = TRUE;
231217309Snwhitehorn    } else {
232217309Snwhitehorn	*this_row = target;
233217309Snwhitehorn	result = FALSE;
234217309Snwhitehorn    }
235217309Snwhitehorn    if (pagesize < rows) {
236217309Snwhitehorn	if (*base_row + pagesize >= rows) {
237217309Snwhitehorn	    *base_row = rows - pagesize;
238217309Snwhitehorn	}
239217309Snwhitehorn    } else {
240217309Snwhitehorn	*base_row = 0;
241217309Snwhitehorn    }
242217309Snwhitehorn    return result;
243217309Snwhitehorn}
244217309Snwhitehorn
245217309Snwhitehornstatic int
246217309Snwhitehorncol_to_chr_offset(const char *text, int col)
247217309Snwhitehorn{
248217309Snwhitehorn    const int *cols = dlg_index_columns(text);
249217309Snwhitehorn    const int *indx = dlg_index_wchars(text);
250217309Snwhitehorn    bool found = FALSE;
251217309Snwhitehorn    int result = 0;
252217309Snwhitehorn    unsigned n;
253217309Snwhitehorn    unsigned len = (unsigned) dlg_count_wchars(text);
254217309Snwhitehorn
255217309Snwhitehorn    for (n = 0; n < len; ++n) {
256217309Snwhitehorn	if (cols[n] <= col && cols[n + 1] > col) {
257217309Snwhitehorn	    result = indx[n];
258217309Snwhitehorn	    found = TRUE;
259217309Snwhitehorn	    break;
260217309Snwhitehorn	}
261217309Snwhitehorn    }
262217309Snwhitehorn    if (!found && len && cols[len] == col) {
263217309Snwhitehorn	result = indx[len];
264217309Snwhitehorn    }
265217309Snwhitehorn    return result;
266217309Snwhitehorn}
267217309Snwhitehorn
268217309Snwhitehorn#define SCROLL_TO(target) show_all = scroll_to(pagesize, listsize, &base_row, &thisrow, target)
269217309Snwhitehorn
270217309Snwhitehorn#define PREV_ROW (*list)[thisrow - 1]
271217309Snwhitehorn#define THIS_ROW (*list)[thisrow]
272217309Snwhitehorn#define NEXT_ROW (*list)[thisrow + 1]
273217309Snwhitehorn
274217309Snwhitehorn#define UPDATE_COL(input) col_offset = dlg_edit_offset(input, chr_offset, box_width)
275217309Snwhitehorn
276217309Snwhitehornstatic int
277217309Snwhitehornwidest_line(char **list)
278217309Snwhitehorn{
279217309Snwhitehorn    int result = MAX_LEN;
280217309Snwhitehorn    char *value;
281217309Snwhitehorn
282217309Snwhitehorn    if (list != 0) {
283217309Snwhitehorn	while ((value = *list++) != 0) {
284217309Snwhitehorn	    int check = (int) strlen(value);
285217309Snwhitehorn	    if (check > result)
286217309Snwhitehorn		result = check;
287217309Snwhitehorn	}
288217309Snwhitehorn    }
289217309Snwhitehorn    return result;
290217309Snwhitehorn}
291217309Snwhitehorn
292217309Snwhitehorn#define NAVIGATE_BINDINGS \
293217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_DOWN,	KEY_DOWN ), \
294217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_RIGHT,	KEY_RIGHT ), \
295217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_LEFT,	KEY_LEFT ), \
296217309Snwhitehorn	DLG_KEYS_DATA( DLGK_GRID_UP,	KEY_UP ), \
297217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_NEXT,	TAB ), \
298217309Snwhitehorn	DLG_KEYS_DATA( DLGK_FIELD_PREV,	KEY_BTAB ), \
299217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_FIRST,	KEY_HOME ), \
300217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_END ), \
301217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_LAST,	KEY_LL ), \
302217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	KEY_NPAGE ), \
303217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_NEXT,	DLGK_MOUSE(KEY_NPAGE) ), \
304217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	KEY_PPAGE ), \
305217309Snwhitehorn	DLG_KEYS_DATA( DLGK_PAGE_PREV,	DLGK_MOUSE(KEY_PPAGE) )
306217309Snwhitehorn/*
307217309Snwhitehorn * Display a dialog box for editing a copy of a file
308217309Snwhitehorn */
309217309Snwhitehornint
310217309Snwhitehorndlg_editbox(const char *title,
311217309Snwhitehorn	    char ***list,
312217309Snwhitehorn	    int *rows,
313217309Snwhitehorn	    int height,
314217309Snwhitehorn	    int width)
315217309Snwhitehorn{
316217309Snwhitehorn    /* *INDENT-OFF* */
317217309Snwhitehorn    static DLG_KEYS_BINDING binding[] = {
318224014Snwhitehorn	HELPKEY_BINDINGS,
319217309Snwhitehorn	ENTERKEY_BINDINGS,
320217309Snwhitehorn	NAVIGATE_BINDINGS,
321217309Snwhitehorn	END_KEYS_BINDING
322217309Snwhitehorn    };
323217309Snwhitehorn    static DLG_KEYS_BINDING binding2[] = {
324217309Snwhitehorn	INPUTSTR_BINDINGS,
325224014Snwhitehorn	HELPKEY_BINDINGS,
326217309Snwhitehorn	ENTERKEY_BINDINGS,
327217309Snwhitehorn	NAVIGATE_BINDINGS,
328217309Snwhitehorn	END_KEYS_BINDING
329217309Snwhitehorn    };
330217309Snwhitehorn    /* *INDENT-ON* */
331217309Snwhitehorn
332217309Snwhitehorn#ifdef KEY_RESIZE
333217309Snwhitehorn    int old_height = height;
334217309Snwhitehorn    int old_width = width;
335217309Snwhitehorn#endif
336217309Snwhitehorn    int x, y, box_y, box_x, box_height, box_width;
337217309Snwhitehorn    int show_buttons;
338217309Snwhitehorn    int thisrow, base_row, lastrow;
339217309Snwhitehorn    int goal_col = -1;
340217309Snwhitehorn    int col_offset = 0;
341217309Snwhitehorn    int chr_offset = 0;
342217309Snwhitehorn    int key, fkey, code;
343217309Snwhitehorn    int pagesize;
344217309Snwhitehorn    int listsize = size_list(*list);
345217309Snwhitehorn    int result = DLG_EXIT_UNKNOWN;
346217309Snwhitehorn    int state;
347217309Snwhitehorn    size_t max_len = (size_t) dlg_max_input(widest_line(*list));
348217309Snwhitehorn    char *input, *buffer;
349217309Snwhitehorn    bool show_all, show_one, was_mouse;
350251843Sbapt    bool first_trace = TRUE;
351217309Snwhitehorn    WINDOW *dialog;
352217309Snwhitehorn    WINDOW *editing;
353217309Snwhitehorn    DIALOG_VARS save_vars;
354217309Snwhitehorn    const char **buttons = dlg_ok_labels();
355217309Snwhitehorn    int mincols = (3 * COLS / 4);
356217309Snwhitehorn
357217309Snwhitehorn    dlg_save_vars(&save_vars);
358217309Snwhitehorn    dialog_vars.separate_output = TRUE;
359217309Snwhitehorn
360217309Snwhitehorn    dlg_does_output();
361217309Snwhitehorn
362217309Snwhitehorn    buffer = dlg_malloc(char, max_len + 1);
363217309Snwhitehorn    assert_ptr(buffer, "dlg_editbox");
364217309Snwhitehorn
365217309Snwhitehorn    thisrow = base_row = lastrow = 0;
366217309Snwhitehorn
367217309Snwhitehorn#ifdef KEY_RESIZE
368217309Snwhitehorn  retry:
369217309Snwhitehorn#endif
370217309Snwhitehorn    show_buttons = TRUE;
371251843Sbapt    state = dialog_vars.default_button >= 0 ? dlg_default_button() : sTEXT;
372251843Sbapt    fkey = 0;
373217309Snwhitehorn
374217309Snwhitehorn    dlg_button_layout(buttons, &mincols);
375217309Snwhitehorn    dlg_auto_size(title, "", &height, &width, 3 * LINES / 4, mincols);
376217309Snwhitehorn    dlg_print_size(height, width);
377217309Snwhitehorn    dlg_ctl_size(height, width);
378217309Snwhitehorn
379217309Snwhitehorn    x = dlg_box_x_ordinate(width);
380217309Snwhitehorn    y = dlg_box_y_ordinate(height);
381217309Snwhitehorn
382217309Snwhitehorn    dialog = dlg_new_window(height, width, y, x);
383217309Snwhitehorn    dlg_register_window(dialog, "editbox", binding);
384217309Snwhitehorn    dlg_register_buttons(dialog, "editbox", buttons);
385217309Snwhitehorn
386217309Snwhitehorn    dlg_mouse_setbase(x, y);
387217309Snwhitehorn
388251843Sbapt    dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
389251843Sbapt    dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
390217309Snwhitehorn    dlg_draw_title(dialog, title);
391217309Snwhitehorn
392251843Sbapt    (void) wattrset(dialog, dialog_attr);
393217309Snwhitehorn
394217309Snwhitehorn    /* Draw the editing field in a box */
395217309Snwhitehorn    box_y = MARGIN + 0;
396217309Snwhitehorn    box_x = MARGIN + 1;
397217309Snwhitehorn    box_width = width - 2 - (2 * MARGIN);
398217309Snwhitehorn    box_height = height - (4 * MARGIN);
399217309Snwhitehorn
400217309Snwhitehorn    dlg_draw_box(dialog,
401217309Snwhitehorn		 box_y,
402217309Snwhitehorn		 box_x,
403217309Snwhitehorn		 box_height,
404217309Snwhitehorn		 box_width,
405251843Sbapt		 border_attr, border2_attr);
406217309Snwhitehorn    dlg_mouse_mkbigregion(box_y + MARGIN,
407217309Snwhitehorn			  box_x + MARGIN,
408217309Snwhitehorn			  box_height - (2 * MARGIN),
409217309Snwhitehorn			  box_width - (2 * MARGIN),
410217309Snwhitehorn			  KEY_MAX, 1, 1, 3);
411217309Snwhitehorn    editing = dlg_sub_window(dialog,
412217309Snwhitehorn			     box_height - (2 * MARGIN),
413217309Snwhitehorn			     box_width - (2 * MARGIN),
414217309Snwhitehorn			     getbegy(dialog) + box_y + 1,
415217309Snwhitehorn			     getbegx(dialog) + box_x + 1);
416251843Sbapt    dlg_register_window(editing, "editbox2", binding2);
417217309Snwhitehorn
418217309Snwhitehorn    show_all = TRUE;
419217309Snwhitehorn    show_one = FALSE;
420217309Snwhitehorn    pagesize = getmaxy(editing);
421217309Snwhitehorn
422217309Snwhitehorn    while (result == DLG_EXIT_UNKNOWN) {
423217309Snwhitehorn	int edit = 0;
424217309Snwhitehorn
425217309Snwhitehorn	if (show_all) {
426217309Snwhitehorn	    display_all(editing, *list, thisrow, base_row, listsize, chr_offset);
427217309Snwhitehorn	    display_one(editing, THIS_ROW,
428217309Snwhitehorn			thisrow, thisrow, base_row, chr_offset);
429217309Snwhitehorn	    show_all = FALSE;
430217309Snwhitehorn	    show_one = TRUE;
431217309Snwhitehorn	} else {
432217309Snwhitehorn	    if (thisrow != lastrow) {
433217309Snwhitehorn		display_one(editing, (*list)[lastrow],
434217309Snwhitehorn			    lastrow, thisrow, base_row, 0);
435217309Snwhitehorn		show_one = TRUE;
436217309Snwhitehorn	    }
437217309Snwhitehorn	}
438217309Snwhitehorn	if (show_one) {
439217309Snwhitehorn	    display_one(editing, THIS_ROW,
440217309Snwhitehorn			thisrow, thisrow, base_row, chr_offset);
441217309Snwhitehorn	    getyx(editing, y, x);
442217309Snwhitehorn	    dlg_draw_scrollbar(dialog,
443217309Snwhitehorn			       base_row,
444217309Snwhitehorn			       base_row,
445217309Snwhitehorn			       base_row + pagesize,
446217309Snwhitehorn			       listsize,
447217309Snwhitehorn			       box_x,
448217309Snwhitehorn			       box_x + getmaxx(editing),
449217309Snwhitehorn			       box_y + 0,
450217309Snwhitehorn			       box_y + getmaxy(editing) + 1,
451251843Sbapt			       border2_attr,
452217309Snwhitehorn			       border_attr);
453217309Snwhitehorn	    wmove(editing, y, x);
454217309Snwhitehorn	    show_one = FALSE;
455217309Snwhitehorn	}
456217309Snwhitehorn	lastrow = thisrow;
457217309Snwhitehorn	input = THIS_ROW;
458217309Snwhitehorn
459217309Snwhitehorn	/*
460217309Snwhitehorn	 * The last field drawn determines where the cursor is shown:
461217309Snwhitehorn	 */
462217309Snwhitehorn	if (show_buttons) {
463217309Snwhitehorn	    show_buttons = FALSE;
464217309Snwhitehorn	    UPDATE_COL(input);
465217309Snwhitehorn	    if (state != sTEXT) {
466217309Snwhitehorn		display_one(editing, input, thisrow,
467217309Snwhitehorn			    -1, base_row, 0);
468217309Snwhitehorn		wrefresh(editing);
469217309Snwhitehorn	    }
470217309Snwhitehorn	    dlg_draw_buttons(dialog,
471217309Snwhitehorn			     height - 2,
472217309Snwhitehorn			     0,
473217309Snwhitehorn			     buttons,
474217309Snwhitehorn			     (state != sTEXT) ? state : 99,
475217309Snwhitehorn			     FALSE,
476217309Snwhitehorn			     width);
477217309Snwhitehorn	    if (state == sTEXT) {
478217309Snwhitehorn		display_one(editing, input, thisrow,
479217309Snwhitehorn			    thisrow, base_row, chr_offset);
480217309Snwhitehorn	    }
481217309Snwhitehorn	}
482217309Snwhitehorn
483251843Sbapt	if (first_trace) {
484251843Sbapt	    first_trace = FALSE;
485251843Sbapt	    dlg_trace_win(dialog);
486251843Sbapt	}
487251843Sbapt
488217309Snwhitehorn	key = dlg_mouse_wgetch((state == sTEXT) ? editing : dialog, &fkey);
489217309Snwhitehorn	if (key == ERR) {
490217309Snwhitehorn	    result = DLG_EXIT_ERROR;
491217309Snwhitehorn	    break;
492217309Snwhitehorn	} else if (key == ESC) {
493217309Snwhitehorn	    result = DLG_EXIT_ESC;
494217309Snwhitehorn	    break;
495217309Snwhitehorn	}
496217309Snwhitehorn	if (state != sTEXT) {
497217309Snwhitehorn	    if (dlg_result_key(key, fkey, &result))
498217309Snwhitehorn		break;
499217309Snwhitehorn	}
500217309Snwhitehorn
501217309Snwhitehorn	was_mouse = (fkey && is_DLGK_MOUSE(key));
502217309Snwhitehorn	if (was_mouse)
503217309Snwhitehorn	    key -= M_EVENT;
504217309Snwhitehorn
505217309Snwhitehorn	/*
506217309Snwhitehorn	 * Handle mouse clicks first, since we want to know if this is a
507217309Snwhitehorn	 * button, or something that dlg_edit_string() should handle.
508217309Snwhitehorn	 */
509217309Snwhitehorn	if (fkey
510217309Snwhitehorn	    && was_mouse
511217309Snwhitehorn	    && (code = dlg_ok_buttoncode(key)) >= 0) {
512217309Snwhitehorn	    result = code;
513217309Snwhitehorn	    continue;
514217309Snwhitehorn	}
515217309Snwhitehorn
516217309Snwhitehorn	if (was_mouse
517217309Snwhitehorn	    && (key >= KEY_MAX)) {
518217309Snwhitehorn	    int wide = getmaxx(editing);
519217309Snwhitehorn	    int cell = key - KEY_MAX;
520217309Snwhitehorn	    thisrow = (cell / wide) + base_row;
521217309Snwhitehorn	    col_offset = (cell % wide);
522217309Snwhitehorn	    chr_offset = col_to_chr_offset(THIS_ROW, col_offset);
523217309Snwhitehorn	    show_one = TRUE;
524217309Snwhitehorn	    if (state != sTEXT) {
525217309Snwhitehorn		state = sTEXT;
526217309Snwhitehorn		show_buttons = TRUE;
527217309Snwhitehorn	    }
528217309Snwhitehorn	    continue;
529217309Snwhitehorn	} else if (was_mouse && key >= KEY_MIN) {
530217309Snwhitehorn	    key = dlg_lookup_key(dialog, key, &fkey);
531217309Snwhitehorn	}
532217309Snwhitehorn
533217309Snwhitehorn	if (state == sTEXT) {	/* editing box selected */
534217309Snwhitehorn	    /*
535217309Snwhitehorn	     * Intercept scrolling keys that dlg_edit_string() does not
536217309Snwhitehorn	     * understand.
537217309Snwhitehorn	     */
538217309Snwhitehorn	    if (fkey) {
539217309Snwhitehorn		bool moved = TRUE;
540217309Snwhitehorn
541217309Snwhitehorn		switch (key) {
542217309Snwhitehorn		case DLGK_GRID_UP:
543217309Snwhitehorn		    SCROLL_TO(thisrow - 1);
544217309Snwhitehorn		    break;
545217309Snwhitehorn		case DLGK_GRID_DOWN:
546217309Snwhitehorn		    SCROLL_TO(thisrow + 1);
547217309Snwhitehorn		    break;
548217309Snwhitehorn		case DLGK_PAGE_FIRST:
549217309Snwhitehorn		    SCROLL_TO(0);
550217309Snwhitehorn		    break;
551217309Snwhitehorn		case DLGK_PAGE_LAST:
552217309Snwhitehorn		    SCROLL_TO(listsize);
553217309Snwhitehorn		    break;
554217309Snwhitehorn		case DLGK_PAGE_NEXT:
555217309Snwhitehorn		    SCROLL_TO(base_row + pagesize);
556217309Snwhitehorn		    break;
557217309Snwhitehorn		case DLGK_PAGE_PREV:
558217309Snwhitehorn		    if (thisrow > base_row) {
559217309Snwhitehorn			SCROLL_TO(base_row);
560217309Snwhitehorn		    } else {
561217309Snwhitehorn			SCROLL_TO(base_row - pagesize);
562217309Snwhitehorn		    }
563217309Snwhitehorn		    break;
564217309Snwhitehorn		case DLGK_DELETE_LEFT:
565217309Snwhitehorn		    if (chr_offset == 0) {
566217309Snwhitehorn			if (thisrow == 0) {
567217309Snwhitehorn			    beep();
568217309Snwhitehorn			} else {
569217309Snwhitehorn			    size_t len = (strlen(THIS_ROW) +
570217309Snwhitehorn					  strlen(PREV_ROW) + 1);
571217309Snwhitehorn			    char *tmp = dlg_malloc(char, len);
572217309Snwhitehorn
573217309Snwhitehorn			    assert_ptr(tmp, "dlg_editbox");
574217309Snwhitehorn
575217309Snwhitehorn			    chr_offset = dlg_count_wchars(PREV_ROW);
576217309Snwhitehorn			    UPDATE_COL(PREV_ROW);
577217309Snwhitehorn			    goal_col = col_offset;
578217309Snwhitehorn
579217309Snwhitehorn			    sprintf(tmp, "%s%s", PREV_ROW, THIS_ROW);
580217309Snwhitehorn			    if (len > max_len)
581217309Snwhitehorn				tmp[max_len] = '\0';
582217309Snwhitehorn
583217309Snwhitehorn			    free(PREV_ROW);
584217309Snwhitehorn			    PREV_ROW = tmp;
585217309Snwhitehorn			    for (y = thisrow; y < listsize; ++y) {
586217309Snwhitehorn				(*list)[y] = (*list)[y + 1];
587217309Snwhitehorn			    }
588217309Snwhitehorn			    --listsize;
589217309Snwhitehorn			    --thisrow;
590217309Snwhitehorn			    SCROLL_TO(thisrow);
591217309Snwhitehorn
592217309Snwhitehorn			    show_all = TRUE;
593217309Snwhitehorn			}
594217309Snwhitehorn		    } else {
595217309Snwhitehorn			/* dlg_edit_string() can handle this case */
596217309Snwhitehorn			moved = FALSE;
597217309Snwhitehorn		    }
598217309Snwhitehorn		    break;
599217309Snwhitehorn		default:
600217309Snwhitehorn		    moved = FALSE;
601217309Snwhitehorn		    break;
602217309Snwhitehorn		}
603217309Snwhitehorn		if (moved) {
604217309Snwhitehorn		    if (thisrow != lastrow) {
605217309Snwhitehorn			if (goal_col < 0)
606217309Snwhitehorn			    goal_col = col_offset;
607217309Snwhitehorn			chr_offset = col_to_chr_offset(THIS_ROW, goal_col);
608217309Snwhitehorn		    } else {
609217309Snwhitehorn			UPDATE_COL(THIS_ROW);
610217309Snwhitehorn		    }
611217309Snwhitehorn		    continue;
612217309Snwhitehorn		}
613217309Snwhitehorn	    }
614217309Snwhitehorn	    strncpy(buffer, input, max_len - 1)[max_len - 1] = '\0';
615217309Snwhitehorn	    edit = dlg_edit_string(buffer, &chr_offset, key, fkey, FALSE);
616217309Snwhitehorn
617217309Snwhitehorn	    if (edit) {
618217309Snwhitehorn		goal_col = UPDATE_COL(input);
619217309Snwhitehorn		if (strcmp(input, buffer)) {
620217309Snwhitehorn		    free(input);
621217309Snwhitehorn		    THIS_ROW = dlg_strclone(buffer);
622217309Snwhitehorn		    input = THIS_ROW;
623217309Snwhitehorn		}
624217309Snwhitehorn		display_one(editing, input, thisrow,
625217309Snwhitehorn			    thisrow, base_row, chr_offset);
626217309Snwhitehorn		continue;
627217309Snwhitehorn	    }
628217309Snwhitehorn	}
629217309Snwhitehorn
630217309Snwhitehorn	/* handle non-functionkeys */
631217309Snwhitehorn	if (!fkey && (code = dlg_char_to_button(key, buttons)) >= 0) {
632217309Snwhitehorn	    dlg_del_window(dialog);
633217309Snwhitehorn	    result = dlg_ok_buttoncode(code);
634217309Snwhitehorn	    continue;
635217309Snwhitehorn	}
636217309Snwhitehorn
637217309Snwhitehorn	/* handle functionkeys */
638217309Snwhitehorn	if (fkey) {
639217309Snwhitehorn	    switch (key) {
640217309Snwhitehorn	    case DLGK_FIELD_PREV:
641217309Snwhitehorn		show_buttons = TRUE;
642217309Snwhitehorn		state = dlg_prev_ok_buttonindex(state, sTEXT);
643217309Snwhitehorn		break;
644217309Snwhitehorn	    case DLGK_FIELD_NEXT:
645217309Snwhitehorn		show_buttons = TRUE;
646217309Snwhitehorn		state = dlg_next_ok_buttonindex(state, sTEXT);
647217309Snwhitehorn		break;
648217309Snwhitehorn	    case DLGK_ENTER:
649217309Snwhitehorn		if (state == sTEXT) {
650217309Snwhitehorn		    const int *indx = dlg_index_wchars(THIS_ROW);
651217309Snwhitehorn		    int split = indx[chr_offset];
652217309Snwhitehorn		    char *tmp = dlg_strclone(THIS_ROW + split);
653217309Snwhitehorn
654217309Snwhitehorn		    assert_ptr(tmp, "dlg_editbox");
655217309Snwhitehorn		    grow_list(list, rows, listsize + 1);
656217309Snwhitehorn		    ++listsize;
657217309Snwhitehorn		    for (y = listsize; y > thisrow; --y) {
658217309Snwhitehorn			(*list)[y] = (*list)[y - 1];
659217309Snwhitehorn		    }
660217309Snwhitehorn		    THIS_ROW[split] = '\0';
661217309Snwhitehorn		    ++thisrow;
662217309Snwhitehorn		    chr_offset = 0;
663217309Snwhitehorn		    col_offset = 0;
664217309Snwhitehorn		    THIS_ROW = tmp;
665217309Snwhitehorn		    SCROLL_TO(thisrow);
666217309Snwhitehorn		    show_all = TRUE;
667217309Snwhitehorn		} else {
668217309Snwhitehorn		    result = dlg_ok_buttoncode(state);
669217309Snwhitehorn		}
670217309Snwhitehorn		break;
671217309Snwhitehorn#ifdef KEY_RESIZE
672217309Snwhitehorn	    case KEY_RESIZE:
673217309Snwhitehorn		/* reset data */
674217309Snwhitehorn		height = old_height;
675217309Snwhitehorn		width = old_width;
676217309Snwhitehorn		/* repaint */
677217309Snwhitehorn		dlg_clear();
678217309Snwhitehorn		dlg_del_window(editing);
679217309Snwhitehorn		dlg_del_window(dialog);
680217309Snwhitehorn		refresh();
681217309Snwhitehorn		dlg_mouse_free_regions();
682217309Snwhitehorn		goto retry;
683217309Snwhitehorn#endif
684217309Snwhitehorn	    default:
685217309Snwhitehorn		beep();
686217309Snwhitehorn		break;
687217309Snwhitehorn	    }
688217309Snwhitehorn	} else {
689217309Snwhitehorn	    if ((key == ' ') && (state != sTEXT)) {
690217309Snwhitehorn		result = dlg_ok_buttoncode(state);
691217309Snwhitehorn	    } else {
692217309Snwhitehorn		beep();
693217309Snwhitehorn	    }
694217309Snwhitehorn	}
695217309Snwhitehorn    }
696217309Snwhitehorn
697217309Snwhitehorn    dlg_unregister_window(editing);
698217309Snwhitehorn    dlg_del_window(editing);
699217309Snwhitehorn    dlg_del_window(dialog);
700217309Snwhitehorn    dlg_mouse_free_regions();
701217309Snwhitehorn
702217309Snwhitehorn    /*
703217309Snwhitehorn     * The caller's copy of the (*list)[] array has been updated, but for
704217309Snwhitehorn     * consistency with the other widgets, we put the "real" result in
705217309Snwhitehorn     * the output buffer.
706217309Snwhitehorn     */
707217309Snwhitehorn    if (result == DLG_EXIT_OK) {
708217309Snwhitehorn	int n;
709217309Snwhitehorn	for (n = 0; n < listsize; ++n) {
710217309Snwhitehorn	    dlg_add_result((*list)[n]);
711217309Snwhitehorn	    dlg_add_separator();
712217309Snwhitehorn	}
713251843Sbapt	dlg_add_last_key(-1);
714217309Snwhitehorn    }
715217309Snwhitehorn    free(buffer);
716217309Snwhitehorn    dlg_restore_vars(&save_vars);
717217309Snwhitehorn    return result;
718217309Snwhitehorn}
719217309Snwhitehorn
720217309Snwhitehornint
721217309Snwhitehorndialog_editbox(const char *title, const char *file, int height, int width)
722217309Snwhitehorn{
723217309Snwhitehorn    int result;
724217309Snwhitehorn    char **list;
725217309Snwhitehorn    int rows;
726217309Snwhitehorn
727217309Snwhitehorn    load_list(file, &list, &rows);
728217309Snwhitehorn    result = dlg_editbox(title, &list, &rows, height, width);
729217309Snwhitehorn    free_list(&list, &rows);
730217309Snwhitehorn    return result;
731217309Snwhitehorn}
732