big dialogs: Refactoring. do not pass the term.
[elinks.git] / src / bfu / inpfield.c
blob4a1bd43ab2f742bb063123ac082319309684a8c7
1 /* Input field widget implementation. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdlib.h>
9 #include <string.h>
11 #include "elinks.h"
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/charsets.h"
21 #include "intl/gettext/libintl.h"
22 #include "osdep/osdep.h"
23 #include "session/session.h"
24 #include "terminal/draw.h"
25 #include "terminal/kbd.h"
26 #include "terminal/mouse.h"
27 #include "terminal/terminal.h"
28 #include "terminal/window.h"
29 #include "util/color.h"
30 #include "util/memlist.h"
31 #include "util/memory.h"
33 #define INPUTFIELD_HEIGHT 1
35 #define INPUTFIELD_FLOATLABEL_PADDING 1
37 #define INPUTFIELD_FLOAT_SEPARATOR ":"
38 #define INPUTFIELD_FLOAT_SEPARATOR_LEN 1
40 void
41 add_dlg_field_do(struct dialog *dlg, enum widget_type type, unsigned char *label,
42 int min, int max, widget_handler_T *handler,
43 int datalen, void *data,
44 struct input_history *history, enum inpfield_flags flags)
46 struct widget *widget = &dlg->widgets[dlg->number_of_widgets++];
48 widget->type = type;
49 widget->text = label;
50 widget->handler = handler;
51 widget->datalen = datalen;
52 widget->data = data;
54 widget->info.field.history = history;
55 widget->info.field.flags = flags;
56 widget->info.field.min = min;
57 widget->info.field.max = max;
60 widget_handler_status_T
61 check_number(struct dialog_data *dlg_data, struct widget_data *widget_data)
63 struct widget *widget = widget_data->widget;
64 char *end;
65 long l;
67 errno = 0;
68 l = strtol(widget_data->cdata, &end, 10);
70 if (errno || !*widget_data->cdata || *end) {
71 info_box(dlg_data->win->term, 0,
72 N_("Bad number"), ALIGN_CENTER,
73 N_("Number expected in field"));
74 return EVENT_NOT_PROCESSED;
77 if (l < widget->info.field.min || l > widget->info.field.max) {
78 info_box(dlg_data->win->term, MSGBOX_FREE_TEXT,
79 N_("Bad number"), ALIGN_CENTER,
80 msg_text(dlg_data->win->term,
81 N_("Number should be in the range from %d to %d."),
82 widget->info.field.min, widget->info.field.max));
83 return EVENT_NOT_PROCESSED;
86 return EVENT_PROCESSED;
89 widget_handler_status_T
90 check_nonempty(struct dialog_data *dlg_data, struct widget_data *widget_data)
92 unsigned char *p;
94 for (p = widget_data->cdata; *p; p++)
95 if (*p > ' ')
96 return EVENT_PROCESSED;
98 info_box(dlg_data->win->term, 0,
99 N_("Bad string"), ALIGN_CENTER,
100 N_("Empty string not allowed"));
102 return EVENT_NOT_PROCESSED;
105 void
106 dlg_format_field(struct dialog_data *dlg_data,
107 struct widget_data *widget_data,
108 int x, int *y, int w, int *rw, enum format_align align, int format_only)
110 struct terminal *term = dlg_data->win->term;
111 static int max_label_width;
112 static int *prev_y; /* Assert the uniqueness of y */ /* TODO: get rid of this !! --Zas */
113 unsigned char *label = widget_data->widget->text;
114 struct color_pair *text_color = NULL;
115 int label_width = 0;
116 int float_label = widget_data->widget->info.field.flags & (INPFIELD_FLOAT|INPFIELD_FLOAT2);
118 if (label && *label && float_label) {
119 label_width = strlen(label);
120 if (prev_y == y) {
121 int_lower_bound(&max_label_width, label_width);
122 } else {
123 max_label_width = label_width;
124 prev_y = y;
127 /* Right align the floating label up against the
128 * input field */
129 x += max_label_width - label_width;
130 w -= max_label_width - label_width;
133 if (label && *label) {
134 if (!format_only) text_color = get_bfu_color(term, "dialog.text");
136 dlg_format_text_do(dlg_data, label, x, y, w, rw, text_color, ALIGN_LEFT, format_only);
139 /* XXX: We want the field and label on the same line if the terminal
140 * width allows it. */
141 if (label && *label && float_label) {
142 if (widget_data->widget->info.field.flags & INPFIELD_FLOAT) {
143 (*y) -= INPUTFIELD_HEIGHT;
144 dlg_format_text_do(dlg_data, INPUTFIELD_FLOAT_SEPARATOR,
145 x + label_width, y, w, rw,
146 text_color, ALIGN_LEFT, format_only);
147 w -= INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
148 x += INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
151 /* FIXME: Is 5 chars for input field enough? --jonas */
152 if (label_width < w - 5) {
153 (*y) -= INPUTFIELD_HEIGHT;
154 w -= label_width;
155 x += label_width;
159 if (rw) int_lower_bound(rw, int_min(w, DIALOG_MIN_WIDTH));
161 set_box(&widget_data->box, x, *y, w, INPUTFIELD_HEIGHT);
163 (*y) += INPUTFIELD_HEIGHT;
166 static widget_handler_status_T
167 input_field_cancel(struct dialog_data *dlg_data, struct widget_data *widget_data)
169 void (*fn)(void *) = widget_data->widget->data;
170 void *data = dlg_data->dlg->udata2;
172 if (fn) fn(data);
174 return cancel_dialog(dlg_data, widget_data);
177 static widget_handler_status_T
178 input_field_ok(struct dialog_data *dlg_data, struct widget_data *widget_data)
180 void (*fn)(void *, unsigned char *) = widget_data->widget->data;
181 void *data = dlg_data->dlg->udata2;
182 unsigned char *text = dlg_data->widgets_data->cdata;
184 if (check_dialog(dlg_data)) return EVENT_NOT_PROCESSED;
186 if (widget_has_history(dlg_data->widgets_data))
187 add_to_input_history(dlg_data->dlg->widgets->info.field.history,
188 text, 1);
190 if (fn) fn(data, text);
192 return cancel_dialog(dlg_data, widget_data);
195 void
196 input_field(struct terminal *term, struct memory_list *ml, int intl,
197 unsigned char *title,
198 unsigned char *text,
199 unsigned char *okbutton,
200 unsigned char *cancelbutton,
201 void *data, struct input_history *history, int l,
202 unsigned char *def, int min, int max,
203 widget_handler_T *check,
204 void (*fn)(void *, unsigned char *),
205 void (*cancelfn)(void *))
207 struct dialog *dlg;
208 unsigned char *field;
210 if (intl) {
211 title = _(title, term);
212 text = _(text, term);
213 okbutton = _(okbutton, term);
214 cancelbutton = _(cancelbutton, term);
217 #define INPUT_WIDGETS_COUNT 3
218 dlg = calloc_dialog(INPUT_WIDGETS_COUNT, l);
219 if (!dlg) return;
221 /* @field is automatically cleared by calloc() */
222 field = get_dialog_offset(dlg, INPUT_WIDGETS_COUNT);
224 if (def) {
225 int defsize = strlen(def) + 1;
227 memcpy(field, def, (defsize > l) ? l - 1 : defsize);
230 dlg->title = title;
231 dlg->layouter = generic_dialog_layouter;
232 dlg->layout.fit_datalen = 1;
233 dlg->udata2 = data;
235 add_dlg_field(dlg, text, min, max, check, l, field, history);
237 add_dlg_button(dlg, okbutton, B_ENTER, input_field_ok, fn);
238 add_dlg_button(dlg, cancelbutton, B_ESC, input_field_cancel, cancelfn);
240 add_dlg_end(dlg, INPUT_WIDGETS_COUNT);
242 add_to_ml(&ml, (void *) dlg, (void *) NULL);
243 do_dialog(term, dlg, ml);
246 void
247 input_dialog(struct terminal *term, struct memory_list *ml,
248 unsigned char *title,
249 unsigned char *text,
250 void *data, struct input_history *history, int l,
251 unsigned char *def, int min, int max,
252 widget_handler_T *check,
253 void (*fn)(void *, unsigned char *),
254 void (*cancelfn)(void *))
256 /* [gettext_accelerator_context(input_dialog)] */
257 input_field(term, ml, 1, title, text, N_("~OK"), N_("~Cancel"),
258 data, history, l,
259 def, min, max,
260 check, fn, cancelfn);
263 static widget_handler_status_T
264 display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data,
265 int hide)
267 struct terminal *term = dlg_data->win->term;
268 struct color_pair *color;
269 int sel = is_selected_widget(dlg_data, widget_data);
270 #ifdef CONFIG_UTF8
271 int len = 0, left = 0;
272 #endif /* CONFIG_UTF8 */
274 #ifdef CONFIG_UTF8
275 if (term->utf8_cp) {
276 unsigned char *t = widget_data->cdata;
277 int p = widget_data->info.field.cpos;
279 len = utf8_ptr2cells(t, &t[p]);
280 int_bounds(&left, len - widget_data->box.width + 1, len);
281 int_lower_bound(&left, 0);
282 widget_data->info.field.vpos = utf8_cells2bytes(t, left, NULL);
283 } else
284 #endif /* CONFIG_UTF8 */
286 int_bounds(&widget_data->info.field.vpos,
287 widget_data->info.field.cpos - widget_data->box.width + 1,
288 widget_data->info.field.cpos);
289 int_lower_bound(&widget_data->info.field.vpos, 0);
292 color = get_bfu_color(term, "dialog.field");
293 if (color)
294 draw_box(term, &widget_data->box, ' ', 0, color);
296 color = get_bfu_color(term, "dialog.field-text");
297 if (color) {
298 unsigned char *text = widget_data->cdata + widget_data->info.field.vpos;
299 int len, w;
301 #ifdef CONFIG_UTF8
302 if (term->utf8_cp && !hide)
303 len = utf8_ptr2cells(text, NULL);
304 else if (term->utf8_cp)
305 len = utf8_ptr2chars(text, NULL);
306 else
307 #endif /* CONFIG_UTF8 */
308 len = strlen(text);
309 w = int_min(len, widget_data->box.width);
311 if (!hide) {
312 #ifdef CONFIG_UTF8
313 if (term->utf8_cp)
314 w = utf8_cells2bytes(text, w, NULL);
315 #endif /* CONFIG_UTF8 */
316 draw_dlg_text(term, dlg_data, widget_data->box.x, widget_data->box.y,
317 text, w, 0, color);
318 } else {
319 struct box box;
321 copy_box(&box, &widget_data->box);
322 box.width = w;
324 draw_box(term, &box, '*', 0, color);
328 if (sel) {
329 int x;
331 #ifdef CONFIG_UTF8
332 if (term->utf8_cp)
333 x = widget_data->box.x + len - left;
334 else
335 #endif /* CONFIG_UTF8 */
336 x = widget_data->box.x + widget_data->info.field.cpos - widget_data->info.field.vpos;
338 set_cursor(term, x, widget_data->box.y, 0);
339 set_window_ptr(dlg_data->win, widget_data->box.x, widget_data->box.y);
342 return EVENT_PROCESSED;
345 static widget_handler_status_T
346 display_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
348 return display_field_do(dlg_data, widget_data, 0);
351 static widget_handler_status_T
352 display_field_pass(struct dialog_data *dlg_data, struct widget_data *widget_data)
354 return display_field_do(dlg_data, widget_data, 1);
357 static widget_handler_status_T
358 init_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
360 if (widget_has_history(widget_data)) {
361 struct input_history_entry *entry;
363 foreach (entry, widget_data->widget->info.field.history->entries) {
364 int datalen = strlen(entry->data);
365 struct input_history_entry *new_entry;
367 /* One byte is reserved in struct input_history_entry. */
368 new_entry = mem_alloc(sizeof(*new_entry) + datalen);
369 if (!new_entry) continue;
371 memcpy(new_entry->data, entry->data, datalen + 1);
372 add_to_list(widget_data->info.field.history, new_entry);
376 widget_data->info.field.cpos = strlen(widget_data->cdata);
377 return EVENT_PROCESSED;
380 static int
381 field_prev_history(struct widget_data *widget_data)
383 if (widget_has_history(widget_data)
384 && (void *) widget_data->info.field.cur_hist->prev != &widget_data->info.field.history) {
385 widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->prev;
386 dlg_set_history(widget_data);
387 return 1;
389 return 0;
392 static int
393 field_next_history(struct widget_data *widget_data)
395 if (widget_has_history(widget_data)
396 && (void *) widget_data->info.field.cur_hist != &widget_data->info.field.history) {
397 widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->next;
398 dlg_set_history(widget_data);
399 return 1;
401 return 0;
404 static widget_handler_status_T
405 mouse_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
407 struct term_event *ev = dlg_data->term_event;
409 if (!check_mouse_position(ev, &widget_data->box))
410 return EVENT_NOT_PROCESSED;
412 /* Handle navigation through history (if any) using up/down mouse wheel */
413 switch (get_mouse_button(ev)) {
414 case B_WHEEL_UP:
415 if (check_mouse_action(ev, B_DOWN)) {
416 if (field_prev_history(widget_data)) {
417 select_widget(dlg_data, widget_data);
418 return EVENT_PROCESSED;
421 return EVENT_NOT_PROCESSED;
423 case B_WHEEL_DOWN:
424 if (check_mouse_action(ev, B_DOWN)) {
425 if (field_next_history(widget_data)) {
426 select_widget(dlg_data, widget_data);
427 return EVENT_PROCESSED;
430 return EVENT_NOT_PROCESSED;
433 /* Place text cursor at mouse position and focus the widget. */
434 widget_data->info.field.cpos = widget_data->info.field.vpos
435 + ev->info.mouse.x - widget_data->box.x;
436 int_upper_bound(&widget_data->info.field.cpos, strlen(widget_data->cdata));
438 select_widget(dlg_data, widget_data);
439 return EVENT_PROCESSED;
442 static widget_handler_status_T
443 kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
445 struct window *win = dlg_data->win;
446 struct terminal *term = win->term;
447 struct term_event *ev = dlg_data->term_event;
448 action_id_T action_id;
450 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
451 if (action_id != -1
452 && !action_is_anonymous_safe(KEYMAP_EDIT, action_id)
453 && get_cmd_opt_bool("anonymous"))
454 return EVENT_NOT_PROCESSED;
456 switch (action_id) {
457 case ACT_EDIT_UP:
458 if (!widget_has_history(widget_data))
459 return EVENT_NOT_PROCESSED;
461 if (field_prev_history(widget_data)) {
462 goto display_field;
464 break;
466 case ACT_EDIT_DOWN:
467 if (!widget_has_history(widget_data))
468 return EVENT_NOT_PROCESSED;
470 if (field_next_history(widget_data)) {
471 goto display_field;
473 break;
475 case ACT_EDIT_RIGHT:
476 if (widget_data->info.field.cpos < strlen(widget_data->cdata)) {
477 #ifdef CONFIG_UTF8
478 if (term->utf8_cp) {
479 unsigned char *next = widget_data->cdata + widget_data->info.field.cpos;
480 unsigned char *end = strchr(next, '\0');
482 utf8_to_unicode(&next, end);
483 widget_data->info.field.cpos = (int)(next - widget_data->cdata);
484 } else
485 #endif /* CONFIG_UTF8 */
487 widget_data->info.field.cpos++;
490 goto display_field;
492 case ACT_EDIT_LEFT:
493 if (widget_data->info.field.cpos > 0)
494 widget_data->info.field.cpos--;
495 #ifdef CONFIG_UTF8
496 if (widget_data->info.field.cpos && term->utf8_cp) {
497 unsigned char *t = widget_data->cdata;
498 unsigned char *t2 = t;
499 int p = widget_data->info.field.cpos;
500 unsigned char tmp = t[p];
502 t[p] = '\0';
503 strlen_utf8(&t2);
504 t[p] = tmp;
505 widget_data->info.field.cpos = (int)(t2 - t);
508 #endif /* CONFIG_UTF8 */
509 goto display_field;
511 case ACT_EDIT_HOME:
512 widget_data->info.field.cpos = 0;
513 goto display_field;
515 case ACT_EDIT_END:
516 widget_data->info.field.cpos = strlen(widget_data->cdata);
517 goto display_field;
519 case ACT_EDIT_BACKSPACE:
520 #ifdef CONFIG_UTF8
521 if (widget_data->info.field.cpos && term->utf8_cp) {
522 /* XXX: stolen from src/viewer/text/form.c */
523 /* FIXME: This isn't nice. We remove last byte
524 * from UTF-8 character to detect
525 * character before it. */
526 unsigned char *text = widget_data->cdata;
527 unsigned char *end = widget_data->cdata + widget_data->info.field.cpos - 1;
528 unicode_val_T data;
529 int old = widget_data->info.field.cpos;
531 while(1) {
532 data = utf8_to_unicode(&text, end);
533 if (data == UCS_NO_CHAR)
534 break;
537 widget_data->info.field.cpos = (int)(text - widget_data->cdata);
538 if (old != widget_data->info.field.cpos) {
539 int length;
541 text = widget_data->cdata;
542 length = strlen(text + old) + 1;
543 memmove(text + widget_data->info.field.cpos, text + old, length);
545 goto display_field;
547 #endif /* CONFIG_UTF8 */
548 if (widget_data->info.field.cpos) {
549 memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
550 widget_data->cdata + widget_data->info.field.cpos,
551 strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
552 widget_data->info.field.cpos--;
554 goto display_field;
556 case ACT_EDIT_DELETE:
558 int cdata_len = strlen(widget_data->cdata);
560 if (widget_data->info.field.cpos >= cdata_len) goto display_field;
562 #ifdef CONFIG_UTF8
563 if (term->utf8_cp) {
564 unsigned char *end = widget_data->cdata + cdata_len;
565 unsigned char *text = widget_data->cdata + widget_data->info.field.cpos;
566 unsigned char *old = text;
568 utf8_to_unicode(&text, end);
569 if (old != text) {
570 memmove(old, text,
571 (int)(end - text) + 1);
573 goto display_field;
575 #endif /* CONFIG_UTF8 */
576 memmove(widget_data->cdata + widget_data->info.field.cpos,
577 widget_data->cdata + widget_data->info.field.cpos + 1,
578 cdata_len - widget_data->info.field.cpos + 1);
579 goto display_field;
582 case ACT_EDIT_KILL_TO_BOL:
583 memmove(widget_data->cdata,
584 widget_data->cdata + widget_data->info.field.cpos,
585 strlen(widget_data->cdata + widget_data->info.field.cpos) + 1);
586 widget_data->info.field.cpos = 0;
587 goto display_field;
589 case ACT_EDIT_KILL_TO_EOL:
590 widget_data->cdata[widget_data->info.field.cpos] = 0;
591 goto display_field;
593 case ACT_EDIT_KILL_WORD_BACK:
595 int cdata_len = strlen(widget_data->cdata);
596 int start = widget_data->info.field.cpos;
598 while (start > 0 && isspace(widget_data->cdata[start - 1]))
599 --start;
600 while (start > 0 && !isspace(widget_data->cdata[start - 1]))
601 --start;
603 memmove(widget_data->cdata + start,
604 widget_data->cdata + widget_data->info.field.cpos,
605 cdata_len - widget_data->info.field.cpos + 1);
607 widget_data->info.field.cpos = start;
609 goto display_field;
612 case ACT_EDIT_MOVE_BACKWARD_WORD:
613 while (widget_data->info.field.cpos > 0 && isspace(widget_data->cdata[widget_data->info.field.cpos - 1]))
614 --widget_data->info.field.cpos;
615 while (widget_data->info.field.cpos > 0 && !isspace(widget_data->cdata[widget_data->info.field.cpos - 1]))
616 --widget_data->info.field.cpos;
618 goto display_field;
620 case ACT_EDIT_MOVE_FORWARD_WORD:
621 while (isspace(widget_data->cdata[widget_data->info.field.cpos]))
622 ++widget_data->info.field.cpos;
623 while (widget_data->cdata[widget_data->info.field.cpos] && !isspace(widget_data->cdata[widget_data->info.field.cpos]))
624 ++widget_data->info.field.cpos;
625 while (isspace(widget_data->cdata[widget_data->info.field.cpos]))
626 ++widget_data->info.field.cpos;
628 goto display_field;
630 case ACT_EDIT_COPY_CLIPBOARD:
631 /* Copy to clipboard */
632 set_clipboard_text(widget_data->cdata);
633 return EVENT_PROCESSED;
635 case ACT_EDIT_CUT_CLIPBOARD:
636 /* Cut to clipboard */
637 set_clipboard_text(widget_data->cdata);
638 widget_data->cdata[0] = 0;
639 widget_data->info.field.cpos = 0;
640 goto display_field;
642 case ACT_EDIT_PASTE_CLIPBOARD:
644 /* Paste from clipboard */
645 unsigned char *clipboard = get_clipboard_text();
647 if (!clipboard) goto display_field;
649 safe_strncpy(widget_data->cdata, clipboard, widget_data->widget->datalen);
650 widget_data->info.field.cpos = strlen(widget_data->cdata);
651 mem_free(clipboard);
652 goto display_field;
655 case ACT_EDIT_AUTO_COMPLETE:
656 if (!widget_has_history(widget_data))
657 return EVENT_NOT_PROCESSED;
659 do_tab_compl(dlg_data, &widget_data->info.field.history);
660 goto display_field;
662 case ACT_EDIT_AUTO_COMPLETE_FILE:
663 if (!widget_has_history(widget_data))
664 return EVENT_NOT_PROCESSED;
666 do_tab_compl_file(dlg_data, &widget_data->info.field.history);
667 goto display_field;
669 case ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS:
670 if (!widget_has_history(widget_data))
671 return EVENT_NOT_PROCESSED;
673 do_tab_compl_unambiguous(dlg_data, &widget_data->info.field.history);
674 goto display_field;
676 case ACT_EDIT_REDRAW:
677 redraw_terminal_cls(term);
678 return EVENT_PROCESSED;
680 default:
681 if (check_kbd_textinput_key(ev)) {
682 unsigned char *text = widget_data->cdata;
683 int textlen = strlen(text);
684 #ifndef CONFIG_UTF8
685 /* Both get_kbd_key(ev) and @text
686 * are in the terminal's charset. */
687 const int inslen = 1;
688 #else /* CONFIG_UTF8 */
689 const unsigned char *ins;
690 int inslen;
692 /* get_kbd_key(ev) is UCS-4, and @text
693 * is in the terminal's charset. */
694 ins = u2cp_no_nbsp(get_kbd_key(ev),
695 get_opt_codepage_tree(term->spec,
696 "charset",
697 NULL));
698 inslen = strlen(ins);
699 #endif /* CONFIG_UTF8 */
701 if (textlen >= widget_data->widget->datalen - inslen)
702 goto display_field;
704 /* Shift to position of the cursor */
705 textlen -= widget_data->info.field.cpos;
706 text += widget_data->info.field.cpos;
708 memmove(text + inslen, text, textlen + 1);
709 #ifdef CONFIG_UTF8
710 memcpy(text, ins, inslen);
711 #else /* !CONFIG_UTF8 */
712 *text = get_kbd_key(ev);
713 #endif /* !CONFIG_UTF8 */
714 widget_data->info.field.cpos += inslen;
715 goto display_field;
718 return EVENT_NOT_PROCESSED;
720 display_field:
721 display_widget(dlg_data, widget_data);
722 redraw_from_window(dlg_data->win);
723 return EVENT_PROCESSED;
727 static widget_handler_status_T
728 clear_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
730 widget_data->info.field.cpos = 0;
732 if (widget_data->widget->datalen)
733 memset(widget_data->cdata, 0, widget_data->widget->datalen);
735 return EVENT_PROCESSED;
738 const struct widget_ops field_ops = {
739 display_field,
740 init_field,
741 mouse_field,
742 kbd_field,
743 NULL,
744 clear_field,
747 const struct widget_ops field_pass_ops = {
748 display_field_pass,
749 init_field,
750 mouse_field,
751 kbd_field,
752 NULL,
753 clear_field,
757 /* Input lines */
759 static void
760 input_line_layouter(struct dialog_data *dlg_data)
762 struct input_line *input_line = dlg_data->dlg->udata;
763 struct session *ses = input_line->ses;
764 struct window *win = dlg_data->win;
765 int y = win->term->height - 1
766 - ses->status.show_status_bar
767 - ses->status.show_tabs_bar;
769 dlg_format_field(dlg_data, dlg_data->widgets_data, 0,
770 &y, win->term->width, NULL, ALIGN_LEFT, 0);
773 static widget_handler_status_T
774 input_line_event_handler(struct dialog_data *dlg_data)
776 struct input_line *input_line = dlg_data->dlg->udata;
777 input_line_handler_T handler = input_line->handler;
778 enum edit_action action_id;
779 struct widget_data *widget_data = dlg_data->widgets_data;
780 struct term_event *ev = dlg_data->term_event;
782 /* Noodle time */
783 switch (ev->ev) {
784 case EVENT_KBD:
785 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
787 /* Handle some basic actions such as quiting for empty buffers */
788 switch (action_id) {
789 case ACT_EDIT_ENTER:
790 case ACT_EDIT_NEXT_ITEM:
791 case ACT_EDIT_PREVIOUS_ITEM:
792 if (widget_has_history(widget_data))
793 add_to_input_history(widget_data->widget->info.field.history,
794 input_line->buffer, 1);
795 break;
797 case ACT_EDIT_BACKSPACE:
798 if (!*input_line->buffer)
799 goto cancel_input_line;
800 break;
802 case ACT_EDIT_CANCEL:
803 goto cancel_input_line;
805 default:
806 break;
809 /* First let the input field do its business */
810 kbd_field(dlg_data, widget_data);
811 break;
813 case EVENT_MOUSE:
814 #ifdef CONFIG_MOUSE
815 if (ev->info.mouse.y != dlg_data->win->y) {
816 delete_window_ev(dlg_data->win, ev);
817 return EVENT_PROCESSED;
819 #endif /* CONFIG_MOUSE */
820 return EVENT_NOT_PROCESSED;
822 case EVENT_REDRAW:
823 /* Try to catch the redraw event initiated by the history
824 * completion and only respond if something was actually
825 * updated. Meaning we have new data in the line buffer that
826 * should be propagated to the line handler. */
827 if (!widget_has_history(widget_data)
828 || widget_data->info.field.cpos <= 0
829 || widget_data->info.field.cpos <= strlen(input_line->buffer))
830 return EVENT_NOT_PROCESSED;
832 /* Fall thru */
834 case EVENT_RESIZE:
835 action_id = ACT_EDIT_REDRAW;
836 break;
838 default:
839 return EVENT_NOT_PROCESSED;
842 update_dialog_data(dlg_data);
844 send_action_to_handler:
845 /* Then pass it on to the specialized handler */
846 switch (handler(input_line, action_id)) {
847 case INPUT_LINE_CANCEL:
848 cancel_input_line:
849 cancel_dialog(dlg_data, widget_data);
850 break;
852 case INPUT_LINE_REWIND:
853 /* This is stolen kbd_field() handling for ACT_EDIT_BACKSPACE */
854 memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
855 widget_data->cdata + widget_data->info.field.cpos,
856 strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
857 widget_data->info.field.cpos--;
859 update_dialog_data(dlg_data);
861 goto send_action_to_handler;
863 case INPUT_LINE_PROCEED:
864 break;
867 /* Hack: We want our caller to perform its redrawing routine,
868 * even if we did process the event here. */
869 if (action_id == ACT_EDIT_REDRAW) return EVENT_NOT_PROCESSED;
871 /* Completely bypass any further dialog event handling */
872 return EVENT_PROCESSED;
875 void
876 input_field_line(struct session *ses, unsigned char *prompt, void *data,
877 struct input_history *history, input_line_handler_T handler)
879 struct dialog *dlg;
880 unsigned char *buffer;
881 struct input_line *input_line;
883 assert(ses);
885 dlg = calloc_dialog(INPUT_LINE_WIDGETS, sizeof(*input_line));
886 if (!dlg) return;
888 input_line = (void *) get_dialog_offset(dlg, INPUT_LINE_WIDGETS);
889 input_line->ses = ses;
890 input_line->data = data;
891 input_line->handler = handler;
892 buffer = input_line->buffer;
894 dlg->handle_event = input_line_event_handler;
895 dlg->layouter = input_line_layouter;
896 dlg->layout.only_widgets = 1;
897 dlg->udata = input_line;
899 add_dlg_field_float2(dlg, prompt, 0, 0, NULL, INPUT_LINE_BUFFER_SIZE,
900 buffer, history);
902 do_dialog(ses->tab->term, dlg, getml(dlg, (void *) NULL));