Handle history of input line.
[midnight-commander.git] / lib / widget / input.c
blob7593ef0880aef4a9a45a7bc39f401c54412e2d26
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 Authors:
9 Radek Doulik, 1994, 1995
10 Miguel de Icaza, 1994, 1995
11 Jakub Jelinek, 1995
12 Andrej Borsenkow, 1996
13 Norbert Warmuth, 1997
14 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 /** \file input.c
33 * \brief Source: WInput widget
36 #include <config.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/mouse.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
48 #include "lib/fileloc.h"
49 #include "lib/skin.h"
50 #include "lib/strutil.h"
51 #include "lib/util.h"
52 #include "lib/keybind.h" /* global_keymap_t */
53 #include "lib/widget.h"
54 #include "lib/event.h" /* mc_event_raise() */
56 #include "input_complete.h"
58 /*** global variables ****************************************************************************/
60 int quote = 0;
62 const global_keymap_t *input_map = NULL;
64 /*** file scope macro definitions ****************************************************************/
66 #define LARGE_HISTORY_BUTTON 1
68 #ifdef LARGE_HISTORY_BUTTON
69 #define HISTORY_BUTTON_WIDTH 3
70 #else
71 #define HISTORY_BUTTON_WIDTH 1
72 #endif
74 #define should_show_history_button(in) \
75 (in->history != NULL && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 \
76 && in->widget.owner != NULL)
78 /*** file scope type declarations ****************************************************************/
80 /*** file scope variables ************************************************************************/
82 /* Input widgets have a global kill ring */
83 /* Pointer to killed data */
84 static char *kill_buffer = NULL;
86 /*** file scope functions ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
89 static size_t
90 get_history_length (const GList * history)
92 size_t len = 0;
94 for (; history != NULL; history = g_list_previous (history))
95 len++;
97 return len;
100 /* --------------------------------------------------------------------------------------------- */
102 static void
103 draw_history_button (WInput * in)
105 char c;
106 gboolean disabled = (((Widget *) in)->options & W_DISABLED) != 0;
108 if (g_list_next (in->history_current) == NULL)
109 c = '^';
110 else if (g_list_previous (in->history_current) == NULL)
111 c = 'v';
112 else
113 c = '|';
115 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
116 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
117 #ifdef LARGE_HISTORY_BUTTON
119 Dlg_head *h;
120 h = in->widget.owner;
121 tty_print_string ("[ ]");
122 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
124 #endif
125 tty_print_char (c);
128 /* --------------------------------------------------------------------------------------------- */
130 static void
131 input_set_markers (WInput * in, long m1)
133 in->mark = m1;
136 /* --------------------------------------------------------------------------------------------- */
138 static void
139 input_mark_cmd (WInput * in, gboolean mark)
141 if (mark == 0)
143 in->highlight = FALSE;
144 input_set_markers (in, 0);
146 else
148 in->highlight = TRUE;
149 input_set_markers (in, in->point);
153 /* --------------------------------------------------------------------------------------------- */
155 static gboolean
156 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
158 if (in->highlight)
160 *start_mark = min (in->mark, in->point);
161 *end_mark = max (in->mark, in->point);
162 return TRUE;
164 else
166 *start_mark = *end_mark = 0;
167 return FALSE;
171 /* --------------------------------------------------------------------------------------------- */
173 static void
174 delete_region (WInput * in, int x_first, int x_last)
176 int first = min (x_first, x_last);
177 int last = max (x_first, x_last);
178 size_t len;
180 input_mark_cmd (in, FALSE);
181 in->point = first;
182 last = str_offset_to_pos (in->buffer, last);
183 first = str_offset_to_pos (in->buffer, first);
184 len = strlen (&in->buffer[last]) + 1;
185 memmove (&in->buffer[first], &in->buffer[last], len);
186 in->charpoint = 0;
187 in->need_push = TRUE;
190 /* --------------------------------------------------------------------------------------------- */
192 static void
193 do_show_hist (WInput * in)
195 size_t len;
196 char *r;
198 len = get_history_length (in->history);
200 r = history_show (&in->history, &in->widget, g_list_position (in->history_current, in->history));
201 if (r != NULL)
203 input_assign_text (in, r);
204 g_free (r);
207 /* Has history cleaned up or not? */
208 if (len != get_history_length (in->history))
209 in->history_changed = TRUE;
212 /* --------------------------------------------------------------------------------------------- */
213 /** Strip password from incomplete url (just user:pass@host without VFS prefix).
215 * @param url partial URL
216 * @return newly allocated string without password
219 static char *
220 input_history_strip_password (char *url)
222 char *at, *delim, *colon;
224 at = strrchr (url, '@');
225 if (at == NULL)
226 return g_strdup (url);
228 /* TODO: handle ':' and '@' in password */
230 delim = strstr (url, VFS_PATH_URL_DELIMITER);
231 if (delim != NULL)
232 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
233 else
234 colon = strchr (url, ':');
236 if (colon == NULL)
237 return g_strdup (url);
238 *colon = '\0';
240 return g_strconcat (url, at, NULL);
243 /* --------------------------------------------------------------------------------------------- */
245 static void
246 push_history (WInput * in, const char *text)
248 char *t;
249 gboolean empty;
251 if (text == NULL)
252 return;
254 t = g_strstrip (g_strdup (text));
255 empty = *t == '\0';
256 g_free (t);
257 t = g_strdup (empty ? "" : text);
259 if (!empty && in->history_name != NULL && in->strip_password)
262 We got string user:pass@host without any VFS prefixes
263 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
264 Therefore we want to strip password in separate algorithm
266 char *url_with_stripped_password;
268 url_with_stripped_password = input_history_strip_password (t);
269 g_free (t);
270 t = url_with_stripped_password;
273 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
274 in->history_changed)
276 in->history = list_append_unique (in->history, t);
277 in->history_current = in->history;
278 in->history_changed = TRUE;
280 else
281 g_free (t);
283 in->need_push = FALSE;
286 /* --------------------------------------------------------------------------------------------- */
288 static void
289 move_buffer_backward (WInput * in, int start, int end)
291 int i, pos, len;
292 int str_len;
294 str_len = str_length (in->buffer);
295 if (start >= str_len || end > str_len + 1)
296 return;
298 pos = str_offset_to_pos (in->buffer, start);
299 len = str_offset_to_pos (in->buffer, end) - pos;
301 for (i = pos; in->buffer[i + len - 1]; i++)
302 in->buffer[i] = in->buffer[i + len];
305 /* --------------------------------------------------------------------------------------------- */
307 static cb_ret_t
308 insert_char (WInput * in, int c_code)
310 size_t i;
311 int res;
313 if (in->highlight)
315 long m1, m2;
316 if (input_eval_marks (in, &m1, &m2))
317 delete_region (in, m1, m2);
319 if (c_code == -1)
320 return MSG_NOT_HANDLED;
322 if (in->charpoint >= MB_LEN_MAX)
323 return MSG_HANDLED;
325 in->charbuf[in->charpoint] = c_code;
326 in->charpoint++;
328 res = str_is_valid_char (in->charbuf, in->charpoint);
329 if (res < 0)
331 if (res != -2)
332 in->charpoint = 0; /* broken multibyte char, skip */
333 return MSG_HANDLED;
336 in->need_push = TRUE;
337 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
339 /* Expand the buffer */
340 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
341 char *narea = g_try_renew (char, in->buffer, new_length);
342 if (narea)
344 in->buffer = narea;
345 in->current_max_size = new_length;
349 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
351 /* bytes from begin */
352 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
353 /* move chars */
354 size_t rest_bytes = strlen (in->buffer + ins_point);
356 for (i = rest_bytes + 1; i > 0; i--)
357 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
359 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
360 in->point++;
363 in->charpoint = 0;
364 return MSG_HANDLED;
367 /* --------------------------------------------------------------------------------------------- */
369 static void
370 beginning_of_line (WInput * in)
372 in->point = 0;
373 in->charpoint = 0;
376 /* --------------------------------------------------------------------------------------------- */
378 static void
379 end_of_line (WInput * in)
381 in->point = str_length (in->buffer);
382 in->charpoint = 0;
385 /* --------------------------------------------------------------------------------------------- */
387 static void
388 backward_char (WInput * in)
390 const char *act;
392 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
393 if (in->point > 0)
394 in->point -= str_cprev_noncomb_char (&act, in->buffer);
395 in->charpoint = 0;
398 /* --------------------------------------------------------------------------------------------- */
400 static void
401 forward_char (WInput * in)
403 const char *act;
405 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
406 if (act[0] != '\0')
407 in->point += str_cnext_noncomb_char (&act);
408 in->charpoint = 0;
411 /* --------------------------------------------------------------------------------------------- */
413 static void
414 forward_word (WInput * in)
416 const char *p;
418 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
419 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
421 str_cnext_char (&p);
422 in->point++;
424 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
426 str_cnext_char (&p);
427 in->point++;
431 /* --------------------------------------------------------------------------------------------- */
433 static void
434 backward_word (WInput * in)
436 const char *p, *p_tmp;
438 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
439 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
441 while (p != in->buffer)
443 p_tmp = p;
444 str_cprev_char (&p);
445 if (!str_isspace (p) && !str_ispunct (p))
447 p = p_tmp;
448 break;
450 in->point--;
452 while (p != in->buffer)
454 str_cprev_char (&p);
455 if (str_isspace (p) || str_ispunct (p))
456 break;
458 in->point--;
462 /* --------------------------------------------------------------------------------------------- */
464 static void
465 backward_delete (WInput * in)
467 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
468 int start;
470 if (in->point == 0)
471 return;
473 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
474 move_buffer_backward (in, start, in->point);
475 in->charpoint = 0;
476 in->need_push = TRUE;
477 in->point = start;
480 /* --------------------------------------------------------------------------------------------- */
482 static void
483 delete_char (WInput * in)
485 const char *act;
486 int end = in->point;
488 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
489 end += str_cnext_noncomb_char (&act);
491 move_buffer_backward (in, in->point, end);
492 in->charpoint = 0;
493 in->need_push = TRUE;
496 /* --------------------------------------------------------------------------------------------- */
498 static void
499 copy_region (WInput * in, int x_first, int x_last)
501 int first = min (x_first, x_last);
502 int last = max (x_first, x_last);
504 if (last == first)
506 /* Copy selected files to clipboard */
507 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
508 /* try use external clipboard utility */
509 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
510 return;
513 g_free (kill_buffer);
515 first = str_offset_to_pos (in->buffer, first);
516 last = str_offset_to_pos (in->buffer, last);
518 kill_buffer = g_strndup (in->buffer + first, last - first);
520 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
521 /* try use external clipboard utility */
522 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
525 /* --------------------------------------------------------------------------------------------- */
527 static void
528 kill_word (WInput * in)
530 int old_point = in->point;
531 int new_point;
533 forward_word (in);
534 new_point = in->point;
535 in->point = old_point;
537 delete_region (in, old_point, new_point);
538 in->need_push = TRUE;
539 in->charpoint = 0;
542 /* --------------------------------------------------------------------------------------------- */
544 static void
545 back_kill_word (WInput * in)
547 int old_point = in->point;
548 int new_point;
550 backward_word (in);
551 new_point = in->point;
552 in->point = old_point;
554 delete_region (in, old_point, new_point);
555 in->need_push = TRUE;
558 /* --------------------------------------------------------------------------------------------- */
560 static void
561 yank (WInput * in)
563 if (kill_buffer != NULL)
565 char *p;
567 in->charpoint = 0;
568 for (p = kill_buffer; *p != '\0'; p++)
569 insert_char (in, *p);
570 in->charpoint = 0;
574 /* --------------------------------------------------------------------------------------------- */
576 static void
577 kill_line (WInput * in)
579 int chp;
581 chp = str_offset_to_pos (in->buffer, in->point);
582 g_free (kill_buffer);
583 kill_buffer = g_strdup (&in->buffer[chp]);
584 in->buffer[chp] = '\0';
585 in->charpoint = 0;
588 /* --------------------------------------------------------------------------------------------- */
590 static void
591 clear_line (WInput * in)
593 in->need_push = TRUE;
594 in->buffer[0] = '\0';
595 in->point = 0;
596 in->mark = 0;
597 in->highlight = FALSE;
598 in->charpoint = 0;
601 /* --------------------------------------------------------------------------------------------- */
603 static void
604 ins_from_clip (WInput * in)
606 char *p = NULL;
607 ev_clipboard_text_from_file_t event_data;
609 /* try use external clipboard utility */
610 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
612 event_data.text = &p;
613 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
614 if (event_data.ret)
616 char *pp;
618 for (pp = p; *pp != '\0'; pp++)
619 insert_char (in, *pp);
621 g_free (p);
625 /* --------------------------------------------------------------------------------------------- */
627 static void
628 hist_prev (WInput * in)
630 GList *prev;
632 if (in->history == NULL)
633 return;
635 if (in->need_push)
636 push_history (in, in->buffer);
638 prev = g_list_previous (in->history_current);
639 if (prev != NULL)
641 input_assign_text (in, (char *) prev->data);
642 in->history_current = prev;
643 in->history_changed = TRUE;
644 in->need_push = FALSE;
648 /* --------------------------------------------------------------------------------------------- */
650 static void
651 hist_next (WInput * in)
653 GList *next;
655 if (in->need_push)
657 push_history (in, in->buffer);
658 input_assign_text (in, "");
659 return;
662 if (in->history == NULL)
663 return;
665 next = g_list_next (in->history_current);
666 if (next == NULL)
668 input_assign_text (in, "");
669 in->history_current = in->history;
671 else
673 input_assign_text (in, (char *) next->data);
674 in->history_current = next;
675 in->history_changed = TRUE;
676 in->need_push = FALSE;
680 /* --------------------------------------------------------------------------------------------- */
682 static void
683 port_region_marked_for_delete (WInput * in)
685 in->buffer[0] = '\0';
686 in->point = 0;
687 in->first = FALSE;
688 in->charpoint = 0;
691 /* --------------------------------------------------------------------------------------------- */
693 static cb_ret_t
694 input_execute_cmd (WInput * in, unsigned long command)
696 cb_ret_t res = MSG_HANDLED;
698 /* a highlight command like shift-arrow */
699 if (command == CK_MarkLeft || command == CK_MarkRight ||
700 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
701 command == CK_MarkToHome || command == CK_MarkToEnd)
703 if (!in->highlight)
705 input_mark_cmd (in, FALSE); /* clear */
706 input_mark_cmd (in, TRUE); /* marking on */
710 switch (command)
712 case CK_WordRight:
713 case CK_WordLeft:
714 case CK_Right:
715 case CK_Left:
716 if (in->highlight)
717 input_mark_cmd (in, FALSE);
720 switch (command)
722 case CK_Home:
723 case CK_MarkToHome:
724 beginning_of_line (in);
725 break;
726 case CK_End:
727 case CK_MarkToEnd:
728 end_of_line (in);
729 break;
730 case CK_Left:
731 case CK_MarkLeft:
732 backward_char (in);
733 break;
734 case CK_WordLeft:
735 case CK_MarkToWordBegin:
736 backward_word (in);
737 break;
738 case CK_Right:
739 case CK_MarkRight:
740 forward_char (in);
741 break;
742 case CK_WordRight:
743 case CK_MarkToWordEnd:
744 forward_word (in);
745 break;
746 case CK_BackSpace:
747 if (in->highlight)
749 long m1, m2;
750 if (input_eval_marks (in, &m1, &m2))
751 delete_region (in, m1, m2);
753 else
754 backward_delete (in);
755 break;
756 case CK_Delete:
757 if (in->first)
758 port_region_marked_for_delete (in);
759 else if (in->highlight)
761 long m1, m2;
762 if (input_eval_marks (in, &m1, &m2))
763 delete_region (in, m1, m2);
765 else
766 delete_char (in);
767 break;
768 case CK_DeleteToWordEnd:
769 kill_word (in);
770 break;
771 case CK_DeleteToWordBegin:
772 back_kill_word (in);
773 break;
774 case CK_Mark:
775 input_mark_cmd (in, TRUE);
776 break;
777 case CK_Remove:
778 delete_region (in, in->point, in->mark);
779 break;
780 case CK_DeleteToEnd:
781 kill_line (in);
782 break;
783 case CK_Clear:
784 clear_line (in);
785 break;
786 case CK_Store:
787 copy_region (in, in->mark, in->point);
788 break;
789 case CK_Cut:
790 copy_region (in, in->mark, in->point);
791 delete_region (in, in->point, in->mark);
792 break;
793 case CK_Yank:
794 yank (in);
795 break;
796 case CK_Paste:
797 ins_from_clip (in);
798 break;
799 case CK_HistoryPrev:
800 hist_prev (in);
801 break;
802 case CK_HistoryNext:
803 hist_next (in);
804 break;
805 case CK_History:
806 do_show_hist (in);
807 break;
808 case CK_Complete:
809 complete (in);
810 break;
811 default:
812 res = MSG_NOT_HANDLED;
815 if (command != CK_MarkLeft && command != CK_MarkRight &&
816 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
817 command != CK_MarkToHome && command != CK_MarkToEnd)
818 in->highlight = FALSE;
820 return res;
823 /* --------------------------------------------------------------------------------------------- */
825 /* "history_load" event handler */
826 static gboolean
827 input_load_history (const gchar * event_group_name, const gchar * event_name,
828 gpointer init_data, gpointer data)
830 WInput *in = (WInput *) init_data;
831 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
832 const char *def_text;
833 size_t buffer_len;
835 (void) event_group_name;
836 (void) event_name;
838 in->history = history_load (ev->cfg, in->history_name);
839 in->history_current = in->history;
841 if (in->init_text == NULL)
842 def_text = "";
843 else if (in->init_text == INPUT_LAST_TEXT)
845 if (in->history != NULL && in->history->data != NULL)
846 def_text = (const char *) in->history->data;
847 else
848 def_text = "";
850 in->init_text = NULL;
852 else
853 def_text = in->init_text;
855 buffer_len = strlen (def_text);
856 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
857 in->current_max_size = buffer_len;
858 if (buffer_len > (size_t) in->field_width)
859 in->buffer = g_realloc (in->buffer, buffer_len);
860 strcpy (in->buffer, def_text);
861 in->point = str_length (in->buffer);
863 return TRUE;
866 /* --------------------------------------------------------------------------------------------- */
868 /* "history_save" event handler */
869 static gboolean
870 input_save_history (const gchar * event_group_name, const gchar * event_name,
871 gpointer init_data, gpointer data)
873 WInput *in = (WInput *) init_data;
875 (void) event_group_name;
876 (void) event_name;
878 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
880 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
882 push_history (in, in->buffer);
883 if (in->history_changed)
884 history_save (ev->cfg, in->history_name, in->history);
885 in->history_changed = FALSE;
888 return TRUE;
891 /* --------------------------------------------------------------------------------------------- */
893 static void
894 input_destroy (WInput * in)
896 if (in == NULL)
898 fprintf (stderr, "Internal error: null Input *\n");
899 exit (EXIT_FAILURE);
902 input_free_completions (in);
904 /* clean history */
905 if (in->history != NULL)
907 /* history is already saved before this moment */
908 in->history = g_list_first (in->history);
909 g_list_foreach (in->history, (GFunc) g_free, NULL);
910 g_list_free (in->history);
912 g_free (in->history_name);
914 g_free (in->buffer);
915 input_free_completions (in);
916 g_free (in->init_text);
918 g_free (kill_buffer);
919 kill_buffer = NULL;
922 /* --------------------------------------------------------------------------------------------- */
924 static int
925 input_event (Gpm_Event * event, void *data)
927 WInput *in = (WInput *) data;
928 Widget *w = (Widget *) data;
930 if (!mouse_global_in_widget (event, w))
931 return MOU_UNHANDLED;
933 if ((event->type & GPM_DOWN) != 0)
935 in->first = FALSE;
936 input_mark_cmd (in, FALSE);
939 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
941 Gpm_Event local;
943 local = mouse_get_local (event, w);
945 dlg_select_widget (w);
947 if (local.x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
948 && should_show_history_button (in))
949 do_show_hist (in);
950 else
952 in->point = str_length (in->buffer);
953 if (local.x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
954 in->point = str_column_to_pos (in->buffer, local.x + in->term_first_shown - 1);
957 input_update (in, TRUE);
960 /* A lone up mustn't do anything */
961 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
962 return MOU_NORMAL;
964 if ((event->type & GPM_DRAG) == 0)
965 input_mark_cmd (in, TRUE);
967 return MOU_NORMAL;
970 /* --------------------------------------------------------------------------------------------- */
971 /*** public functions ****************************************************************************/
972 /* --------------------------------------------------------------------------------------------- */
974 /** Create new instance of WInput object.
975 * @param y Y coordinate
976 * @param x X coordinate
977 * @param input_colors Array of used colors
978 * @param width Widget width
979 * @param def_text Default text filled in widget
980 * @param histname Name of history
981 * @param completion_flags Flags for specify type of completions
982 * @returns WInput object
984 WInput *
985 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
986 const char *histname, input_complete_t completion_flags)
988 WInput *in;
990 in = g_new (WInput, 1);
991 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
992 in->widget.options |= W_IS_INPUT;
994 memmove (in->color, input_colors, sizeof (input_colors_t));
996 in->field_width = width;
997 in->first = TRUE;
998 in->highlight = FALSE;
999 in->term_first_shown = 0;
1000 in->disable_update = 0;
1001 in->mark = 0;
1002 in->need_push = TRUE;
1003 in->is_password = FALSE;
1004 in->charpoint = 0;
1005 in->strip_password = FALSE;
1007 /* in->buffer will be corrected in "history_load" event handler */
1008 in->current_max_size = width + 1;
1009 in->buffer = g_new0 (char, in->current_max_size);
1010 in->point = 0;
1012 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
1014 in->completions = NULL;
1015 in->completion_flags = completion_flags;
1017 /* prepare to history setup */
1018 in->history = NULL;
1019 in->history_current = NULL;
1020 in->history_changed = FALSE;
1021 in->history_name = NULL;
1022 if ((histname != NULL) && (*histname != '\0'))
1023 in->history_name = g_strdup (histname);
1025 /* history will be loaded later */
1027 return in;
1030 /* --------------------------------------------------------------------------------------------- */
1032 cb_ret_t
1033 input_callback (Widget * w, widget_msg_t msg, int parm)
1035 WInput *in = (WInput *) w;
1036 cb_ret_t v;
1038 switch (msg)
1040 case WIDGET_INIT:
1041 /* subscribe to "history_load" event */
1042 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1043 /* subscribe to "history_save" event */
1044 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1045 return MSG_HANDLED;
1047 case WIDGET_KEY:
1048 if (parm == XCTRL ('q'))
1050 quote = 1;
1051 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1052 quote = 0;
1053 return v;
1056 /* Keys we want others to handle */
1057 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1058 || parm == KEY_F (10) || parm == '\n')
1059 return MSG_NOT_HANDLED;
1061 /* When pasting multiline text, insert literal Enter */
1062 if ((parm & ~KEY_M_MASK) == '\n')
1064 quote = 1;
1065 v = input_handle_char (in, '\n');
1066 quote = 0;
1067 return v;
1070 return input_handle_char (in, parm);
1072 case WIDGET_COMMAND:
1073 return input_execute_cmd (in, parm);
1075 case WIDGET_FOCUS:
1076 case WIDGET_UNFOCUS:
1077 case WIDGET_DRAW:
1078 input_update (in, FALSE);
1079 return MSG_HANDLED;
1081 case WIDGET_CURSOR:
1082 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1083 - in->term_first_shown);
1084 return MSG_HANDLED;
1086 case WIDGET_DESTROY:
1087 /* unsubscribe from "history_load" event */
1088 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1089 /* unsubscribe from "history_save" event */
1090 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1091 input_destroy (in);
1092 return MSG_HANDLED;
1094 default:
1095 return default_proc (msg, parm);
1099 /* --------------------------------------------------------------------------------------------- */
1101 /** Get default colors for WInput widget.
1102 * @returns default colors
1104 const int *
1105 input_get_default_colors (void)
1107 static input_colors_t standart_colors;
1109 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1110 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1111 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1112 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1114 return standart_colors;
1117 /* --------------------------------------------------------------------------------------------- */
1119 void
1120 input_set_origin (WInput * in, int x, int field_width)
1122 in->widget.x = x;
1123 in->field_width = in->widget.cols = field_width;
1124 input_update (in, FALSE);
1127 /* --------------------------------------------------------------------------------------------- */
1129 cb_ret_t
1130 input_handle_char (WInput * in, int key)
1132 cb_ret_t v;
1133 unsigned long command;
1135 v = MSG_NOT_HANDLED;
1137 if (quote != 0)
1139 input_free_completions (in);
1140 v = insert_char (in, key);
1141 input_update (in, TRUE);
1142 quote = 0;
1143 return v;
1146 command = keybind_lookup_keymap_command (input_map, key);
1148 if (command == CK_IgnoreKey)
1150 if (key > 255)
1151 return MSG_NOT_HANDLED;
1152 if (in->first)
1153 port_region_marked_for_delete (in);
1154 input_free_completions (in);
1155 v = insert_char (in, key);
1157 else
1159 if (command != CK_Complete)
1160 input_free_completions (in);
1161 input_execute_cmd (in, command);
1162 v = MSG_HANDLED;
1163 if (in->first)
1164 input_update (in, TRUE); /* needed to clear in->first */
1167 input_update (in, TRUE);
1168 return v;
1171 /* --------------------------------------------------------------------------------------------- */
1173 /* This function is a test for a special input key used in complete.c */
1174 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1175 and 2 if it is a complete key */
1177 input_key_is_in_map (WInput * in, int key)
1179 unsigned long command;
1181 (void) in;
1183 command = keybind_lookup_keymap_command (input_map, key);
1184 if (command == CK_IgnoreKey)
1185 return 0;
1187 return (command == CK_Complete) ? 2 : 1;
1190 /* --------------------------------------------------------------------------------------------- */
1192 void
1193 input_assign_text (WInput * in, const char *text)
1195 input_free_completions (in);
1196 g_free (in->buffer);
1197 in->current_max_size = strlen (text) + 1;
1198 in->buffer = g_strndup (text, in->current_max_size); /* was in->buffer->text */
1199 in->point = str_length (in->buffer);
1200 in->mark = 0;
1201 in->need_push = TRUE;
1202 in->charpoint = 0;
1203 input_update (in, TRUE);
1206 /* --------------------------------------------------------------------------------------------- */
1208 /* Inserts text in input line */
1209 void
1210 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1212 input_disable_update (in);
1213 while (*text != '\0')
1214 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1215 if (insert_extra_space)
1216 input_handle_char (in, ' ');
1217 input_enable_update (in);
1218 input_update (in, TRUE);
1221 /* --------------------------------------------------------------------------------------------- */
1223 void
1224 input_set_point (WInput * in, int pos)
1226 int max_pos;
1228 max_pos = str_length (in->buffer);
1229 pos = min (pos, max_pos);
1230 if (pos != in->point)
1231 input_free_completions (in);
1232 in->point = pos;
1233 in->charpoint = 0;
1234 input_update (in, TRUE);
1237 /* --------------------------------------------------------------------------------------------- */
1239 void
1240 input_update (WInput * in, gboolean clear_first)
1242 int has_history = 0;
1243 int i;
1244 int buf_len;
1245 const char *cp;
1246 int pw;
1248 if (should_show_history_button (in))
1249 has_history = HISTORY_BUTTON_WIDTH;
1251 if (in->disable_update != 0)
1252 return;
1254 buf_len = str_length (in->buffer);
1255 pw = str_term_width2 (in->buffer, in->point);
1257 /* Make the point visible */
1258 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1260 in->term_first_shown = pw - (in->field_width / 3);
1261 if (in->term_first_shown < 0)
1262 in->term_first_shown = 0;
1265 /* Adjust the mark */
1266 in->mark = min (in->mark, buf_len);
1268 if (has_history != 0)
1269 draw_history_button (in);
1271 if ((((Widget *) in)->options & W_DISABLED) != 0)
1272 tty_setcolor (DISABLED_COLOR);
1273 else if (in->first)
1274 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1275 else
1276 tty_setcolor (in->color[WINPUTC_MAIN]);
1278 widget_move (&in->widget, 0, 0);
1280 if (!in->is_password)
1282 if (!in->highlight)
1283 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1284 in->field_width - has_history));
1285 else
1287 long m1, m2;
1289 if (input_eval_marks (in, &m1, &m2))
1291 tty_setcolor (in->color[WINPUTC_MAIN]);
1292 cp = str_term_substring (in->buffer, in->term_first_shown,
1293 in->field_width - has_history);
1294 tty_print_string (cp);
1295 tty_setcolor (in->color[WINPUTC_MARK]);
1296 if (m1 < in->term_first_shown)
1298 widget_move (&in->widget, 0, 0);
1299 tty_print_string (str_term_substring
1300 (in->buffer, in->term_first_shown,
1301 m2 - in->term_first_shown));
1303 else
1305 int sel_width;
1307 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1308 sel_width =
1309 min (m2 - m1,
1310 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1311 in->term_first_shown));
1312 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1317 else
1319 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1320 tty_setcolor (in->color[WINPUTC_MAIN]);
1321 for (i = 0; i < in->field_width - has_history; i++)
1323 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1324 tty_print_char ('*');
1325 else
1326 tty_print_char (' ');
1327 if (cp[0] != '\0')
1328 str_cnext_char (&cp);
1332 if (clear_first)
1333 in->first = FALSE;
1336 /* --------------------------------------------------------------------------------------------- */
1338 void
1339 input_enable_update (WInput * in)
1341 in->disable_update--;
1342 input_update (in, FALSE);
1345 /* --------------------------------------------------------------------------------------------- */
1347 void
1348 input_disable_update (WInput * in)
1350 in->disable_update++;
1353 /* --------------------------------------------------------------------------------------------- */
1356 * Cleans the input line and adds the current text to the history
1358 * @param in the input line
1360 void
1361 input_clean (WInput * in)
1363 push_history (in, in->buffer);
1364 in->need_push = TRUE;
1365 in->buffer[0] = '\0';
1366 in->point = 0;
1367 in->charpoint = 0;
1368 in->mark = 0;
1369 in->highlight = FALSE;
1370 input_free_completions (in);
1371 input_update (in, FALSE);
1374 /* --------------------------------------------------------------------------------------------- */
1376 void
1377 input_free_completions (WInput * in)
1379 g_strfreev (in->completions);
1380 in->completions = NULL;
1383 /* --------------------------------------------------------------------------------------------- */