bug 153, 1066: Convert bookmarks to/from UTF-8 when searching.
[elinks.git] / src / bfu / inpfield.c
blob4c0dcd2e4b7e5185a48ddfd918c8afad2c8e2a32
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 terminal *term,
107 struct widget_data *widget_data,
108 int x, int *y, int w, int *rw, enum format_align align, int format_only)
110 static int max_label_width;
111 static int *prev_y; /* Assert the uniqueness of y */ /* TODO: get rid of this !! --Zas */
112 unsigned char *label = widget_data->widget->text;
113 struct color_pair *text_color = NULL;
114 int label_width = 0;
115 int float_label = widget_data->widget->info.field.flags & (INPFIELD_FLOAT|INPFIELD_FLOAT2);
117 if (label && *label && float_label) {
118 label_width = strlen(label);
119 if (prev_y == y) {
120 int_lower_bound(&max_label_width, label_width);
121 } else {
122 max_label_width = label_width;
123 prev_y = y;
126 /* Right align the floating label up against the
127 * input field */
128 x += max_label_width - label_width;
129 w -= max_label_width - label_width;
132 if (label && *label) {
133 if (!format_only) text_color = get_bfu_color(term, "dialog.text");
135 dlg_format_text_do(term, label, x, y, w, rw, text_color, ALIGN_LEFT, format_only);
138 /* XXX: We want the field and label on the same line if the terminal
139 * width allows it. */
140 if (label && *label && float_label) {
141 if (widget_data->widget->info.field.flags & INPFIELD_FLOAT) {
142 (*y) -= INPUTFIELD_HEIGHT;
143 dlg_format_text_do(term, INPUTFIELD_FLOAT_SEPARATOR,
144 x + label_width, y, w, rw,
145 text_color, ALIGN_LEFT, format_only);
146 w -= INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
147 x += INPUTFIELD_FLOAT_SEPARATOR_LEN + INPUTFIELD_FLOATLABEL_PADDING;
150 /* FIXME: Is 5 chars for input field enough? --jonas */
151 if (label_width < w - 5) {
152 (*y) -= INPUTFIELD_HEIGHT;
153 w -= label_width;
154 x += label_width;
158 if (rw) int_lower_bound(rw, int_min(w, DIALOG_MIN_WIDTH));
160 set_box(&widget_data->box, x, *y, w, INPUTFIELD_HEIGHT);
162 (*y) += INPUTFIELD_HEIGHT;
165 static widget_handler_status_T
166 input_field_cancel(struct dialog_data *dlg_data, struct widget_data *widget_data)
168 void (*fn)(void *) = widget_data->widget->data;
169 void *data = dlg_data->dlg->udata2;
171 if (fn) fn(data);
173 return cancel_dialog(dlg_data, widget_data);
176 static widget_handler_status_T
177 input_field_ok(struct dialog_data *dlg_data, struct widget_data *widget_data)
179 void (*fn)(void *, unsigned char *) = widget_data->widget->data;
180 void *data = dlg_data->dlg->udata2;
181 unsigned char *text = dlg_data->widgets_data->cdata;
183 if (check_dialog(dlg_data)) return EVENT_NOT_PROCESSED;
185 if (widget_has_history(dlg_data->widgets_data))
186 add_to_input_history(dlg_data->dlg->widgets->info.field.history,
187 text, 1);
189 if (fn) fn(data, text);
191 return cancel_dialog(dlg_data, widget_data);
194 void
195 input_field(struct terminal *term, struct memory_list *ml, int intl,
196 unsigned char *title,
197 unsigned char *text,
198 unsigned char *okbutton,
199 unsigned char *cancelbutton,
200 void *data, struct input_history *history, int l,
201 unsigned char *def, int min, int max,
202 widget_handler_T *check,
203 void (*fn)(void *, unsigned char *),
204 void (*cancelfn)(void *))
206 struct dialog *dlg;
207 unsigned char *field;
209 if (intl) {
210 title = _(title, term);
211 text = _(text, term);
212 okbutton = _(okbutton, term);
213 cancelbutton = _(cancelbutton, term);
216 #define INPUT_WIDGETS_COUNT 3
217 dlg = calloc_dialog(INPUT_WIDGETS_COUNT, l);
218 if (!dlg) return;
220 /* @field is automatically cleared by calloc() */
221 field = get_dialog_offset(dlg, INPUT_WIDGETS_COUNT);
223 if (def) {
224 int defsize = strlen(def) + 1;
226 memcpy(field, def, (defsize > l) ? l - 1 : defsize);
229 dlg->title = title;
230 dlg->layouter = generic_dialog_layouter;
231 dlg->layout.fit_datalen = 1;
232 dlg->udata2 = data;
234 add_dlg_field(dlg, text, min, max, check, l, field, history);
236 add_dlg_button(dlg, okbutton, B_ENTER, input_field_ok, fn);
237 add_dlg_button(dlg, cancelbutton, B_ESC, input_field_cancel, cancelfn);
239 add_dlg_end(dlg, INPUT_WIDGETS_COUNT);
241 add_to_ml(&ml, (void *) dlg, (void *) NULL);
242 do_dialog(term, dlg, ml);
245 void
246 input_dialog(struct terminal *term, struct memory_list *ml,
247 unsigned char *title,
248 unsigned char *text,
249 void *data, struct input_history *history, int l,
250 unsigned char *def, int min, int max,
251 widget_handler_T *check,
252 void (*fn)(void *, unsigned char *),
253 void (*cancelfn)(void *))
255 /* [gettext_accelerator_context(input_dialog)] */
256 input_field(term, ml, 1, title, text, N_("~OK"), N_("~Cancel"),
257 data, history, l,
258 def, min, max,
259 check, fn, cancelfn);
262 static widget_handler_status_T
263 display_field_do(struct dialog_data *dlg_data, struct widget_data *widget_data,
264 int hide)
266 struct terminal *term = dlg_data->win->term;
267 struct color_pair *color;
268 int sel = is_selected_widget(dlg_data, widget_data);
269 #ifdef CONFIG_UTF8
270 int len = 0, left = 0;
271 #endif /* CONFIG_UTF8 */
273 #ifdef CONFIG_UTF8
274 if (term->utf8_cp) {
275 unsigned char *t = widget_data->cdata;
276 int p = widget_data->info.field.cpos;
278 len = utf8_ptr2cells(t, &t[p]);
279 int_bounds(&left, len - widget_data->box.width + 1, len);
280 int_lower_bound(&left, 0);
281 widget_data->info.field.vpos = utf8_cells2bytes(t, left, NULL);
282 } else
283 #endif /* CONFIG_UTF8 */
285 int_bounds(&widget_data->info.field.vpos,
286 widget_data->info.field.cpos - widget_data->box.width + 1,
287 widget_data->info.field.cpos);
288 int_lower_bound(&widget_data->info.field.vpos, 0);
291 color = get_bfu_color(term, "dialog.field");
292 if (color)
293 draw_box(term, &widget_data->box, ' ', 0, color);
295 color = get_bfu_color(term, "dialog.field-text");
296 if (color) {
297 unsigned char *text = widget_data->cdata + widget_data->info.field.vpos;
298 int len, w;
300 #ifdef CONFIG_UTF8
301 if (term->utf8_cp && !hide)
302 len = utf8_ptr2cells(text, NULL);
303 else if (term->utf8_cp)
304 len = utf8_ptr2chars(text, NULL);
305 else
306 #endif /* CONFIG_UTF8 */
307 len = strlen(text);
308 w = int_min(len, widget_data->box.width);
310 if (!hide) {
311 #ifdef CONFIG_UTF8
312 if (term->utf8_cp)
313 w = utf8_cells2bytes(text, w, NULL);
314 #endif /* CONFIG_UTF8 */
315 draw_text(term, widget_data->box.x, widget_data->box.y,
316 text, w, 0, color);
317 } else {
318 struct box box;
320 copy_box(&box, &widget_data->box);
321 box.width = w;
323 draw_box(term, &box, '*', 0, color);
327 if (sel) {
328 int x;
330 #ifdef CONFIG_UTF8
331 if (term->utf8_cp)
332 x = widget_data->box.x + len - left;
333 else
334 #endif /* CONFIG_UTF8 */
335 x = widget_data->box.x + widget_data->info.field.cpos - widget_data->info.field.vpos;
337 set_cursor(term, x, widget_data->box.y, 0);
338 set_window_ptr(dlg_data->win, widget_data->box.x, widget_data->box.y);
341 return EVENT_PROCESSED;
344 static widget_handler_status_T
345 display_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
347 return display_field_do(dlg_data, widget_data, 0);
350 static widget_handler_status_T
351 display_field_pass(struct dialog_data *dlg_data, struct widget_data *widget_data)
353 return display_field_do(dlg_data, widget_data, 1);
356 static widget_handler_status_T
357 init_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
359 if (widget_has_history(widget_data)) {
360 struct input_history_entry *entry;
362 foreach (entry, widget_data->widget->info.field.history->entries) {
363 int datalen = strlen(entry->data);
364 struct input_history_entry *new_entry;
366 /* One byte is reserved in struct input_history_entry. */
367 new_entry = mem_alloc(sizeof(*new_entry) + datalen);
368 if (!new_entry) continue;
370 memcpy(new_entry->data, entry->data, datalen + 1);
371 add_to_list(widget_data->info.field.history, new_entry);
375 widget_data->info.field.cpos = strlen(widget_data->cdata);
376 return EVENT_PROCESSED;
379 static int
380 field_prev_history(struct widget_data *widget_data)
382 if (widget_has_history(widget_data)
383 && (void *) widget_data->info.field.cur_hist->prev != &widget_data->info.field.history) {
384 widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->prev;
385 dlg_set_history(widget_data);
386 return 1;
388 return 0;
391 static int
392 field_next_history(struct widget_data *widget_data)
394 if (widget_has_history(widget_data)
395 && (void *) widget_data->info.field.cur_hist != &widget_data->info.field.history) {
396 widget_data->info.field.cur_hist = widget_data->info.field.cur_hist->next;
397 dlg_set_history(widget_data);
398 return 1;
400 return 0;
403 static widget_handler_status_T
404 mouse_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
406 struct term_event *ev = dlg_data->term_event;
408 if (!check_mouse_position(ev, &widget_data->box))
409 return EVENT_NOT_PROCESSED;
411 /* Handle navigation through history (if any) using up/down mouse wheel */
412 switch (get_mouse_button(ev)) {
413 case B_WHEEL_UP:
414 if (check_mouse_action(ev, B_DOWN)) {
415 if (field_prev_history(widget_data)) {
416 select_widget(dlg_data, widget_data);
417 return EVENT_PROCESSED;
420 return EVENT_NOT_PROCESSED;
422 case B_WHEEL_DOWN:
423 if (check_mouse_action(ev, B_DOWN)) {
424 if (field_next_history(widget_data)) {
425 select_widget(dlg_data, widget_data);
426 return EVENT_PROCESSED;
429 return EVENT_NOT_PROCESSED;
432 /* Place text cursor at mouse position and focus the widget. */
433 widget_data->info.field.cpos = widget_data->info.field.vpos
434 + ev->info.mouse.x - widget_data->box.x;
435 int_upper_bound(&widget_data->info.field.cpos, strlen(widget_data->cdata));
437 select_widget(dlg_data, widget_data);
438 return EVENT_PROCESSED;
441 static widget_handler_status_T
442 kbd_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
444 struct window *win = dlg_data->win;
445 struct terminal *term = win->term;
446 struct term_event *ev = dlg_data->term_event;
447 action_id_T action_id;
449 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
450 if (action_id != -1
451 && !action_is_anonymous_safe(KEYMAP_EDIT, action_id)
452 && get_cmd_opt_bool("anonymous"))
453 return EVENT_NOT_PROCESSED;
455 switch (action_id) {
456 case ACT_EDIT_UP:
457 if (!widget_has_history(widget_data))
458 return EVENT_NOT_PROCESSED;
460 if (field_prev_history(widget_data)) {
461 goto display_field;
463 break;
465 case ACT_EDIT_DOWN:
466 if (!widget_has_history(widget_data))
467 return EVENT_NOT_PROCESSED;
469 if (field_next_history(widget_data)) {
470 goto display_field;
472 break;
474 case ACT_EDIT_RIGHT:
475 if (widget_data->info.field.cpos < strlen(widget_data->cdata)) {
476 #ifdef CONFIG_UTF8
477 if (term->utf8_cp) {
478 unsigned char *next = widget_data->cdata + widget_data->info.field.cpos;
479 unsigned char *end = strchr(next, '\0');
481 utf8_to_unicode(&next, end);
482 widget_data->info.field.cpos = (int)(next - widget_data->cdata);
483 } else
484 #endif /* CONFIG_UTF8 */
486 widget_data->info.field.cpos++;
489 goto display_field;
491 case ACT_EDIT_LEFT:
492 if (widget_data->info.field.cpos > 0)
493 widget_data->info.field.cpos--;
494 #ifdef CONFIG_UTF8
495 if (widget_data->info.field.cpos && term->utf8_cp) {
496 unsigned char *t = widget_data->cdata;
497 unsigned char *t2 = t;
498 int p = widget_data->info.field.cpos;
499 unsigned char tmp = t[p];
501 t[p] = '\0';
502 strlen_utf8(&t2);
503 t[p] = tmp;
504 widget_data->info.field.cpos = (int)(t2 - t);
507 #endif /* CONFIG_UTF8 */
508 goto display_field;
510 case ACT_EDIT_HOME:
511 widget_data->info.field.cpos = 0;
512 goto display_field;
514 case ACT_EDIT_END:
515 widget_data->info.field.cpos = strlen(widget_data->cdata);
516 goto display_field;
518 case ACT_EDIT_BACKSPACE:
519 #ifdef CONFIG_UTF8
520 if (widget_data->info.field.cpos && term->utf8_cp) {
521 /* XXX: stolen from src/viewer/text/form.c */
522 /* FIXME: This isn't nice. We remove last byte
523 * from UTF-8 character to detect
524 * character before it. */
525 unsigned char *text = widget_data->cdata;
526 unsigned char *end = widget_data->cdata + widget_data->info.field.cpos - 1;
527 unicode_val_T data;
528 int old = widget_data->info.field.cpos;
530 while(1) {
531 data = utf8_to_unicode(&text, end);
532 if (data == UCS_NO_CHAR)
533 break;
536 widget_data->info.field.cpos = (int)(text - widget_data->cdata);
537 if (old != widget_data->info.field.cpos) {
538 int length;
540 text = widget_data->cdata;
541 length = strlen(text + old) + 1;
542 memmove(text + widget_data->info.field.cpos, text + old, length);
544 goto display_field;
546 #endif /* CONFIG_UTF8 */
547 if (widget_data->info.field.cpos) {
548 memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
549 widget_data->cdata + widget_data->info.field.cpos,
550 strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
551 widget_data->info.field.cpos--;
553 goto display_field;
555 case ACT_EDIT_DELETE:
557 int cdata_len = strlen(widget_data->cdata);
559 if (widget_data->info.field.cpos >= cdata_len) goto display_field;
561 #ifdef CONFIG_UTF8
562 if (term->utf8_cp) {
563 unsigned char *end = widget_data->cdata + cdata_len;
564 unsigned char *text = widget_data->cdata + widget_data->info.field.cpos;
565 unsigned char *old = text;
567 utf8_to_unicode(&text, end);
568 if (old != text) {
569 memmove(old, text,
570 (int)(end - text) + 1);
572 goto display_field;
574 #endif /* CONFIG_UTF8 */
575 memmove(widget_data->cdata + widget_data->info.field.cpos,
576 widget_data->cdata + widget_data->info.field.cpos + 1,
577 cdata_len - widget_data->info.field.cpos + 1);
578 goto display_field;
581 case ACT_EDIT_KILL_TO_BOL:
582 memmove(widget_data->cdata,
583 widget_data->cdata + widget_data->info.field.cpos,
584 strlen(widget_data->cdata + widget_data->info.field.cpos) + 1);
585 widget_data->info.field.cpos = 0;
586 goto display_field;
588 case ACT_EDIT_KILL_TO_EOL:
589 widget_data->cdata[widget_data->info.field.cpos] = 0;
590 goto display_field;
592 case ACT_EDIT_KILL_WORD_BACK:
594 int cdata_len = strlen(widget_data->cdata);
595 int start = widget_data->info.field.cpos;
597 while (start > 0 && isspace(widget_data->cdata[start - 1]))
598 --start;
599 while (start > 0 && !isspace(widget_data->cdata[start - 1]))
600 --start;
602 memmove(widget_data->cdata + start,
603 widget_data->cdata + widget_data->info.field.cpos,
604 cdata_len - widget_data->info.field.cpos + 1);
606 widget_data->info.field.cpos = start;
608 goto display_field;
611 case ACT_EDIT_MOVE_BACKWARD_WORD:
612 while (widget_data->info.field.cpos > 0 && isspace(widget_data->cdata[widget_data->info.field.cpos - 1]))
613 --widget_data->info.field.cpos;
614 while (widget_data->info.field.cpos > 0 && !isspace(widget_data->cdata[widget_data->info.field.cpos - 1]))
615 --widget_data->info.field.cpos;
617 goto display_field;
619 case ACT_EDIT_MOVE_FORWARD_WORD:
620 while (isspace(widget_data->cdata[widget_data->info.field.cpos]))
621 ++widget_data->info.field.cpos;
622 while (widget_data->cdata[widget_data->info.field.cpos] && !isspace(widget_data->cdata[widget_data->info.field.cpos]))
623 ++widget_data->info.field.cpos;
624 while (isspace(widget_data->cdata[widget_data->info.field.cpos]))
625 ++widget_data->info.field.cpos;
627 goto display_field;
629 case ACT_EDIT_COPY_CLIPBOARD:
630 /* Copy to clipboard */
631 set_clipboard_text(widget_data->cdata);
632 return EVENT_PROCESSED;
634 case ACT_EDIT_CUT_CLIPBOARD:
635 /* Cut to clipboard */
636 set_clipboard_text(widget_data->cdata);
637 widget_data->cdata[0] = 0;
638 widget_data->info.field.cpos = 0;
639 goto display_field;
641 case ACT_EDIT_PASTE_CLIPBOARD:
643 /* Paste from clipboard */
644 unsigned char *clipboard = get_clipboard_text();
646 if (!clipboard) goto display_field;
648 safe_strncpy(widget_data->cdata, clipboard, widget_data->widget->datalen);
649 widget_data->info.field.cpos = strlen(widget_data->cdata);
650 mem_free(clipboard);
651 goto display_field;
654 case ACT_EDIT_AUTO_COMPLETE:
655 if (!widget_has_history(widget_data))
656 return EVENT_NOT_PROCESSED;
658 do_tab_compl(dlg_data, &widget_data->info.field.history);
659 goto display_field;
661 case ACT_EDIT_AUTO_COMPLETE_FILE:
662 if (!widget_has_history(widget_data))
663 return EVENT_NOT_PROCESSED;
665 do_tab_compl_file(dlg_data, &widget_data->info.field.history);
666 goto display_field;
668 case ACT_EDIT_AUTO_COMPLETE_UNAMBIGUOUS:
669 if (!widget_has_history(widget_data))
670 return EVENT_NOT_PROCESSED;
672 do_tab_compl_unambiguous(dlg_data, &widget_data->info.field.history);
673 goto display_field;
675 case ACT_EDIT_REDRAW:
676 redraw_terminal_cls(term);
677 return EVENT_PROCESSED;
679 default:
680 if (check_kbd_textinput_key(ev)) {
681 unsigned char *text = widget_data->cdata;
682 int textlen = strlen(text);
683 #ifndef CONFIG_UTF8
684 /* Both get_kbd_key(ev) and @text
685 * are in the terminal's charset. */
686 const int inslen = 1;
687 #else /* CONFIG_UTF8 */
688 const unsigned char *ins;
689 int inslen;
691 /* get_kbd_key(ev) is UCS-4, and @text
692 * is in the terminal's charset. */
693 ins = u2cp_no_nbsp(get_kbd_key(ev),
694 get_terminal_codepage(term));
695 inslen = strlen(ins);
696 #endif /* CONFIG_UTF8 */
698 if (textlen >= widget_data->widget->datalen - inslen)
699 goto display_field;
701 /* Shift to position of the cursor */
702 textlen -= widget_data->info.field.cpos;
703 text += widget_data->info.field.cpos;
705 memmove(text + inslen, text, textlen + 1);
706 #ifdef CONFIG_UTF8
707 memcpy(text, ins, inslen);
708 #else /* !CONFIG_UTF8 */
709 *text = get_kbd_key(ev);
710 #endif /* !CONFIG_UTF8 */
711 widget_data->info.field.cpos += inslen;
712 goto display_field;
715 return EVENT_NOT_PROCESSED;
717 display_field:
718 display_widget(dlg_data, widget_data);
719 redraw_from_window(dlg_data->win);
720 return EVENT_PROCESSED;
724 static widget_handler_status_T
725 clear_field(struct dialog_data *dlg_data, struct widget_data *widget_data)
727 widget_data->info.field.cpos = 0;
729 if (widget_data->widget->datalen)
730 memset(widget_data->cdata, 0, widget_data->widget->datalen);
732 return EVENT_PROCESSED;
735 const struct widget_ops field_ops = {
736 display_field,
737 init_field,
738 mouse_field,
739 kbd_field,
740 NULL,
741 clear_field,
744 const struct widget_ops field_pass_ops = {
745 display_field_pass,
746 init_field,
747 mouse_field,
748 kbd_field,
749 NULL,
750 clear_field,
754 /* Input lines */
756 static void
757 input_line_layouter(struct dialog_data *dlg_data)
759 struct input_line *input_line = dlg_data->dlg->udata;
760 struct session *ses = input_line->ses;
761 struct window *win = dlg_data->win;
762 int y = win->term->height - 1
763 - ses->status.show_status_bar
764 - ses->status.show_tabs_bar;
766 dlg_format_field(win->term, dlg_data->widgets_data, 0,
767 &y, win->term->width, NULL, ALIGN_LEFT, 0);
770 static widget_handler_status_T
771 input_line_event_handler(struct dialog_data *dlg_data)
773 struct input_line *input_line = dlg_data->dlg->udata;
774 input_line_handler_T handler = input_line->handler;
775 enum edit_action action_id;
776 struct widget_data *widget_data = dlg_data->widgets_data;
777 struct term_event *ev = dlg_data->term_event;
779 /* Noodle time */
780 switch (ev->ev) {
781 case EVENT_KBD:
782 action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
784 /* Handle some basic actions such as quiting for empty buffers */
785 switch (action_id) {
786 case ACT_EDIT_ENTER:
787 case ACT_EDIT_NEXT_ITEM:
788 case ACT_EDIT_PREVIOUS_ITEM:
789 if (widget_has_history(widget_data))
790 add_to_input_history(widget_data->widget->info.field.history,
791 input_line->buffer, 1);
792 break;
794 case ACT_EDIT_BACKSPACE:
795 if (!*input_line->buffer)
796 goto cancel_input_line;
797 break;
799 case ACT_EDIT_CANCEL:
800 goto cancel_input_line;
802 default:
803 break;
806 /* First let the input field do its business */
807 kbd_field(dlg_data, widget_data);
808 break;
810 case EVENT_MOUSE:
811 #ifdef CONFIG_MOUSE
812 if (ev->info.mouse.y != dlg_data->win->y) {
813 delete_window_ev(dlg_data->win, ev);
814 return EVENT_PROCESSED;
816 #endif /* CONFIG_MOUSE */
817 return EVENT_NOT_PROCESSED;
819 case EVENT_REDRAW:
820 /* Try to catch the redraw event initiated by the history
821 * completion and only respond if something was actually
822 * updated. Meaning we have new data in the line buffer that
823 * should be propagated to the line handler. */
824 if (!widget_has_history(widget_data)
825 || widget_data->info.field.cpos <= 0
826 || widget_data->info.field.cpos <= strlen(input_line->buffer))
827 return EVENT_NOT_PROCESSED;
829 /* Fall thru */
831 case EVENT_RESIZE:
832 action_id = ACT_EDIT_REDRAW;
833 break;
835 default:
836 return EVENT_NOT_PROCESSED;
839 update_dialog_data(dlg_data);
841 send_action_to_handler:
842 /* Then pass it on to the specialized handler */
843 switch (handler(input_line, action_id)) {
844 case INPUT_LINE_CANCEL:
845 cancel_input_line:
846 cancel_dialog(dlg_data, widget_data);
847 break;
849 case INPUT_LINE_REWIND:
850 /* This is stolen kbd_field() handling for ACT_EDIT_BACKSPACE */
851 memmove(widget_data->cdata + widget_data->info.field.cpos - 1,
852 widget_data->cdata + widget_data->info.field.cpos,
853 strlen(widget_data->cdata) - widget_data->info.field.cpos + 1);
854 widget_data->info.field.cpos--;
856 update_dialog_data(dlg_data);
858 goto send_action_to_handler;
860 case INPUT_LINE_PROCEED:
861 break;
864 /* Hack: We want our caller to perform its redrawing routine,
865 * even if we did process the event here. */
866 if (action_id == ACT_EDIT_REDRAW) return EVENT_NOT_PROCESSED;
868 /* Completely bypass any further dialog event handling */
869 return EVENT_PROCESSED;
872 void
873 input_field_line(struct session *ses, unsigned char *prompt, void *data,
874 struct input_history *history, input_line_handler_T handler)
876 struct dialog *dlg;
877 unsigned char *buffer;
878 struct input_line *input_line;
880 assert(ses);
882 dlg = calloc_dialog(INPUT_LINE_WIDGETS, sizeof(*input_line));
883 if (!dlg) return;
885 input_line = (void *) get_dialog_offset(dlg, INPUT_LINE_WIDGETS);
886 input_line->ses = ses;
887 input_line->data = data;
888 input_line->handler = handler;
889 buffer = input_line->buffer;
891 dlg->handle_event = input_line_event_handler;
892 dlg->layouter = input_line_layouter;
893 dlg->layout.only_widgets = 1;
894 dlg->udata = input_line;
896 add_dlg_field_float2(dlg, prompt, 0, 0, NULL, INPUT_LINE_BUFFER_SIZE,
897 buffer, history);
899 do_dialog(ses->tab->term, dlg, getml(dlg, (void *) NULL));