Ticket #2653 (fixed: input password)
[midnight-commander.git] / lib / widget / input.c
blobf863144041c27cd19e781adcc9019d825913947b
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 /* --------------------------------------------------------------------------------------------- */
208 static void
209 push_history (WInput * in, const char *text)
211 /* input widget where urls with passwords are entered without any
212 vfs prefix */
213 const char *password_input_fields[] = {
214 " Link to a remote machine ",
215 " FTP to machine ",
216 " SMB link to machine "
218 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
220 char *t;
221 size_t i;
222 gboolean empty;
224 if (text == NULL)
225 return;
227 #ifdef ENABLE_NLS
228 for (i = 0; i < ELEMENTS; i++)
229 password_input_fields[i] = _(password_input_fields[i]);
230 #endif
232 t = g_strstrip (g_strdup (text));
233 empty = *t == '\0';
234 g_free (t);
235 t = g_strdup (empty ? "" : text);
237 if (in->history_name != NULL)
239 /* FIXME: It is the strange code. Rewrite is needed. */
241 const char *p = in->history_name + 3;
243 for (i = 0; i < ELEMENTS; i++)
244 if (strcmp (p, password_input_fields[i]) == 0)
245 break;
247 strip_password (t, i >= ELEMENTS);
250 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
251 in->history_changed)
253 in->history = list_append_unique (in->history, t);
254 in->history_changed = TRUE;
256 else
257 g_free (t);
259 in->need_push = FALSE;
262 /* --------------------------------------------------------------------------------------------- */
264 static void
265 move_buffer_backward (WInput * in, int start, int end)
267 int i, pos, len;
268 int str_len;
270 str_len = str_length (in->buffer);
271 if (start >= str_len || end > str_len + 1)
272 return;
274 pos = str_offset_to_pos (in->buffer, start);
275 len = str_offset_to_pos (in->buffer, end) - pos;
277 for (i = pos; in->buffer[i + len - 1]; i++)
278 in->buffer[i] = in->buffer[i + len];
281 /* --------------------------------------------------------------------------------------------- */
283 static cb_ret_t
284 insert_char (WInput * in, int c_code)
286 size_t i;
287 int res;
289 if (in->highlight)
291 long m1, m2;
292 if (input_eval_marks (in, &m1, &m2))
293 delete_region (in, m1, m2);
295 if (c_code == -1)
296 return MSG_NOT_HANDLED;
298 if (in->charpoint >= MB_LEN_MAX)
299 return MSG_HANDLED;
301 in->charbuf[in->charpoint] = c_code;
302 in->charpoint++;
304 res = str_is_valid_char (in->charbuf, in->charpoint);
305 if (res < 0)
307 if (res != -2)
308 in->charpoint = 0; /* broken multibyte char, skip */
309 return MSG_HANDLED;
312 in->need_push = TRUE;
313 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
315 /* Expand the buffer */
316 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
317 char *narea = g_try_renew (char, in->buffer, new_length);
318 if (narea)
320 in->buffer = narea;
321 in->current_max_size = new_length;
325 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
327 /* bytes from begin */
328 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
329 /* move chars */
330 size_t rest_bytes = strlen (in->buffer + ins_point);
332 for (i = rest_bytes + 1; i > 0; i--)
333 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
335 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
336 in->point++;
339 in->charpoint = 0;
340 return MSG_HANDLED;
343 /* --------------------------------------------------------------------------------------------- */
345 static void
346 beginning_of_line (WInput * in)
348 in->point = 0;
349 in->charpoint = 0;
352 /* --------------------------------------------------------------------------------------------- */
354 static void
355 end_of_line (WInput * in)
357 in->point = str_length (in->buffer);
358 in->charpoint = 0;
361 /* --------------------------------------------------------------------------------------------- */
363 static void
364 backward_char (WInput * in)
366 const char *act;
368 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
369 if (in->point > 0)
370 in->point -= str_cprev_noncomb_char (&act, in->buffer);
371 in->charpoint = 0;
374 /* --------------------------------------------------------------------------------------------- */
376 static void
377 forward_char (WInput * in)
379 const char *act;
381 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
382 if (act[0] != '\0')
383 in->point += str_cnext_noncomb_char (&act);
384 in->charpoint = 0;
387 /* --------------------------------------------------------------------------------------------- */
389 static void
390 forward_word (WInput * in)
392 const char *p;
394 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
395 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
397 str_cnext_char (&p);
398 in->point++;
400 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
402 str_cnext_char (&p);
403 in->point++;
407 /* --------------------------------------------------------------------------------------------- */
409 static void
410 backward_word (WInput * in)
412 const char *p, *p_tmp;
414 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
415 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
417 while (p != in->buffer)
419 p_tmp = p;
420 str_cprev_char (&p);
421 if (!str_isspace (p) && !str_ispunct (p))
423 p = p_tmp;
424 break;
426 in->point--;
428 while (p != in->buffer)
430 str_cprev_char (&p);
431 if (str_isspace (p) || str_ispunct (p))
432 break;
434 in->point--;
438 /* --------------------------------------------------------------------------------------------- */
440 static void
441 backward_delete (WInput * in)
443 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
444 int start;
446 if (in->point == 0)
447 return;
449 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
450 move_buffer_backward (in, start, in->point);
451 in->charpoint = 0;
452 in->need_push = TRUE;
453 in->point = start;
456 /* --------------------------------------------------------------------------------------------- */
458 static void
459 delete_char (WInput * in)
461 const char *act;
462 int end = in->point;
464 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
465 end += str_cnext_noncomb_char (&act);
467 move_buffer_backward (in, in->point, end);
468 in->charpoint = 0;
469 in->need_push = TRUE;
472 /* --------------------------------------------------------------------------------------------- */
474 static void
475 copy_region (WInput * in, int x_first, int x_last)
477 int first = min (x_first, x_last);
478 int last = max (x_first, x_last);
480 if (last == first)
482 /* Copy selected files to clipboard */
483 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
484 /* try use external clipboard utility */
485 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
486 return;
489 g_free (kill_buffer);
491 first = str_offset_to_pos (in->buffer, first);
492 last = str_offset_to_pos (in->buffer, last);
494 kill_buffer = g_strndup (in->buffer + first, last - first);
496 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
497 /* try use external clipboard utility */
498 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
501 /* --------------------------------------------------------------------------------------------- */
503 static void
504 kill_word (WInput * in)
506 int old_point = in->point;
507 int new_point;
509 forward_word (in);
510 new_point = in->point;
511 in->point = old_point;
513 delete_region (in, old_point, new_point);
514 in->need_push = TRUE;
515 in->charpoint = 0;
518 /* --------------------------------------------------------------------------------------------- */
520 static void
521 back_kill_word (WInput * in)
523 int old_point = in->point;
524 int new_point;
526 backward_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;
534 /* --------------------------------------------------------------------------------------------- */
536 static void
537 yank (WInput * in)
539 if (kill_buffer != NULL)
541 char *p;
543 in->charpoint = 0;
544 for (p = kill_buffer; *p != '\0'; p++)
545 insert_char (in, *p);
546 in->charpoint = 0;
550 /* --------------------------------------------------------------------------------------------- */
552 static void
553 kill_line (WInput * in)
555 int chp;
557 chp = str_offset_to_pos (in->buffer, in->point);
558 g_free (kill_buffer);
559 kill_buffer = g_strdup (&in->buffer[chp]);
560 in->buffer[chp] = '\0';
561 in->charpoint = 0;
564 /* --------------------------------------------------------------------------------------------- */
566 static void
567 clear_line (WInput * in)
569 in->need_push = TRUE;
570 in->buffer[0] = '\0';
571 in->point = 0;
572 in->mark = 0;
573 in->highlight = FALSE;
574 in->charpoint = 0;
577 /* --------------------------------------------------------------------------------------------- */
579 static void
580 ins_from_clip (WInput * in)
582 char *p = NULL;
583 ev_clipboard_text_from_file_t event_data;
585 /* try use external clipboard utility */
586 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
588 event_data.text = &p;
589 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
590 if (event_data.ret)
592 char *pp;
594 for (pp = p; *pp != '\0'; pp++)
595 insert_char (in, *pp);
597 g_free (p);
601 /* --------------------------------------------------------------------------------------------- */
603 static void
604 hist_prev (WInput * in)
606 GList *prev;
608 if (in->history == NULL)
609 return;
611 if (in->need_push)
612 push_history (in, in->buffer);
614 prev = g_list_previous (in->history);
615 if (prev != NULL)
617 in->history = prev;
618 in->history_changed = TRUE;
619 input_assign_text (in, (char *) prev->data);
620 in->need_push = FALSE;
624 /* --------------------------------------------------------------------------------------------- */
626 static void
627 hist_next (WInput * in)
629 GList *next;
631 if (in->need_push)
633 push_history (in, in->buffer);
634 input_assign_text (in, "");
635 return;
638 if (in->history == NULL)
639 return;
641 next = g_list_next (in->history);
642 if (next == NULL)
643 input_assign_text (in, "");
644 else
646 in->history = next;
647 in->history_changed = TRUE;
648 input_assign_text (in, (char *) next->data);
649 in->need_push = FALSE;
653 /* --------------------------------------------------------------------------------------------- */
655 static void
656 port_region_marked_for_delete (WInput * in)
658 in->buffer[0] = '\0';
659 in->point = 0;
660 in->first = FALSE;
661 in->charpoint = 0;
664 /* --------------------------------------------------------------------------------------------- */
666 static cb_ret_t
667 input_execute_cmd (WInput * in, unsigned long command)
669 cb_ret_t res = MSG_HANDLED;
671 /* a highlight command like shift-arrow */
672 if (command == CK_MarkLeft || command == CK_MarkRight ||
673 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
674 command == CK_MarkToHome || command == CK_MarkToEnd)
676 if (!in->highlight)
678 input_mark_cmd (in, FALSE); /* clear */
679 input_mark_cmd (in, TRUE); /* marking on */
683 switch (command)
685 case CK_WordRight:
686 case CK_WordLeft:
687 case CK_Right:
688 case CK_Left:
689 if (in->highlight)
690 input_mark_cmd (in, FALSE);
693 switch (command)
695 case CK_Home:
696 case CK_MarkToHome:
697 beginning_of_line (in);
698 break;
699 case CK_End:
700 case CK_MarkToEnd:
701 end_of_line (in);
702 break;
703 case CK_Left:
704 case CK_MarkLeft:
705 backward_char (in);
706 break;
707 case CK_WordLeft:
708 case CK_MarkToWordBegin:
709 backward_word (in);
710 break;
711 case CK_Right:
712 case CK_MarkRight:
713 forward_char (in);
714 break;
715 case CK_WordRight:
716 case CK_MarkToWordEnd:
717 forward_word (in);
718 break;
719 case CK_BackSpace:
720 if (in->highlight)
722 long m1, m2;
723 if (input_eval_marks (in, &m1, &m2))
724 delete_region (in, m1, m2);
726 else
727 backward_delete (in);
728 break;
729 case CK_Delete:
730 if (in->first)
731 port_region_marked_for_delete (in);
732 else if (in->highlight)
734 long m1, m2;
735 if (input_eval_marks (in, &m1, &m2))
736 delete_region (in, m1, m2);
738 else
739 delete_char (in);
740 break;
741 case CK_DeleteToWordEnd:
742 kill_word (in);
743 break;
744 case CK_DeleteToWordBegin:
745 back_kill_word (in);
746 break;
747 case CK_Mark:
748 input_mark_cmd (in, TRUE);
749 break;
750 case CK_Remove:
751 delete_region (in, in->point, in->mark);
752 break;
753 case CK_DeleteToEnd:
754 kill_line (in);
755 break;
756 case CK_Clear:
757 clear_line (in);
758 break;
759 case CK_Store:
760 copy_region (in, in->mark, in->point);
761 break;
762 case CK_Cut:
763 copy_region (in, in->mark, in->point);
764 delete_region (in, in->point, in->mark);
765 break;
766 case CK_Yank:
767 yank (in);
768 break;
769 case CK_Paste:
770 ins_from_clip (in);
771 break;
772 case CK_HistoryPrev:
773 hist_prev (in);
774 break;
775 case CK_HistoryNext:
776 hist_next (in);
777 break;
778 case CK_History:
779 do_show_hist (in);
780 break;
781 case CK_Complete:
782 complete (in);
783 break;
784 default:
785 res = MSG_NOT_HANDLED;
788 if (command != CK_MarkLeft && command != CK_MarkRight &&
789 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
790 command != CK_MarkToHome && command != CK_MarkToEnd)
791 in->highlight = FALSE;
793 return res;
796 /* --------------------------------------------------------------------------------------------- */
798 /* "history_load" event handler */
799 static gboolean
800 input_load_history (const gchar * event_group_name, const gchar * event_name,
801 gpointer init_data, gpointer data)
803 WInput *in = (WInput *) init_data;
804 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
805 const char *def_text;
806 size_t buffer_len;
808 (void) event_group_name;
809 (void) event_name;
811 in->history = history_load (ev->cfg, in->history_name);
813 if (in->init_text == NULL)
814 def_text = "";
815 else if (in->init_text == INPUT_LAST_TEXT)
817 if (in->history != NULL && in->history->data != NULL)
818 def_text = (const char *) in->history->data;
819 else
820 def_text = "";
822 in->init_text = NULL;
824 else
825 def_text = in->init_text;
827 buffer_len = strlen (def_text);
828 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
829 in->current_max_size = buffer_len;
830 if (buffer_len > (size_t) in->field_width)
831 in->buffer = g_realloc (in->buffer, buffer_len);
832 strcpy (in->buffer, def_text);
833 in->point = str_length (in->buffer);
835 return TRUE;
838 /* --------------------------------------------------------------------------------------------- */
840 /* "history_save" event handler */
841 static gboolean
842 input_save_history (const gchar * event_group_name, const gchar * event_name,
843 gpointer init_data, gpointer data)
845 WInput *in = (WInput *) init_data;
847 (void) event_group_name;
848 (void) event_name;
850 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
852 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
854 push_history (in, in->buffer);
855 if (in->history_changed)
856 history_save (ev->cfg, in->history_name, in->history);
857 in->history_changed = FALSE;
860 return TRUE;
863 /* --------------------------------------------------------------------------------------------- */
865 static void
866 input_destroy (WInput * in)
868 if (in == NULL)
870 fprintf (stderr, "Internal error: null Input *\n");
871 exit (EXIT_FAILURE);
874 input_free_completions (in);
876 /* clean history */
877 if (in->history != NULL)
879 /* history is already saved before this moment */
880 in->history = g_list_first (in->history);
881 g_list_foreach (in->history, (GFunc) g_free, NULL);
882 g_list_free (in->history);
884 g_free (in->history_name);
886 g_free (in->buffer);
887 input_free_completions (in);
888 g_free (in->init_text);
890 g_free (kill_buffer);
891 kill_buffer = NULL;
894 /* --------------------------------------------------------------------------------------------- */
896 static int
897 input_event (Gpm_Event * event, void *data)
899 WInput *in = (WInput *) data;
901 if ((event->type & GPM_DOWN) != 0)
903 in->first = FALSE;
904 input_mark_cmd (in, FALSE);
907 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
909 dlg_select_widget (in);
911 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
912 && should_show_history_button (in))
913 do_show_hist (in);
914 else
916 in->point = str_length (in->buffer);
917 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
918 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
920 input_update (in, TRUE);
922 /* A lone up mustn't do anything */
923 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
924 return MOU_NORMAL;
926 if ((event->type & GPM_DRAG) == 0)
927 input_mark_cmd (in, TRUE);
929 return MOU_NORMAL;
932 /* --------------------------------------------------------------------------------------------- */
933 /*** public functions ****************************************************************************/
934 /* --------------------------------------------------------------------------------------------- */
936 /** Create new instance of WInput object.
937 * @param y Y coordinate
938 * @param x X coordinate
939 * @param input_colors Array of used colors
940 * @param width Widget width
941 * @param def_text Default text filled in widget
942 * @param histname Name of history
943 * @param completion_flags Flags for specify type of completions
944 * @returns WInput object
946 WInput *
947 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
948 const char *histname, input_complete_t completion_flags)
950 WInput *in;
952 in = g_new (WInput, 1);
953 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
954 in->widget.options |= W_IS_INPUT;
956 memmove (in->color, input_colors, sizeof (input_colors_t));
958 in->field_width = width;
959 in->first = TRUE;
960 in->highlight = FALSE;
961 in->term_first_shown = 0;
962 in->disable_update = 0;
963 in->mark = 0;
964 in->need_push = TRUE;
965 in->is_password = FALSE;
966 in->charpoint = 0;
968 /* in->buffer will be corrected in "history_load" event handler */
969 in->current_max_size = width + 1;
970 in->buffer = g_new0 (char, in->current_max_size);
971 in->point = 0;
973 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
975 in->completions = NULL;
976 in->completion_flags = completion_flags;
978 /* prepare to history setup */
979 in->history = NULL;
980 in->history_changed = FALSE;
981 in->history_name = NULL;
982 if ((histname != NULL) && (*histname != '\0'))
983 in->history_name = g_strdup (histname);
985 /* history will be loaded later */
987 return in;
990 /* --------------------------------------------------------------------------------------------- */
992 cb_ret_t
993 input_callback (Widget * w, widget_msg_t msg, int parm)
995 WInput *in = (WInput *) w;
996 cb_ret_t v;
998 switch (msg)
1000 case WIDGET_INIT:
1001 /* subscribe to "history_load" event */
1002 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1003 /* subscribe to "history_save" event */
1004 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1005 return MSG_HANDLED;
1007 case WIDGET_KEY:
1008 if (parm == XCTRL ('q'))
1010 quote = 1;
1011 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1012 quote = 0;
1013 return v;
1016 /* Keys we want others to handle */
1017 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1018 || parm == KEY_F (10) || parm == '\n')
1019 return MSG_NOT_HANDLED;
1021 /* When pasting multiline text, insert literal Enter */
1022 if ((parm & ~KEY_M_MASK) == '\n')
1024 quote = 1;
1025 v = input_handle_char (in, '\n');
1026 quote = 0;
1027 return v;
1030 return input_handle_char (in, parm);
1032 case WIDGET_COMMAND:
1033 return input_execute_cmd (in, parm);
1035 case WIDGET_FOCUS:
1036 case WIDGET_UNFOCUS:
1037 case WIDGET_DRAW:
1038 input_update (in, FALSE);
1039 return MSG_HANDLED;
1041 case WIDGET_CURSOR:
1042 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1043 - in->term_first_shown);
1044 return MSG_HANDLED;
1046 case WIDGET_DESTROY:
1047 /* unsubscribe from "history_load" event */
1048 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1049 /* unsubscribe from "history_save" event */
1050 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1051 input_destroy (in);
1052 return MSG_HANDLED;
1054 default:
1055 return default_proc (msg, parm);
1059 /* --------------------------------------------------------------------------------------------- */
1061 /** Get default colors for WInput widget.
1062 * @returns default colors
1064 const int *
1065 input_get_default_colors (void)
1067 static input_colors_t standart_colors;
1069 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1070 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1071 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1072 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1074 return standart_colors;
1077 /* --------------------------------------------------------------------------------------------- */
1079 void
1080 input_set_origin (WInput * in, int x, int field_width)
1082 in->widget.x = x;
1083 in->field_width = in->widget.cols = field_width;
1084 input_update (in, FALSE);
1087 /* --------------------------------------------------------------------------------------------- */
1089 cb_ret_t
1090 input_handle_char (WInput * in, int key)
1092 cb_ret_t v;
1093 unsigned long command;
1095 v = MSG_NOT_HANDLED;
1097 if (quote != 0)
1099 input_free_completions (in);
1100 v = insert_char (in, key);
1101 input_update (in, TRUE);
1102 quote = 0;
1103 return v;
1106 command = keybind_lookup_keymap_command (input_map, key);
1108 if (command == CK_IgnoreKey)
1110 if (key > 255)
1111 return MSG_NOT_HANDLED;
1112 if (in->first)
1113 port_region_marked_for_delete (in);
1114 input_free_completions (in);
1115 v = insert_char (in, key);
1117 else
1119 if (command != CK_Complete)
1120 input_free_completions (in);
1121 input_execute_cmd (in, command);
1122 v = MSG_HANDLED;
1123 if (in->first)
1124 input_update (in, TRUE); /* needed to clear in->first */
1127 input_update (in, TRUE);
1128 return v;
1131 /* --------------------------------------------------------------------------------------------- */
1133 /* This function is a test for a special input key used in complete.c */
1134 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1135 and 2 if it is a complete key */
1137 input_key_is_in_map (WInput * in, int key)
1139 unsigned long command;
1141 (void) in;
1143 command = keybind_lookup_keymap_command (input_map, key);
1144 if (command == CK_IgnoreKey)
1145 return 0;
1147 return (command == CK_Complete) ? 2 : 1;
1150 /* --------------------------------------------------------------------------------------------- */
1152 void
1153 input_assign_text (WInput * in, const char *text)
1155 input_free_completions (in);
1156 g_free (in->buffer);
1157 in->buffer = g_strdup (text); /* was in->buffer->text */
1158 in->current_max_size = strlen (in->buffer) + 1;
1159 in->point = str_length (in->buffer);
1160 in->mark = 0;
1161 in->need_push = TRUE;
1162 in->charpoint = 0;
1165 /* --------------------------------------------------------------------------------------------- */
1167 /* Inserts text in input line */
1168 void
1169 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1171 input_disable_update (in);
1172 while (*text != '\0')
1173 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1174 if (insert_extra_space)
1175 input_handle_char (in, ' ');
1176 input_enable_update (in);
1177 input_update (in, TRUE);
1180 /* --------------------------------------------------------------------------------------------- */
1182 void
1183 input_set_point (WInput * in, int pos)
1185 int max_pos;
1187 max_pos = str_length (in->buffer);
1188 pos = min (pos, max_pos);
1189 if (pos != in->point)
1190 input_free_completions (in);
1191 in->point = pos;
1192 in->charpoint = 0;
1193 input_update (in, TRUE);
1196 /* --------------------------------------------------------------------------------------------- */
1198 void
1199 input_update (WInput * in, gboolean clear_first)
1201 int has_history = 0;
1202 int i;
1203 int buf_len;
1204 const char *cp;
1205 int pw;
1207 if (should_show_history_button (in))
1208 has_history = HISTORY_BUTTON_WIDTH;
1210 if (in->disable_update != 0)
1211 return;
1213 buf_len = str_length (in->buffer);
1214 pw = str_term_width2 (in->buffer, in->point);
1216 /* Make the point visible */
1217 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1219 in->term_first_shown = pw - (in->field_width / 3);
1220 if (in->term_first_shown < 0)
1221 in->term_first_shown = 0;
1224 /* Adjust the mark */
1225 in->mark = min (in->mark, buf_len);
1227 if (has_history != 0)
1228 draw_history_button (in);
1230 if ((((Widget *) in)->options & W_DISABLED) != 0)
1231 tty_setcolor (DISABLED_COLOR);
1232 else if (in->first)
1233 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1234 else
1235 tty_setcolor (in->color[WINPUTC_MAIN]);
1237 widget_move (&in->widget, 0, 0);
1239 if (!in->is_password)
1241 if (!in->highlight)
1242 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1243 in->field_width - has_history));
1244 else
1246 long m1, m2;
1248 if (input_eval_marks (in, &m1, &m2))
1250 tty_setcolor (in->color[WINPUTC_MAIN]);
1251 cp = str_term_substring (in->buffer, in->term_first_shown,
1252 in->field_width - has_history);
1253 tty_print_string (cp);
1254 tty_setcolor (in->color[WINPUTC_MARK]);
1255 if (m1 < in->term_first_shown)
1257 widget_move (&in->widget, 0, 0);
1258 tty_print_string (str_term_substring
1259 (in->buffer, in->term_first_shown,
1260 m2 - in->term_first_shown));
1262 else
1264 int sel_width;
1266 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1267 sel_width =
1268 min (m2 - m1,
1269 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1270 in->term_first_shown));
1271 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1276 else
1278 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1279 tty_setcolor (in->color[WINPUTC_MAIN]);
1280 for (i = 0; i < in->field_width - has_history; i++)
1282 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1283 tty_print_char ('*');
1284 else
1285 tty_print_char (' ');
1286 if (cp[0] != '\0')
1287 str_cnext_char (&cp);
1291 if (clear_first)
1292 in->first = FALSE;
1295 /* --------------------------------------------------------------------------------------------- */
1297 void
1298 input_enable_update (WInput * in)
1300 in->disable_update--;
1301 input_update (in, FALSE);
1304 /* --------------------------------------------------------------------------------------------- */
1306 void
1307 input_disable_update (WInput * in)
1309 in->disable_update++;
1312 /* --------------------------------------------------------------------------------------------- */
1315 * Cleans the input line and adds the current text to the history
1317 * @param in the input line
1319 void
1320 input_clean (WInput * in)
1322 push_history (in, in->buffer);
1323 in->need_push = TRUE;
1324 in->buffer[0] = '\0';
1325 in->point = 0;
1326 in->charpoint = 0;
1327 in->mark = 0;
1328 in->highlight = FALSE;
1329 input_free_completions (in);
1330 input_update (in, FALSE);
1333 /* --------------------------------------------------------------------------------------------- */
1335 void
1336 input_free_completions (WInput * in)
1338 g_strfreev (in->completions);
1339 in->completions = NULL;
1342 /* --------------------------------------------------------------------------------------------- */