1 /* Input field widget implementation. */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include "elinks.h"
12 
13 #include "bfu/button.h"
14 #include "bfu/dialog.h"
15 #include "bfu/inpfield.h"
16 #include "bfu/inphist.h"
17 #include "bfu/msgbox.h"
18 #include "bfu/text.h"
19 #include "config/kbdbind.h"
20 #include "intl/gettext/libintl.h"
21 #include "osdep/osdep.h"
22 #include "session/session.h"
23 #include "terminal/draw.h"
24 #include "terminal/kbd.h"
25 #include "terminal/mouse.h"
26 #include "terminal/terminal.h"
27 #include "terminal/window.h"
28 #include "util/color.h"
29 #include "util/memlist.h"
30 #include "util/memory.h"
31 
32 #define INPUTFIELD_HEIGHT 1
33 
34 #define INPUTFIELD_FLOATLABEL_PADDING 1
35 
36 #define INPUTFIELD_FLOAT_SEPARATOR ":"
37 #define INPUTFIELD_FLOAT_SEPARATOR_LEN 1
38 
39 void
add_dlg_field_do(struct dialog * dlg,enum widget_type type,unsigned char * label,int min,int max,widget_handler_T * handler,int datalen,void * data,struct input_history * history,enum inpfield_flags flags)40 add_dlg_field_do(struct dialog *dlg, enum widget_type type, unsigned char *label,
41 		 int min, int max, widget_handler_T *handler,
42 		 int datalen, void *data,
43 		 struct input_history *history, enum inpfield_flags flags)
44 {
45 	struct widget *widget = &dlg->widgets[dlg->number_of_widgets++];
46 
47 	widget->type    = type;
48 	widget->text    = label;
49 	widget->handler = handler;
50 	widget->datalen = datalen;
51 	widget->data    = data;
52 
53 	widget->info.field.history = history;
54 	widget->info.field.flags   = flags;
55 	widget->info.field.min     = min;
56 	widget->info.field.max     = max;
57 }
58 
59 widget_handler_status_T
check_number(struct dialog_data * dlg_data,struct widget_data * widget_data)60 check_number(struct dialog_data *dlg_data, struct widget_data *widget_data)
61 {
62 	struct widget *widget = widget_data->widget;
63 	char *end;
64 	long l;
65 
66 	errno = 0;
67 	l = strtol(widget_data->cdata, &end, 10);
68 
69 	if (errno || !*widget_data->cdata || *end) {
70 		info_box(dlg_data->win->term, 0,
71 			 N_("Bad number"), ALIGN_CENTER,
72 			 N_("Number expected in field"));
73 		return EVENT_NOT_PROCESSED;
74 	}
75 
76 	if (l < widget->info.field.min || l > widget->info.field.max) {
77 		info_box(dlg_data->win->term, MSGBOX_FREE_TEXT,
78 			 N_("Bad number"), ALIGN_CENTER,
79 			 msg_text(dlg_data->win->term,
80 				  N_("Number should be in the range from %d to %d."),
81 				  widget->info.field.min, widget->info.field.max));
82 		return EVENT_NOT_PROCESSED;
83 	}
84 
85 	return EVENT_PROCESSED;
86 }
87 
88 widget_handler_status_T
check_nonempty(struct dialog_data * dlg_data,struct widget_data * widget_data)89 check_nonempty(struct dialog_data *dlg_data, struct widget_data *widget_data)
90 {
91 	unsigned char *p;
92 
93 	for (p = widget_data->cdata; *p; p++)
94 		if (*p > ' ')
95 			return EVENT_PROCESSED;
96 
97 	info_box(dlg_data->win->term, 0,
98 		 N_("Bad string"), ALIGN_CENTER,
99 		 N_("Empty string not allowed"));
100 
101 	return EVENT_NOT_PROCESSED;
102 }
103 
104 void
dlg_format_field(struct terminal * term,struct widget_data * widget_data,int x,int * y,int w,int * rw,enum format_align align)105 dlg_format_field(struct terminal *term,
106 		 struct widget_data *widget_data,
107 		 int x, int *y, int w, int *rw, enum format_align align)
108 {
109 	static int max_label_width;
110 	static int *prev_y; /* Assert the uniqueness of y */	/* TODO: get rid of this !! --Zas */
111 	unsigned char *label = widget_data->widget->text;
112 	struct color_pair *text_color = NULL;
113 	int label_width = 0;
114 	int float_label = widget_data->widget->info.field.flags & (INPFIELD_FLOAT|INPFIELD_FLOAT2);
115 
116 	if (label && *label && float_label) {
117 		label_width = strlen(label);
118 		if (prev_y == y) {
119 			int_lower_bound(&max_label_width, label_width);
120 		} else {
121 			max_label_width = label_width;
122 			prev_y = y;
123 		}
124 
125 		/* Right align the floating label up against the
126 		 * input field */
127 		x += max_label_width - label_width;
128 		w -= max_label_width - label_width;
129 	}
130 
131 	if (label && *label) {
132 		if (term) text_color = get_bfu_color(term, "dialog.text");
133 
134 		dlg_format_text_do(term, label, x, y, w, rw, text_color, ALIGN_LEFT);
135 	}
136 
137 	/* XXX: We want the field and label on the same line if the terminal
138 	 * width allows it. */
139 	if (label && *label && float_label) {
140 		if (widget_data->widget->info.field.flags & INPFIELD_FLOAT) {
141 			(*y) -= INPUTFIELD_HEIGHT;
142 			dlg_format_text_do(term, INPUTFIELD_FLOAT_SEPARATOR,
143 					   x + label_width, y, w, rw,
144 					   text_color, ALIGN_LEFT);
145 			w -= INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
146 			x += INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
147 		}
148 
149 		/* FIXME: Is 5 chars for input field enough? --jonas */
150 		if (label_width < w - 5) {
151 			(*y) -= INPUTFIELD_HEIGHT;
152 			w -= label_width;
153 			x += label_width;
154 		}
155 	}
156 
157 	if (rw) int_lower_bound(rw, int_min(w, DIALOG_MIN_WIDTH));
158 
159 	set_box(&widget_data->box, x, *y, w, INPUTFIELD_HEIGHT);
160 
161 	(*y) += INPUTFIELD_HEIGHT;
162 }
163 
164 static widget_handler_status_T
input_field_cancel(struct dialog_data * dlg_data,struct widget_data * widget_data)165 input_field_cancel(struct dialog_data *dlg_data, struct widget_data *widget_data)
166 {
167 	void (*fn)(void *) = widget_data->widget->data;
168 	void *data = dlg_data->dlg->udata2;
169 
170 	if (fn) fn(data);
171 
172 	return cancel_dialog(dlg_data, widget_data);
173 }
174 
175 static widget_handler_status_T
input_field_ok(struct dialog_data * dlg_data,struct widget_data * widget_data)176 input_field_ok(struct dialog_data *dlg_data, struct widget_data *widget_data)
177 {
178 	void (*fn)(void *, unsigned char *) = widget_data->widget->data;
179 	void *data = dlg_data->dlg->udata2;
180 	unsigned char *text = dlg_data->widgets_data->cdata;
181 
182 	if (check_dialog(dlg_data)) return EVENT_NOT_PROCESSED;
183 
184 	if (widget_has_history(dlg_data->widgets_data))
185 		add_to_input_history(dlg_data->dlg->widgets->info.field.history,
186 				     text, 1);
187 
188 	if (fn) fn(data, text);
189 
190 	return cancel_dialog(dlg_data, widget_data);
191 }
192 
193 void
input_field(struct terminal * term,struct memory_list * ml,int intl,unsigned char * title,unsigned char * text,unsigned char * okbutton,unsigned char * cancelbutton,void * data,struct input_history * history,int l,unsigned char * def,int min,int max,widget_handler_T * check,void (* fn)(void *,unsigned char *),void (* cancelfn)(void *))194 input_field(struct terminal *term, struct memory_list *ml, int intl,
195 	    unsigned char *title,
196 	    unsigned char *text,
197 	    unsigned char *okbutton,
198 	    unsigned char *cancelbutton,
199 	    void *data, struct input_history *history, int l,
200 	    unsigned char *def, int min, int max,
201 	    widget_handler_T *check,
202 	    void (*fn)(void *, unsigned char *),
203 	    void (*cancelfn)(void *))
204 {
205 	struct dialog *dlg;
206 	unsigned char *field;
207 
208 	if (intl) {
209 		title = _(title, term);
210 		text = _(text, term);
211 		okbutton = _(okbutton, term);
212 		cancelbutton = _(cancelbutton, term);
213 	}
214 
215 #define INPUT_WIDGETS_COUNT 3
216 	dlg = calloc_dialog(INPUT_WIDGETS_COUNT, l);
217 	if (!dlg) return;
218 
219 	/* @field is automatically cleared by calloc() */
220 	field = get_dialog_offset(dlg, INPUT_WIDGETS_COUNT);
221 
222 	if (def) {
223 		int defsize = strlen(def) + 1;
224 
225 		memcpy(field, def, (defsize > l) ? l - 1 : defsize);
226 	}
227 
228 	dlg->title = title;
229 	dlg->layouter = generic_dialog_layouter;
230 	dlg->layout.fit_datalen = 1;
231 	dlg->udata2 = data;
232 
233 	add_dlg_field(dlg, text, min, max, check, l, field, history);
234 
235 	add_dlg_button(dlg, okbutton, B_ENTER, input_field_ok, fn);
236 	add_dlg_button(dlg, cancelbutton, B_ESC, input_field_cancel, cancelfn);
237 
238 	add_dlg_end(dlg, INPUT_WIDGETS_COUNT);
239 
240 	add_to_ml(&ml, dlg, NULL);
241 	do_dialog(term, dlg, ml);
242 }
243 
244 void
input_dialog(struct terminal * term,struct memory_list * ml,unsigned char * title,unsigned char * text,void * data,struct input_history * history,int l,unsigned char * def,int min,int max,widget_handler_T * check,void (* fn)(void *,unsigned char *),void (* cancelfn)(void *))245 input_dialog(struct terminal *term, struct memory_list *ml,
246 	     unsigned char *title,
247 	     unsigned char *text,
248 	     void *data, struct input_history *history, int l,
249 	     unsigned char *def, int min, int max,
250 	     widget_handler_T *check,
251 	     void (*fn)(void *, unsigned char *),
252 	     void (*cancelfn)(void *))
253 {
254 	input_field(term, ml, 1, title, text, N_("~OK"), N_("~Cancel"),
255 		    data, history, l,
256 		    def, min, max,
257 		    check, fn, cancelfn);
258 }
259 
260 static widget_handler_status_T
display_field_do(struct dialog_data * dlg_data,struct widget_data * widget_data,int hide)261 display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data,
262 		 int hide)
263 {
264 	struct terminal *term = dlg_data->win->term;
265 	struct color_pair *color;
266 	int sel = is_selected_widget(dlg_data, widget_data);
267 
268 	int_bounds(&widget_data->info.field.vpos,
269 		   widget_data->info.field.cpos - widget_data->box.width + 1,
270 		   widget_data->info.field.cpos);
271 	int_lower_bound(&widget_data->info.field.vpos, 0);
272 
273 	color = get_bfu_color(term, "dialog.field");
274 	if (color)
275 		draw_box(term, &widget_data->box, ' ', 0, color);
276 
277 	color = get_bfu_color(term, "dialog.field-text");
278 	if (color) {
279 		int len = strlen(widget_data->cdata + widget_data->info.field.vpos);
280 		int w = int_min(len, widget_data->box.width);
281 
282 		if (!hide) {
283 			draw_text(term, widget_data->box.x, widget_data->box.y,
284 				  widget_data->cdata + widget_data->info.field.vpos, w,
285 				  0, color);
286 		} else {
287 			struct box box;
288 
289 			copy_box(&box, &widget_data->box);
290 			box.width = w;
291 
292 			draw_box(term, &box, '*', 0, color);
293 		}
294 	}
295 
296 	if (sel) {
297 		int x = widget_data->box.x + widget_data->info.field.cpos - widget_data->info.field.vpos;
298 
299 		set_cursor(term, x, widget_data->box.y, 0);
300 		set_window_ptr(dlg_data->win, widget_data->box.x, widget_data->box.y);
301 	}
302 
303 	return EVENT_PROCESSED;
304 }
305 
306 static widget_handler_status_T
display_field(struct dialog_data * dlg_data,struct widget_data * widget_data)307 display_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
308 {
309 	return display_field_do(dlg_data, widget_data, 0);
310 }
311 
312 static widget_handler_status_T
display_field_pass(struct dialog_data * dlg_data,struct widget_data * widget_data)313 display_field_pass(struct dialog_data *dlg_data, struct widget_data *widget_data)
314 {
315 	return display_field_do(dlg_data, widget_data, 1);
316 }
317 
318 static widget_handler_status_T
init_field(struct dialog_data * dlg_data,struct widget_data * widget_data)319 init_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
320 {
321 	if (widget_has_history(widget_data)) {
322 		struct input_history_entry *entry;
323 
324 		foreach (entry, widget_data->widget->info.field.history->entries) {
325 			int datalen = strlen(entry->data);
326 			struct input_history_entry *new_entry;
327 
328 			/* One byte is reserved in struct input_history_entry. */
329 			new_entry = mem_alloc(sizeof(*new_entry) + datalen);
330 			if (!new_entry) continue;
331 
332 			memcpy(new_entry->data, entry->data, datalen + 1);
333 			add_to_list(widget_data->info.field.history, new_entry);
334 		}
335 	}
336 
337 	widget_data->info.field.cpos = strlen(widget_data->cdata);
338 	return EVENT_PROCESSED;
339 }
340 
341 static int
field_prev_history(struct widget_data * widget_data)342 field_prev_history(struct widget_data *widget_data)
343 {
344 	if (widget_has_history(widget_data)
345 	    && (void *) widget_data->info.field.cur_hist->prev != &widget_data->info.field.history) {
346 		widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->prev;
347 		dlg_set_history(widget_data);
348 		return 1;
349 	}
350 	return 0;
351 }
352 
353 static int
field_next_history(struct widget_data * widget_data)354 field_next_history(struct widget_data *widget_data)
355 {
356 	if (widget_has_history(widget_data)
357 	    && (void *) widget_data->info.field.cur_hist != &widget_data->info.field.history) {
358 		widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->next;
359 		dlg_set_history(widget_data);
360 		return 1;
361 	}
362 	return 0;
363 }
364 
365 static widget_handler_status_T
mouse_field(struct dialog_data * dlg_data,struct widget_data * widget_data)366 mouse_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
367 {
368 	struct term_event *ev = dlg_data->term_event;
369 
370 	if (!check_mouse_position(ev, &widget_data->box))
371 		return EVENT_NOT_PROCESSED;
372 
373 	/* Handle navigation through history (if any) using up/down mouse wheel */
374 	switch (get_mouse_button(ev)) {
375 	case B_WHEEL_UP:
376 		if (check_mouse_action(ev, B_DOWN)) {
377 			if (field_prev_history(widget_data)) {
378 				select_widget(dlg_data, widget_data);
379 				return EVENT_PROCESSED;
380 			}
381 		}
382 		return EVENT_NOT_PROCESSED;
383 
384 	case B_WHEEL_DOWN:
385 		if (check_mouse_action(ev, B_DOWN)) {
386 			if (field_next_history(widget_data)) {
387 				select_widget(dlg_data, widget_data);
388 				return EVENT_PROCESSED;
389 			}
390 		}
391 		return EVENT_NOT_PROCESSED;
392 	}
393 
394 	/* Place text cursor at mouse position and focus the widget. */
395 	widget_data->info.field.cpos = widget_data->info.field.vpos
396 				     + ev->info.mouse.x - widget_data->box.x;
397 	int_upper_bound(&widget_data->info.field.cpos, strlen(widget_data->cdata));
398 
399 	select_widget(dlg_data, widget_data);
400 	return EVENT_PROCESSED;
401 }
402 
403 static widget_handler_status_T
kbd_field(struct dialog_data * dlg_data,struct widget_data * widget_data)404 kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
405 {
406 	struct window *win = dlg_data->win;
407 	struct terminal *term = win->term;
408 	struct term_event *ev = dlg_data->term_event;
409 	action_id_T action_id;
410 
411 	action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
412 	if (action_id != -1
413 	    && !action_is_anonymous_safe(KEYMAP_EDIT, action_id)
414 	    && get_cmd_opt_bool("anonymous"))
415 		return EVENT_NOT_PROCESSED;
416 
417 	switch (action_id) {
418 		case ACT_EDIT_UP:
419 			if (!widget_has_history(widget_data))
420 				return EVENT_NOT_PROCESSED;
421 
422 			if (field_prev_history(widget_data)) {
423 				goto display_field;
424 			}
425 			break;
426 
427 		case ACT_EDIT_DOWN:
428 			if (!widget_has_history(widget_data))
429 				return EVENT_NOT_PROCESSED;
430 
431 			if (field_next_history(widget_data)) {
432 				goto display_field;
433 			}
434 			break;
435 
436 		case ACT_EDIT_RIGHT:
437 			if (widget_data->info.field.cpos < strlen(widget_data->cdata))
438 				widget_data->info.field.cpos++;
439 			goto display_field;
440 
441 		case ACT_EDIT_LEFT:
442 			if (widget_data->info.field.cpos > 0)
443 				widget_data->info.field.cpos--;
444 			goto display_field;
445 
446 		case ACT_EDIT_HOME:
447 			widget_data->info.field.cpos = 0;
448 			goto display_field;
449 
450 		case ACT_EDIT_END:
451 			widget_data->info.field.cpos = strlen(widget_data->cdata);
452 			goto display_field;
453 
454 		case ACT_EDIT_BACKSPACE:
455 			if (widget_data->info.field.cpos) {
456 				memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
457 					widget_data->cdata + widget_data->info.field.cpos,
458 					strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
459 				widget_data->info.field.cpos--;
460 			}
461 			goto display_field;
462 
463 		case ACT_EDIT_DELETE:
464 			{
465 				int cdata_len = strlen(widget_data->cdata);
466 
467 				if (widget_data->info.field.cpos >= cdata_len) goto display_field;
468 
469 				memmove(widget_data->cdata + widget_data->info.field.cpos,
470 					widget_data->cdata + widget_data->info.field.cpos + 1,
471 					cdata_len - widget_data->info.field.cpos + 1);
472 				goto display_field;
473 			}
474 
475 		case ACT_EDIT_KILL_TO_BOL:
476 			memmove(widget_data->cdata,
477 					widget_data->cdata + widget_data->info.field.cpos,
478 					strlen(widget_data->cdata + widget_data->info.field.cpos) + 1);
479 			widget_data->info.field.cpos = 0;
480 			goto display_field;
481 
482 		case ACT_EDIT_KILL_TO_EOL:
483 			widget_data->cdata[widget_data->info.field.cpos] = 0;
484 			goto display_field;
485 
486 		case ACT_EDIT_COPY_CLIPBOARD:
487 			/* Copy to clipboard */
488 			set_clipboard_text(widget_data->cdata);
489 			return EVENT_PROCESSED;
490 
491 		case ACT_EDIT_CUT_CLIPBOARD:
492 			/* Cut to clipboard */
493 			set_clipboard_text(widget_data->cdata);
494 			widget_data->cdata[0] = 0;
495 			widget_data->info.field.cpos = 0;
496 			goto display_field;
497 
498 		case ACT_EDIT_PASTE_CLIPBOARD:
499 			{
500 				/* Paste from clipboard */
501 				unsigned char *clipboard = get_clipboard_text();
502 
503 				if (!clipboard) goto display_field;
504 
505 				safe_strncpy(widget_data->cdata, clipboard, widget_data->widget->datalen);
506 				widget_data->info.field.cpos = strlen(widget_data->cdata);
507 				mem_free(clipboard);
508 				goto display_field;
509 			}
510 
511 		case ACT_EDIT_AUTO_COMPLETE:
512 			if (!widget_has_history(widget_data))
513 				return EVENT_NOT_PROCESSED;
514 
515 			do_tab_compl(dlg_data, &widget_data->info.field.history);
516 			goto display_field;
517 
518 		case ACT_EDIT_AUTO_COMPLETE_FILE:
519 			if (!widget_has_history(widget_data))
520 				return EVENT_NOT_PROCESSED;
521 
522 			do_tab_compl_file(dlg_data, &widget_data->info.field.history);
523 			goto display_field;
524 
525 		case ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS:
526 			if (!widget_has_history(widget_data))
527 				return EVENT_NOT_PROCESSED;
528 
529 			do_tab_compl_unambiguous(dlg_data, &widget_data->info.field.history);
530 			goto display_field;
531 
532 		case ACT_EDIT_REDRAW:
533 			redraw_terminal_cls(term);
534 			return EVENT_PROCESSED;
535 
536 		default:
537 			if (check_kbd_textinput_key(ev)) {
538 				unsigned char *text = widget_data->cdata;
539 				int textlen = strlen(text);
540 
541 				if (textlen >= widget_data->widget->datalen - 1)
542 					goto display_field;
543 
544 				/* Shift to position of the cursor */
545 				textlen -= widget_data->info.field.cpos;
546 				text	+= widget_data->info.field.cpos++;
547 
548 				memmove(text + 1, text, textlen + 1);
549 				*text = get_kbd_key(ev);
550 
551 				goto display_field;
552 			}
553 	}
554 	return EVENT_NOT_PROCESSED;
555 
556 display_field:
557 	display_widget(dlg_data, widget_data);
558 	redraw_from_window(dlg_data->win);
559 	return EVENT_PROCESSED;
560 }
561 
562 
563 static widget_handler_status_T
clear_field(struct dialog_data * dlg_data,struct widget_data * widget_data)564 clear_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
565 {
566 	widget_data->info.field.cpos = 0;
567 
568 	if (widget_data->widget->datalen)
569 		memset(widget_data->cdata, 0, widget_data->widget->datalen);
570 
571 	return EVENT_PROCESSED;
572 }
573 
574 struct widget_ops field_ops = {
575 	display_field,
576 	init_field,
577 	mouse_field,
578 	kbd_field,
579 	NULL,
580 	clear_field,
581 };
582 
583 struct widget_ops field_pass_ops = {
584 	display_field_pass,
585 	init_field,
586 	mouse_field,
587 	kbd_field,
588 	NULL,
589 	clear_field,
590 };
591 
592 
593 /* Input lines */
594 
595 static void
input_line_layouter(struct dialog_data * dlg_data)596 input_line_layouter(struct dialog_data *dlg_data)
597 {
598 	struct input_line *input_line = dlg_data->dlg->udata;
599 	struct session *ses = input_line->ses;
600 	struct window *win = dlg_data->win;
601 	int y = win->term->height - 1
602 		- ses->status.show_status_bar
603 		- ses->status.show_tabs_bar;
604 
605 	dlg_format_field(win->term, dlg_data->widgets_data, 0,
606 			 &y, win->term->width, NULL, ALIGN_LEFT);
607 }
608 
609 static widget_handler_status_T
input_line_event_handler(struct dialog_data * dlg_data)610 input_line_event_handler(struct dialog_data *dlg_data)
611 {
612 	struct input_line *input_line = dlg_data->dlg->udata;
613 	input_line_handler_T handler = input_line->handler;
614 	enum edit_action action_id;
615 	struct widget_data *widget_data = dlg_data->widgets_data;
616 	struct term_event *ev = dlg_data->term_event;
617 
618 	/* Noodle time */
619 	switch (ev->ev) {
620 	case EVENT_KBD:
621 		action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
622 
623 		/* Handle some basic actions such as quiting for empty buffers */
624 		switch (action_id) {
625 		case ACT_EDIT_ENTER:
626 		case ACT_EDIT_NEXT_ITEM:
627 		case ACT_EDIT_PREVIOUS_ITEM:
628 			if (widget_has_history(widget_data))
629 				add_to_input_history(widget_data->widget->info.field.history,
630 						     input_line->buffer, 1);
631 			break;
632 
633 		case ACT_EDIT_BACKSPACE:
634 			if (!*input_line->buffer)
635 				goto cancel_input_line;
636 			break;
637 
638 		case ACT_EDIT_CANCEL:
639 			goto cancel_input_line;
640 
641 		default:
642 			break;
643 		}
644 
645 		/* First let the input field do its business */
646 		kbd_field(dlg_data, widget_data);
647 		break;
648 
649 	case EVENT_MOUSE:
650 #ifdef CONFIG_MOUSE
651 		if (ev->info.mouse.y != dlg_data->win->y) {
652 			delete_window_ev(dlg_data->win, ev);
653 			return EVENT_PROCESSED;
654 		}
655 #endif /* CONFIG_MOUSE */
656 		return EVENT_NOT_PROCESSED;
657 
658 	case EVENT_REDRAW:
659 		/* Try to catch the redraw event initiated by the history
660 		 * completion and only respond if something was actually
661 		 * updated. Meaning we have new data in the line buffer that
662 		 * should be propagated to the line handler. */
663 		if (!widget_has_history(widget_data)
664 		    || widget_data->info.field.cpos <= 0
665 		    || widget_data->info.field.cpos <= strlen(input_line->buffer))
666 			return EVENT_NOT_PROCESSED;
667 
668 		/* Fall thru */
669 
670 	case EVENT_RESIZE:
671 		action_id = ACT_EDIT_REDRAW;
672 		break;
673 
674 	default:
675 		return EVENT_NOT_PROCESSED;
676 	}
677 
678 	update_dialog_data(dlg_data);
679 
680 send_action_to_handler:
681 	/* Then pass it on to the specialized handler */
682 	switch (handler(input_line, action_id)) {
683 	case INPUT_LINE_CANCEL:
684 cancel_input_line:
685 		cancel_dialog(dlg_data, widget_data);
686 		break;
687 
688 	case INPUT_LINE_REWIND:
689 		/* This is stolen kbd_field() handling for ACT_EDIT_BACKSPACE */
690 		memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
691 			widget_data->cdata + widget_data->info.field.cpos,
692 			strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
693 		widget_data->info.field.cpos--;
694 
695 		update_dialog_data(dlg_data);
696 
697 		goto send_action_to_handler;
698 
699 	case INPUT_LINE_PROCEED:
700 		break;
701 	}
702 
703 	/* Hack: We want our caller to perform its redrawing routine,
704 	 * even if we did process the event here. */
705 	if (action_id == ACT_EDIT_REDRAW) return EVENT_NOT_PROCESSED;
706 
707 	/* Completely bypass any further dialog event handling */
708 	return EVENT_PROCESSED;
709 }
710 
711 void
input_field_line(struct session * ses,unsigned char * prompt,void * data,struct input_history * history,input_line_handler_T handler)712 input_field_line(struct session *ses, unsigned char *prompt, void *data,
713 		 struct input_history *history, input_line_handler_T handler)
714 {
715 	struct dialog *dlg;
716 	unsigned char *buffer;
717 	struct input_line *input_line;
718 
719 	assert(ses);
720 
721 	dlg = calloc_dialog(INPUT_LINE_WIDGETS, sizeof(*input_line));
722 	if (!dlg) return;
723 
724 	input_line = (void *) get_dialog_offset(dlg, INPUT_LINE_WIDGETS);
725 	input_line->ses = ses;
726 	input_line->data = data;
727 	input_line->handler = handler;
728 	buffer = input_line->buffer;
729 
730 	dlg->handle_event = input_line_event_handler;
731 	dlg->layouter = input_line_layouter;
732 	dlg->layout.only_widgets = 1;
733 	dlg->udata = input_line;
734 
735 	add_dlg_field_float2(dlg, prompt, 0, 0, NULL, INPUT_LINE_BUFFER_SIZE,
736 			     buffer, history);
737 
738 	do_dialog(ses->tab->term, dlg, getml(dlg, NULL));
739 }
740