(input_history_strip_password): actually url is not the pointer to constant.
[midnight-commander.git] / lib / widget / input.c
blob89acf08e891623760f8706617fff69dd404e0f1f
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 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
109 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
110 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
111 #ifdef LARGE_HISTORY_BUTTON
113 Dlg_head *h;
114 h = in->widget.owner;
115 tty_print_string ("[ ]");
116 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
118 #endif
119 tty_print_char (c);
122 /* --------------------------------------------------------------------------------------------- */
124 static void
125 input_set_markers (WInput * in, long m1)
127 in->mark = m1;
130 /* --------------------------------------------------------------------------------------------- */
132 static void
133 input_mark_cmd (WInput * in, gboolean mark)
135 if (mark == 0)
137 in->highlight = FALSE;
138 input_set_markers (in, 0);
140 else
142 in->highlight = TRUE;
143 input_set_markers (in, in->point);
147 /* --------------------------------------------------------------------------------------------- */
149 static gboolean
150 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
152 if (in->highlight)
154 *start_mark = min (in->mark, in->point);
155 *end_mark = max (in->mark, in->point);
156 return TRUE;
158 else
160 *start_mark = *end_mark = 0;
161 return FALSE;
165 /* --------------------------------------------------------------------------------------------- */
167 static void
168 delete_region (WInput * in, int x_first, int x_last)
170 int first = min (x_first, x_last);
171 int last = max (x_first, x_last);
172 size_t len;
174 input_mark_cmd (in, FALSE);
175 in->point = first;
176 last = str_offset_to_pos (in->buffer, last);
177 first = str_offset_to_pos (in->buffer, first);
178 len = strlen (&in->buffer[last]) + 1;
179 memmove (&in->buffer[first], &in->buffer[last], len);
180 in->charpoint = 0;
181 in->need_push = TRUE;
184 /* --------------------------------------------------------------------------------------------- */
186 static void
187 do_show_hist (WInput * in)
189 size_t len;
190 char *r;
192 len = get_history_length (in->history);
194 r = history_show (&in->history, &in->widget);
195 if (r != NULL)
197 input_assign_text (in, r);
198 g_free (r);
201 /* Has history cleaned up or not? */
202 if (len != get_history_length (in->history))
203 in->history_changed = TRUE;
206 /* --------------------------------------------------------------------------------------------- */
207 /** Strip password from incomplete url (just user:pass@host without VFS prefix).
209 * @param url partial URL
210 * @return newly allocated string without password
213 static char *
214 input_history_strip_password (char *url)
216 char *at, *delim, *colon;
218 at = strrchr (url, '@');
219 if (at == NULL)
220 return g_strdup (url);
222 /* TODO: handle ':' and '@' in password */
224 delim = strstr (url, VFS_PATH_URL_DELIMITER);
225 if (delim != NULL)
226 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
227 else
228 colon = strchr (url, ':');
230 if (colon == NULL)
231 return g_strdup (url);
232 *colon = '\0';
234 return g_strconcat (url, at, NULL);
237 /* --------------------------------------------------------------------------------------------- */
239 static void
240 push_history (WInput * in, const char *text)
242 char *t;
243 gboolean empty;
245 if (text == NULL)
246 return;
248 t = g_strstrip (g_strdup (text));
249 empty = *t == '\0';
250 g_free (t);
251 t = g_strdup (empty ? "" : text);
253 if (!empty && in->history_name != NULL && in->strip_password)
256 We got string user:pass@host without any VFS prefixes
257 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
258 Therefore we want to strip password in separate algorithm
260 char *url_with_stripped_password;
262 url_with_stripped_password = input_history_strip_password (t);
263 g_free (t);
264 t = url_with_stripped_password;
267 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
268 in->history_changed)
270 in->history = list_append_unique (in->history, t);
271 in->history_changed = TRUE;
273 else
274 g_free (t);
276 in->need_push = FALSE;
279 /* --------------------------------------------------------------------------------------------- */
281 static void
282 move_buffer_backward (WInput * in, int start, int end)
284 int i, pos, len;
285 int str_len;
287 str_len = str_length (in->buffer);
288 if (start >= str_len || end > str_len + 1)
289 return;
291 pos = str_offset_to_pos (in->buffer, start);
292 len = str_offset_to_pos (in->buffer, end) - pos;
294 for (i = pos; in->buffer[i + len - 1]; i++)
295 in->buffer[i] = in->buffer[i + len];
298 /* --------------------------------------------------------------------------------------------- */
300 static cb_ret_t
301 insert_char (WInput * in, int c_code)
303 size_t i;
304 int res;
306 if (in->highlight)
308 long m1, m2;
309 if (input_eval_marks (in, &m1, &m2))
310 delete_region (in, m1, m2);
312 if (c_code == -1)
313 return MSG_NOT_HANDLED;
315 if (in->charpoint >= MB_LEN_MAX)
316 return MSG_HANDLED;
318 in->charbuf[in->charpoint] = c_code;
319 in->charpoint++;
321 res = str_is_valid_char (in->charbuf, in->charpoint);
322 if (res < 0)
324 if (res != -2)
325 in->charpoint = 0; /* broken multibyte char, skip */
326 return MSG_HANDLED;
329 in->need_push = TRUE;
330 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
332 /* Expand the buffer */
333 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
334 char *narea = g_try_renew (char, in->buffer, new_length);
335 if (narea)
337 in->buffer = narea;
338 in->current_max_size = new_length;
342 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
344 /* bytes from begin */
345 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
346 /* move chars */
347 size_t rest_bytes = strlen (in->buffer + ins_point);
349 for (i = rest_bytes + 1; i > 0; i--)
350 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
352 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
353 in->point++;
356 in->charpoint = 0;
357 return MSG_HANDLED;
360 /* --------------------------------------------------------------------------------------------- */
362 static void
363 beginning_of_line (WInput * in)
365 in->point = 0;
366 in->charpoint = 0;
369 /* --------------------------------------------------------------------------------------------- */
371 static void
372 end_of_line (WInput * in)
374 in->point = str_length (in->buffer);
375 in->charpoint = 0;
378 /* --------------------------------------------------------------------------------------------- */
380 static void
381 backward_char (WInput * in)
383 const char *act;
385 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
386 if (in->point > 0)
387 in->point -= str_cprev_noncomb_char (&act, in->buffer);
388 in->charpoint = 0;
391 /* --------------------------------------------------------------------------------------------- */
393 static void
394 forward_char (WInput * in)
396 const char *act;
398 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
399 if (act[0] != '\0')
400 in->point += str_cnext_noncomb_char (&act);
401 in->charpoint = 0;
404 /* --------------------------------------------------------------------------------------------- */
406 static void
407 forward_word (WInput * in)
409 const char *p;
411 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
412 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
414 str_cnext_char (&p);
415 in->point++;
417 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
419 str_cnext_char (&p);
420 in->point++;
424 /* --------------------------------------------------------------------------------------------- */
426 static void
427 backward_word (WInput * in)
429 const char *p, *p_tmp;
431 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
432 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
434 while (p != in->buffer)
436 p_tmp = p;
437 str_cprev_char (&p);
438 if (!str_isspace (p) && !str_ispunct (p))
440 p = p_tmp;
441 break;
443 in->point--;
445 while (p != in->buffer)
447 str_cprev_char (&p);
448 if (str_isspace (p) || str_ispunct (p))
449 break;
451 in->point--;
455 /* --------------------------------------------------------------------------------------------- */
457 static void
458 backward_delete (WInput * in)
460 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
461 int start;
463 if (in->point == 0)
464 return;
466 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
467 move_buffer_backward (in, start, in->point);
468 in->charpoint = 0;
469 in->need_push = TRUE;
470 in->point = start;
473 /* --------------------------------------------------------------------------------------------- */
475 static void
476 delete_char (WInput * in)
478 const char *act;
479 int end = in->point;
481 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
482 end += str_cnext_noncomb_char (&act);
484 move_buffer_backward (in, in->point, end);
485 in->charpoint = 0;
486 in->need_push = TRUE;
489 /* --------------------------------------------------------------------------------------------- */
491 static void
492 copy_region (WInput * in, int x_first, int x_last)
494 int first = min (x_first, x_last);
495 int last = max (x_first, x_last);
497 if (last == first)
499 /* Copy selected files to clipboard */
500 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
501 /* try use external clipboard utility */
502 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
503 return;
506 g_free (kill_buffer);
508 first = str_offset_to_pos (in->buffer, first);
509 last = str_offset_to_pos (in->buffer, last);
511 kill_buffer = g_strndup (in->buffer + first, last - first);
513 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
514 /* try use external clipboard utility */
515 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
518 /* --------------------------------------------------------------------------------------------- */
520 static void
521 kill_word (WInput * in)
523 int old_point = in->point;
524 int new_point;
526 forward_word (in);
527 new_point = in->point;
528 in->point = old_point;
530 delete_region (in, old_point, new_point);
531 in->need_push = TRUE;
532 in->charpoint = 0;
535 /* --------------------------------------------------------------------------------------------- */
537 static void
538 back_kill_word (WInput * in)
540 int old_point = in->point;
541 int new_point;
543 backward_word (in);
544 new_point = in->point;
545 in->point = old_point;
547 delete_region (in, old_point, new_point);
548 in->need_push = TRUE;
551 /* --------------------------------------------------------------------------------------------- */
553 static void
554 yank (WInput * in)
556 if (kill_buffer != NULL)
558 char *p;
560 in->charpoint = 0;
561 for (p = kill_buffer; *p != '\0'; p++)
562 insert_char (in, *p);
563 in->charpoint = 0;
567 /* --------------------------------------------------------------------------------------------- */
569 static void
570 kill_line (WInput * in)
572 int chp;
574 chp = str_offset_to_pos (in->buffer, in->point);
575 g_free (kill_buffer);
576 kill_buffer = g_strdup (&in->buffer[chp]);
577 in->buffer[chp] = '\0';
578 in->charpoint = 0;
581 /* --------------------------------------------------------------------------------------------- */
583 static void
584 clear_line (WInput * in)
586 in->need_push = TRUE;
587 in->buffer[0] = '\0';
588 in->point = 0;
589 in->mark = 0;
590 in->highlight = FALSE;
591 in->charpoint = 0;
594 /* --------------------------------------------------------------------------------------------- */
596 static void
597 ins_from_clip (WInput * in)
599 char *p = NULL;
600 ev_clipboard_text_from_file_t event_data;
602 /* try use external clipboard utility */
603 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
605 event_data.text = &p;
606 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
607 if (event_data.ret)
609 char *pp;
611 for (pp = p; *pp != '\0'; pp++)
612 insert_char (in, *pp);
614 g_free (p);
618 /* --------------------------------------------------------------------------------------------- */
620 static void
621 hist_prev (WInput * in)
623 GList *prev;
625 if (in->history == NULL)
626 return;
628 if (in->need_push)
629 push_history (in, in->buffer);
631 prev = g_list_previous (in->history);
632 if (prev != NULL)
634 in->history = prev;
635 in->history_changed = TRUE;
636 input_assign_text (in, (char *) prev->data);
637 in->need_push = FALSE;
641 /* --------------------------------------------------------------------------------------------- */
643 static void
644 hist_next (WInput * in)
646 GList *next;
648 if (in->need_push)
650 push_history (in, in->buffer);
651 input_assign_text (in, "");
652 return;
655 if (in->history == NULL)
656 return;
658 next = g_list_next (in->history);
659 if (next == NULL)
660 input_assign_text (in, "");
661 else
663 in->history = next;
664 in->history_changed = TRUE;
665 input_assign_text (in, (char *) next->data);
666 in->need_push = FALSE;
670 /* --------------------------------------------------------------------------------------------- */
672 static void
673 port_region_marked_for_delete (WInput * in)
675 in->buffer[0] = '\0';
676 in->point = 0;
677 in->first = FALSE;
678 in->charpoint = 0;
681 /* --------------------------------------------------------------------------------------------- */
683 static cb_ret_t
684 input_execute_cmd (WInput * in, unsigned long command)
686 cb_ret_t res = MSG_HANDLED;
688 /* a highlight command like shift-arrow */
689 if (command == CK_MarkLeft || command == CK_MarkRight ||
690 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
691 command == CK_MarkToHome || command == CK_MarkToEnd)
693 if (!in->highlight)
695 input_mark_cmd (in, FALSE); /* clear */
696 input_mark_cmd (in, TRUE); /* marking on */
700 switch (command)
702 case CK_WordRight:
703 case CK_WordLeft:
704 case CK_Right:
705 case CK_Left:
706 if (in->highlight)
707 input_mark_cmd (in, FALSE);
710 switch (command)
712 case CK_Home:
713 case CK_MarkToHome:
714 beginning_of_line (in);
715 break;
716 case CK_End:
717 case CK_MarkToEnd:
718 end_of_line (in);
719 break;
720 case CK_Left:
721 case CK_MarkLeft:
722 backward_char (in);
723 break;
724 case CK_WordLeft:
725 case CK_MarkToWordBegin:
726 backward_word (in);
727 break;
728 case CK_Right:
729 case CK_MarkRight:
730 forward_char (in);
731 break;
732 case CK_WordRight:
733 case CK_MarkToWordEnd:
734 forward_word (in);
735 break;
736 case CK_BackSpace:
737 if (in->highlight)
739 long m1, m2;
740 if (input_eval_marks (in, &m1, &m2))
741 delete_region (in, m1, m2);
743 else
744 backward_delete (in);
745 break;
746 case CK_Delete:
747 if (in->first)
748 port_region_marked_for_delete (in);
749 else if (in->highlight)
751 long m1, m2;
752 if (input_eval_marks (in, &m1, &m2))
753 delete_region (in, m1, m2);
755 else
756 delete_char (in);
757 break;
758 case CK_DeleteToWordEnd:
759 kill_word (in);
760 break;
761 case CK_DeleteToWordBegin:
762 back_kill_word (in);
763 break;
764 case CK_Mark:
765 input_mark_cmd (in, TRUE);
766 break;
767 case CK_Remove:
768 delete_region (in, in->point, in->mark);
769 break;
770 case CK_DeleteToEnd:
771 kill_line (in);
772 break;
773 case CK_Clear:
774 clear_line (in);
775 break;
776 case CK_Store:
777 copy_region (in, in->mark, in->point);
778 break;
779 case CK_Cut:
780 copy_region (in, in->mark, in->point);
781 delete_region (in, in->point, in->mark);
782 break;
783 case CK_Yank:
784 yank (in);
785 break;
786 case CK_Paste:
787 ins_from_clip (in);
788 break;
789 case CK_HistoryPrev:
790 hist_prev (in);
791 break;
792 case CK_HistoryNext:
793 hist_next (in);
794 break;
795 case CK_History:
796 do_show_hist (in);
797 break;
798 case CK_Complete:
799 complete (in);
800 break;
801 default:
802 res = MSG_NOT_HANDLED;
805 if (command != CK_MarkLeft && command != CK_MarkRight &&
806 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
807 command != CK_MarkToHome && command != CK_MarkToEnd)
808 in->highlight = FALSE;
810 return res;
813 /* --------------------------------------------------------------------------------------------- */
815 /* "history_load" event handler */
816 static gboolean
817 input_load_history (const gchar * event_group_name, const gchar * event_name,
818 gpointer init_data, gpointer data)
820 WInput *in = (WInput *) init_data;
821 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
822 const char *def_text;
823 size_t buffer_len;
825 (void) event_group_name;
826 (void) event_name;
828 in->history = history_load (ev->cfg, in->history_name);
830 if (in->init_text == NULL)
831 def_text = "";
832 else if (in->init_text == INPUT_LAST_TEXT)
834 if (in->history != NULL && in->history->data != NULL)
835 def_text = (const char *) in->history->data;
836 else
837 def_text = "";
839 in->init_text = NULL;
841 else
842 def_text = in->init_text;
844 buffer_len = strlen (def_text);
845 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
846 in->current_max_size = buffer_len;
847 if (buffer_len > (size_t) in->field_width)
848 in->buffer = g_realloc (in->buffer, buffer_len);
849 strcpy (in->buffer, def_text);
850 in->point = str_length (in->buffer);
852 return TRUE;
855 /* --------------------------------------------------------------------------------------------- */
857 /* "history_save" event handler */
858 static gboolean
859 input_save_history (const gchar * event_group_name, const gchar * event_name,
860 gpointer init_data, gpointer data)
862 WInput *in = (WInput *) init_data;
864 (void) event_group_name;
865 (void) event_name;
867 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
869 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
871 push_history (in, in->buffer);
872 if (in->history_changed)
873 history_save (ev->cfg, in->history_name, in->history);
874 in->history_changed = FALSE;
877 return TRUE;
880 /* --------------------------------------------------------------------------------------------- */
882 static void
883 input_destroy (WInput * in)
885 if (in == NULL)
887 fprintf (stderr, "Internal error: null Input *\n");
888 exit (EXIT_FAILURE);
891 input_free_completions (in);
893 /* clean history */
894 if (in->history != NULL)
896 /* history is already saved before this moment */
897 in->history = g_list_first (in->history);
898 g_list_foreach (in->history, (GFunc) g_free, NULL);
899 g_list_free (in->history);
901 g_free (in->history_name);
903 g_free (in->buffer);
904 input_free_completions (in);
905 g_free (in->init_text);
907 g_free (kill_buffer);
908 kill_buffer = NULL;
911 /* --------------------------------------------------------------------------------------------- */
913 static int
914 input_event (Gpm_Event * event, void *data)
916 WInput *in = (WInput *) data;
918 if ((event->type & GPM_DOWN) != 0)
920 in->first = FALSE;
921 input_mark_cmd (in, FALSE);
924 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
926 dlg_select_widget (in);
928 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
929 && should_show_history_button (in))
930 do_show_hist (in);
931 else
933 in->point = str_length (in->buffer);
934 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
935 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
937 input_update (in, TRUE);
939 /* A lone up mustn't do anything */
940 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
941 return MOU_NORMAL;
943 if ((event->type & GPM_DRAG) == 0)
944 input_mark_cmd (in, TRUE);
946 return MOU_NORMAL;
949 /* --------------------------------------------------------------------------------------------- */
950 /*** public functions ****************************************************************************/
951 /* --------------------------------------------------------------------------------------------- */
953 /** Create new instance of WInput object.
954 * @param y Y coordinate
955 * @param x X coordinate
956 * @param input_colors Array of used colors
957 * @param width Widget width
958 * @param def_text Default text filled in widget
959 * @param histname Name of history
960 * @param completion_flags Flags for specify type of completions
961 * @returns WInput object
963 WInput *
964 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
965 const char *histname, input_complete_t completion_flags)
967 WInput *in;
969 in = g_new (WInput, 1);
970 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
971 in->widget.options |= W_IS_INPUT;
973 memmove (in->color, input_colors, sizeof (input_colors_t));
975 in->field_width = width;
976 in->first = TRUE;
977 in->highlight = FALSE;
978 in->term_first_shown = 0;
979 in->disable_update = 0;
980 in->mark = 0;
981 in->need_push = TRUE;
982 in->is_password = FALSE;
983 in->charpoint = 0;
984 in->strip_password = FALSE;
986 /* in->buffer will be corrected in "history_load" event handler */
987 in->current_max_size = width + 1;
988 in->buffer = g_new0 (char, in->current_max_size);
989 in->point = 0;
991 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
993 in->completions = NULL;
994 in->completion_flags = completion_flags;
996 /* prepare to history setup */
997 in->history = NULL;
998 in->history_changed = FALSE;
999 in->history_name = NULL;
1000 if ((histname != NULL) && (*histname != '\0'))
1001 in->history_name = g_strdup (histname);
1003 /* history will be loaded later */
1005 return in;
1008 /* --------------------------------------------------------------------------------------------- */
1010 cb_ret_t
1011 input_callback (Widget * w, widget_msg_t msg, int parm)
1013 WInput *in = (WInput *) w;
1014 cb_ret_t v;
1016 switch (msg)
1018 case WIDGET_INIT:
1019 /* subscribe to "history_load" event */
1020 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1021 /* subscribe to "history_save" event */
1022 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1023 return MSG_HANDLED;
1025 case WIDGET_KEY:
1026 if (parm == XCTRL ('q'))
1028 quote = 1;
1029 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1030 quote = 0;
1031 return v;
1034 /* Keys we want others to handle */
1035 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1036 || parm == KEY_F (10) || parm == '\n')
1037 return MSG_NOT_HANDLED;
1039 /* When pasting multiline text, insert literal Enter */
1040 if ((parm & ~KEY_M_MASK) == '\n')
1042 quote = 1;
1043 v = input_handle_char (in, '\n');
1044 quote = 0;
1045 return v;
1048 return input_handle_char (in, parm);
1050 case WIDGET_COMMAND:
1051 return input_execute_cmd (in, parm);
1053 case WIDGET_FOCUS:
1054 case WIDGET_UNFOCUS:
1055 case WIDGET_DRAW:
1056 input_update (in, FALSE);
1057 return MSG_HANDLED;
1059 case WIDGET_CURSOR:
1060 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1061 - in->term_first_shown);
1062 return MSG_HANDLED;
1064 case WIDGET_DESTROY:
1065 /* unsubscribe from "history_load" event */
1066 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1067 /* unsubscribe from "history_save" event */
1068 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1069 input_destroy (in);
1070 return MSG_HANDLED;
1072 default:
1073 return default_proc (msg, parm);
1077 /* --------------------------------------------------------------------------------------------- */
1079 /** Get default colors for WInput widget.
1080 * @returns default colors
1082 const int *
1083 input_get_default_colors (void)
1085 static input_colors_t standart_colors;
1087 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1088 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1089 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1090 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1092 return standart_colors;
1095 /* --------------------------------------------------------------------------------------------- */
1097 void
1098 input_set_origin (WInput * in, int x, int field_width)
1100 in->widget.x = x;
1101 in->field_width = in->widget.cols = field_width;
1102 input_update (in, FALSE);
1105 /* --------------------------------------------------------------------------------------------- */
1107 cb_ret_t
1108 input_handle_char (WInput * in, int key)
1110 cb_ret_t v;
1111 unsigned long command;
1113 v = MSG_NOT_HANDLED;
1115 if (quote != 0)
1117 input_free_completions (in);
1118 v = insert_char (in, key);
1119 input_update (in, TRUE);
1120 quote = 0;
1121 return v;
1124 command = keybind_lookup_keymap_command (input_map, key);
1126 if (command == CK_IgnoreKey)
1128 if (key > 255)
1129 return MSG_NOT_HANDLED;
1130 if (in->first)
1131 port_region_marked_for_delete (in);
1132 input_free_completions (in);
1133 v = insert_char (in, key);
1135 else
1137 if (command != CK_Complete)
1138 input_free_completions (in);
1139 input_execute_cmd (in, command);
1140 v = MSG_HANDLED;
1141 if (in->first)
1142 input_update (in, TRUE); /* needed to clear in->first */
1145 input_update (in, TRUE);
1146 return v;
1149 /* --------------------------------------------------------------------------------------------- */
1151 /* This function is a test for a special input key used in complete.c */
1152 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1153 and 2 if it is a complete key */
1155 input_key_is_in_map (WInput * in, int key)
1157 unsigned long command;
1159 (void) in;
1161 command = keybind_lookup_keymap_command (input_map, key);
1162 if (command == CK_IgnoreKey)
1163 return 0;
1165 return (command == CK_Complete) ? 2 : 1;
1168 /* --------------------------------------------------------------------------------------------- */
1170 void
1171 input_assign_text (WInput * in, const char *text)
1173 input_free_completions (in);
1174 g_free (in->buffer);
1175 in->buffer = g_strdup (text); /* was in->buffer->text */
1176 in->current_max_size = strlen (in->buffer) + 1;
1177 in->point = str_length (in->buffer);
1178 in->mark = 0;
1179 in->need_push = TRUE;
1180 in->charpoint = 0;
1183 /* --------------------------------------------------------------------------------------------- */
1185 /* Inserts text in input line */
1186 void
1187 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1189 input_disable_update (in);
1190 while (*text != '\0')
1191 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1192 if (insert_extra_space)
1193 input_handle_char (in, ' ');
1194 input_enable_update (in);
1195 input_update (in, TRUE);
1198 /* --------------------------------------------------------------------------------------------- */
1200 void
1201 input_set_point (WInput * in, int pos)
1203 int max_pos;
1205 max_pos = str_length (in->buffer);
1206 pos = min (pos, max_pos);
1207 if (pos != in->point)
1208 input_free_completions (in);
1209 in->point = pos;
1210 in->charpoint = 0;
1211 input_update (in, TRUE);
1214 /* --------------------------------------------------------------------------------------------- */
1216 void
1217 input_update (WInput * in, gboolean clear_first)
1219 int has_history = 0;
1220 int i;
1221 int buf_len;
1222 const char *cp;
1223 int pw;
1225 if (should_show_history_button (in))
1226 has_history = HISTORY_BUTTON_WIDTH;
1228 if (in->disable_update != 0)
1229 return;
1231 buf_len = str_length (in->buffer);
1232 pw = str_term_width2 (in->buffer, in->point);
1234 /* Make the point visible */
1235 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1237 in->term_first_shown = pw - (in->field_width / 3);
1238 if (in->term_first_shown < 0)
1239 in->term_first_shown = 0;
1242 /* Adjust the mark */
1243 in->mark = min (in->mark, buf_len);
1245 if (has_history != 0)
1246 draw_history_button (in);
1248 if ((((Widget *) in)->options & W_DISABLED) != 0)
1249 tty_setcolor (DISABLED_COLOR);
1250 else if (in->first)
1251 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1252 else
1253 tty_setcolor (in->color[WINPUTC_MAIN]);
1255 widget_move (&in->widget, 0, 0);
1257 if (!in->is_password)
1259 if (!in->highlight)
1260 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1261 in->field_width - has_history));
1262 else
1264 long m1, m2;
1266 if (input_eval_marks (in, &m1, &m2))
1268 tty_setcolor (in->color[WINPUTC_MAIN]);
1269 cp = str_term_substring (in->buffer, in->term_first_shown,
1270 in->field_width - has_history);
1271 tty_print_string (cp);
1272 tty_setcolor (in->color[WINPUTC_MARK]);
1273 if (m1 < in->term_first_shown)
1275 widget_move (&in->widget, 0, 0);
1276 tty_print_string (str_term_substring
1277 (in->buffer, in->term_first_shown,
1278 m2 - in->term_first_shown));
1280 else
1282 int sel_width;
1284 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1285 sel_width =
1286 min (m2 - m1,
1287 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1288 in->term_first_shown));
1289 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1294 else
1296 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1297 tty_setcolor (in->color[WINPUTC_MAIN]);
1298 for (i = 0; i < in->field_width - has_history; i++)
1300 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1301 tty_print_char ('*');
1302 else
1303 tty_print_char (' ');
1304 if (cp[0] != '\0')
1305 str_cnext_char (&cp);
1309 if (clear_first)
1310 in->first = FALSE;
1313 /* --------------------------------------------------------------------------------------------- */
1315 void
1316 input_enable_update (WInput * in)
1318 in->disable_update--;
1319 input_update (in, FALSE);
1322 /* --------------------------------------------------------------------------------------------- */
1324 void
1325 input_disable_update (WInput * in)
1327 in->disable_update++;
1330 /* --------------------------------------------------------------------------------------------- */
1333 * Cleans the input line and adds the current text to the history
1335 * @param in the input line
1337 void
1338 input_clean (WInput * in)
1340 push_history (in, in->buffer);
1341 in->need_push = TRUE;
1342 in->buffer[0] = '\0';
1343 in->point = 0;
1344 in->charpoint = 0;
1345 in->mark = 0;
1346 in->highlight = FALSE;
1347 input_free_completions (in);
1348 input_update (in, FALSE);
1351 /* --------------------------------------------------------------------------------------------- */
1353 void
1354 input_free_completions (WInput * in)
1356 g_strfreev (in->completions);
1357 in->completions = NULL;
1360 /* --------------------------------------------------------------------------------------------- */