Ticket #3632: refactoring of widget flags.
[midnight-commander.git] / lib / widget / input.c
blob6f587efa38e369337a20bc0a0f0a3d315ee11e62
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
7 Authors:
8 Radek Doulik, 1994, 1995
9 Miguel de Icaza, 1994, 1995
10 Jakub Jelinek, 1995
11 Andrej Borsenkow, 1996
12 Norbert Warmuth, 1997
13 Andrew Borodin <aborodin@vmail.ru>, 2009-2016
15 This file is part of the Midnight Commander.
17 The Midnight Commander is free software: you can redistribute it
18 and/or modify it under the terms of the GNU General Public License as
19 published by the Free Software Foundation, either version 3 of the License,
20 or (at your option) any later version.
22 The Midnight Commander is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program. If not, see <http://www.gnu.org/licenses/>.
31 /** \file input.c
32 * \brief Source: WInput widget
35 #include <config.h>
37 #include <stdlib.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
41 #include "lib/global.h"
43 #include "lib/tty/tty.h"
44 #include "lib/tty/key.h" /* XCTRL and ALT macros */
45 #include "lib/fileloc.h"
46 #include "lib/skin.h"
47 #include "lib/strutil.h"
48 #include "lib/util.h"
49 #include "lib/keybind.h" /* global_keymap_t */
50 #include "lib/widget.h"
51 #include "lib/event.h" /* mc_event_raise() */
53 #include "input_complete.h"
55 /*** global variables ****************************************************************************/
57 gboolean quote = FALSE;
59 const global_keymap_t *input_map = NULL;
61 /* Color styles for input widgets */
62 input_colors_t input_colors;
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.list != NULL && WIDGET (in)->cols > HISTORY_BUTTON_WIDTH * 2 + 1 \
76 && WIDGET (in)->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 = (const GList *) 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, 0, WIDGET (in)->cols - HISTORY_BUTTON_WIDTH);
116 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
118 #ifdef LARGE_HISTORY_BUTTON
119 tty_print_string ("[ ]");
120 widget_move (in, 0, WIDGET (in)->cols - HISTORY_BUTTON_WIDTH + 1);
121 #endif
123 tty_print_char (c);
126 /* --------------------------------------------------------------------------------------------- */
128 static void
129 input_mark_cmd (WInput * in, gboolean mark)
131 in->mark = mark ? in->point : -1;
134 /* --------------------------------------------------------------------------------------------- */
136 static gboolean
137 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
139 if (in->mark >= 0)
141 *start_mark = MIN (in->mark, in->point);
142 *end_mark = MAX (in->mark, in->point);
143 return TRUE;
146 *start_mark = *end_mark = -1;
147 return FALSE;
150 /* --------------------------------------------------------------------------------------------- */
152 static void
153 delete_region (WInput * in, int x_first, int x_last)
155 int first = MIN (x_first, x_last);
156 int last = MAX (x_first, x_last);
157 size_t len;
159 input_mark_cmd (in, FALSE);
160 in->point = first;
161 last = str_offset_to_pos (in->buffer, last);
162 first = str_offset_to_pos (in->buffer, first);
163 len = strlen (&in->buffer[last]) + 1;
164 memmove (&in->buffer[first], &in->buffer[last], len);
165 in->charpoint = 0;
166 in->need_push = TRUE;
169 /* --------------------------------------------------------------------------------------------- */
171 static void
172 do_show_hist (WInput * in)
174 size_t len;
175 char *r;
177 len = get_history_length (in->history.list);
179 r = history_show (&in->history.list, WIDGET (in),
180 g_list_position (in->history.list, in->history.list));
181 if (r != NULL)
183 input_assign_text (in, r);
184 g_free (r);
187 /* Has history cleaned up or not? */
188 if (len != get_history_length (in->history.list))
189 in->history.changed = TRUE;
192 /* --------------------------------------------------------------------------------------------- */
194 * Strip password from incomplete url (just user:pass@host without VFS prefix).
196 * @param url partial URL
197 * @return newly allocated string without password
200 static char *
201 input_history_strip_password (char *url)
203 char *at, *delim, *colon;
205 at = strrchr (url, '@');
206 if (at == NULL)
207 return g_strdup (url);
209 /* TODO: handle ':' and '@' in password */
211 delim = strstr (url, VFS_PATH_URL_DELIMITER);
212 if (delim != NULL)
213 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
214 else
215 colon = strchr (url, ':');
217 /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
218 /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
219 if (colon != NULL && colon > at)
220 colon = NULL;
222 if (colon == NULL)
223 return g_strdup (url);
224 *colon = '\0';
226 return g_strconcat (url, at, (char *) NULL);
229 /* --------------------------------------------------------------------------------------------- */
231 static void
232 push_history (WInput * in, const char *text)
234 char *t;
235 gboolean empty;
237 if (text == NULL)
238 return;
240 t = g_strstrip (g_strdup (text));
241 empty = *t == '\0';
242 g_free (t);
243 t = g_strdup (empty ? "" : text);
245 if (!empty && in->history.name != NULL && in->strip_password)
248 We got string user:pass@host without any VFS prefixes
249 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
250 Therefore we want to strip password in separate algorithm
252 char *url_with_stripped_password;
254 url_with_stripped_password = input_history_strip_password (t);
255 g_free (t);
256 t = url_with_stripped_password;
259 if (in->history.list == NULL || in->history.list->data == NULL
260 || strcmp (in->history.list->data, t) != 0 || in->history.changed)
262 in->history.list = list_append_unique (in->history.list, t);
263 in->history.current = in->history.list;
264 in->history.changed = TRUE;
266 else
267 g_free (t);
269 in->need_push = FALSE;
272 /* --------------------------------------------------------------------------------------------- */
274 static void
275 move_buffer_backward (WInput * in, int start, int end)
277 int i, pos, len;
278 int str_len;
280 str_len = str_length (in->buffer);
281 if (start >= str_len || end > str_len + 1)
282 return;
284 pos = str_offset_to_pos (in->buffer, start);
285 len = str_offset_to_pos (in->buffer, end) - pos;
287 for (i = pos; in->buffer[i + len - 1]; i++)
288 in->buffer[i] = in->buffer[i + len];
291 /* --------------------------------------------------------------------------------------------- */
293 static cb_ret_t
294 insert_char (WInput * in, int c_code)
296 int res;
297 long m1, m2;
299 if (input_eval_marks (in, &m1, &m2))
300 delete_region (in, m1, m2);
302 if (c_code == -1)
303 return MSG_NOT_HANDLED;
305 if (in->charpoint >= MB_LEN_MAX)
306 return MSG_HANDLED;
308 in->charbuf[in->charpoint] = c_code;
309 in->charpoint++;
311 res = str_is_valid_char (in->charbuf, in->charpoint);
312 if (res < 0)
314 if (res != -2)
315 in->charpoint = 0; /* broken multibyte char, skip */
316 return MSG_HANDLED;
319 in->need_push = TRUE;
320 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
322 /* Expand the buffer */
323 size_t new_length;
324 char *narea;
326 new_length = in->current_max_size + WIDGET (in)->cols + in->charpoint;
327 narea = g_try_renew (char, in->buffer, new_length);
328 if (narea != NULL)
330 in->buffer = narea;
331 in->current_max_size = new_length;
335 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
337 size_t i;
338 /* bytes from begin */
339 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
340 /* move chars */
341 size_t rest_bytes = strlen (in->buffer + ins_point);
343 for (i = rest_bytes + 1; i > 0; i--)
344 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
346 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
347 in->point++;
350 in->charpoint = 0;
351 return MSG_HANDLED;
354 /* --------------------------------------------------------------------------------------------- */
356 static void
357 beginning_of_line (WInput * in)
359 in->point = 0;
360 in->charpoint = 0;
363 /* --------------------------------------------------------------------------------------------- */
365 static void
366 end_of_line (WInput * in)
368 in->point = str_length (in->buffer);
369 in->charpoint = 0;
372 /* --------------------------------------------------------------------------------------------- */
374 static void
375 backward_char (WInput * in)
377 const char *act;
379 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
380 if (in->point > 0)
381 in->point -= str_cprev_noncomb_char (&act, in->buffer);
382 in->charpoint = 0;
385 /* --------------------------------------------------------------------------------------------- */
387 static void
388 forward_char (WInput * in)
390 const char *act;
392 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
393 if (act[0] != '\0')
394 in->point += str_cnext_noncomb_char (&act);
395 in->charpoint = 0;
398 /* --------------------------------------------------------------------------------------------- */
400 static void
401 forward_word (WInput * in)
403 const char *p;
405 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
406 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
408 str_cnext_char (&p);
409 in->point++;
411 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
413 str_cnext_char (&p);
414 in->point++;
418 /* --------------------------------------------------------------------------------------------- */
420 static void
421 backward_word (WInput * in)
423 const char *p;
425 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
427 while (p != in->buffer)
429 const char *p_tmp;
431 p_tmp = p;
432 str_cprev_char (&p);
433 if (!str_isspace (p) && !str_ispunct (p))
435 p = p_tmp;
436 break;
438 in->point--;
440 while (p != in->buffer)
442 str_cprev_char (&p);
443 if (str_isspace (p) || str_ispunct (p))
444 break;
446 in->point--;
450 /* --------------------------------------------------------------------------------------------- */
452 static void
453 backward_delete (WInput * in)
455 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
456 int start;
458 if (in->point == 0)
459 return;
461 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
462 move_buffer_backward (in, start, in->point);
463 in->charpoint = 0;
464 in->need_push = TRUE;
465 in->point = start;
468 /* --------------------------------------------------------------------------------------------- */
470 static void
471 delete_char (WInput * in)
473 const char *act;
474 int end = in->point;
476 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
477 end += str_cnext_noncomb_char (&act);
479 move_buffer_backward (in, in->point, end);
480 in->charpoint = 0;
481 in->need_push = TRUE;
484 /* --------------------------------------------------------------------------------------------- */
486 static void
487 copy_region (WInput * in, int x_first, int x_last)
489 int first = MIN (x_first, x_last);
490 int last = MAX (x_first, x_last);
492 if (last == first)
494 /* Copy selected files to clipboard */
495 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file", NULL);
496 /* try use external clipboard utility */
497 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
498 return;
501 g_free (kill_buffer);
503 first = str_offset_to_pos (in->buffer, first);
504 last = str_offset_to_pos (in->buffer, last);
506 kill_buffer = g_strndup (in->buffer + first, last - first);
508 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
509 /* try use external clipboard utility */
510 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
513 /* --------------------------------------------------------------------------------------------- */
515 static void
516 kill_word (WInput * in)
518 int old_point = in->point;
519 int new_point;
521 forward_word (in);
522 new_point = in->point;
523 in->point = old_point;
525 delete_region (in, old_point, new_point);
526 in->need_push = TRUE;
527 in->charpoint = 0;
530 /* --------------------------------------------------------------------------------------------- */
532 static void
533 back_kill_word (WInput * in)
535 int old_point = in->point;
536 int new_point;
538 backward_word (in);
539 new_point = in->point;
540 in->point = old_point;
542 delete_region (in, old_point, new_point);
543 in->need_push = TRUE;
546 /* --------------------------------------------------------------------------------------------- */
548 static void
549 yank (WInput * in)
551 if (kill_buffer != NULL)
553 char *p;
555 in->charpoint = 0;
556 for (p = kill_buffer; *p != '\0'; p++)
557 insert_char (in, *p);
558 in->charpoint = 0;
562 /* --------------------------------------------------------------------------------------------- */
564 static void
565 kill_line (WInput * in)
567 int chp;
569 chp = str_offset_to_pos (in->buffer, in->point);
570 g_free (kill_buffer);
571 kill_buffer = g_strdup (&in->buffer[chp]);
572 in->buffer[chp] = '\0';
573 in->charpoint = 0;
576 /* --------------------------------------------------------------------------------------------- */
578 static void
579 clear_line (WInput * in)
581 in->need_push = TRUE;
582 in->buffer[0] = '\0';
583 in->point = 0;
584 in->mark = -1;
585 in->charpoint = 0;
588 /* --------------------------------------------------------------------------------------------- */
590 static void
591 ins_from_clip (WInput * in)
593 char *p = NULL;
594 ev_clipboard_text_from_file_t event_data;
596 /* try use external clipboard utility */
597 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
599 event_data.text = &p;
600 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
601 if (event_data.ret)
603 char *pp;
605 for (pp = p; *pp != '\0'; pp++)
606 insert_char (in, *pp);
608 g_free (p);
612 /* --------------------------------------------------------------------------------------------- */
614 static void
615 hist_prev (WInput * in)
617 GList *prev;
619 if (in->history.list == NULL)
620 return;
622 if (in->need_push)
623 push_history (in, in->buffer);
625 prev = g_list_previous (in->history.current);
626 if (prev != NULL)
628 input_assign_text (in, (char *) prev->data);
629 in->history.current = prev;
630 in->history.changed = TRUE;
631 in->need_push = FALSE;
635 /* --------------------------------------------------------------------------------------------- */
637 static void
638 hist_next (WInput * in)
640 GList *next;
642 if (in->need_push)
644 push_history (in, in->buffer);
645 input_assign_text (in, "");
646 return;
649 if (in->history.list == NULL)
650 return;
652 next = g_list_next (in->history.current);
653 if (next == NULL)
655 input_assign_text (in, "");
656 in->history.current = in->history.list;
658 else
660 input_assign_text (in, (char *) next->data);
661 in->history.current = next;
662 in->history.changed = TRUE;
663 in->need_push = FALSE;
667 /* --------------------------------------------------------------------------------------------- */
669 static void
670 port_region_marked_for_delete (WInput * in)
672 in->buffer[0] = '\0';
673 in->point = 0;
674 in->first = FALSE;
675 in->charpoint = 0;
678 /* --------------------------------------------------------------------------------------------- */
680 static cb_ret_t
681 input_execute_cmd (WInput * in, long command)
683 cb_ret_t res = MSG_HANDLED;
685 switch (command)
687 case CK_MarkLeft:
688 case CK_MarkRight:
689 case CK_MarkToWordBegin:
690 case CK_MarkToWordEnd:
691 case CK_MarkToHome:
692 case CK_MarkToEnd:
693 /* a highlight command like shift-arrow */
694 if (in->mark < 0)
696 input_mark_cmd (in, FALSE); /* clear */
697 input_mark_cmd (in, TRUE); /* marking on */
699 break;
700 case CK_WordRight:
701 case CK_WordLeft:
702 case CK_Right:
703 case CK_Left:
704 if (in->mark >= 0)
705 input_mark_cmd (in, FALSE);
706 break;
707 default:
708 break;
711 switch (command)
713 case CK_Home:
714 case CK_MarkToHome:
715 beginning_of_line (in);
716 break;
717 case CK_End:
718 case CK_MarkToEnd:
719 end_of_line (in);
720 break;
721 case CK_Left:
722 case CK_MarkLeft:
723 backward_char (in);
724 break;
725 case CK_WordLeft:
726 case CK_MarkToWordBegin:
727 backward_word (in);
728 break;
729 case CK_Right:
730 case CK_MarkRight:
731 forward_char (in);
732 break;
733 case CK_WordRight:
734 case CK_MarkToWordEnd:
735 forward_word (in);
736 break;
737 case CK_BackSpace:
739 long m1, m2;
741 if (input_eval_marks (in, &m1, &m2))
742 delete_region (in, m1, m2);
743 else
744 backward_delete (in);
746 break;
747 case CK_Delete:
748 if (in->first)
749 port_region_marked_for_delete (in);
750 else
752 long m1, m2;
754 if (input_eval_marks (in, &m1, &m2))
755 delete_region (in, m1, m2);
756 else
757 delete_char (in);
759 break;
760 case CK_DeleteToWordEnd:
761 kill_word (in);
762 break;
763 case CK_DeleteToWordBegin:
764 back_kill_word (in);
765 break;
766 case CK_Mark:
767 input_mark_cmd (in, TRUE);
768 break;
769 case CK_Remove:
770 delete_region (in, in->point, MAX (in->mark, 0));
771 break;
772 case CK_DeleteToEnd:
773 kill_line (in);
774 break;
775 case CK_Clear:
776 clear_line (in);
777 break;
778 case CK_Store:
779 copy_region (in, MAX (in->mark, 0), in->point);
780 break;
781 case CK_Cut:
783 long m;
785 m = MAX (in->mark, 0);
786 copy_region (in, m, in->point);
787 delete_region (in, in->point, m);
789 break;
790 case CK_Yank:
791 yank (in);
792 break;
793 case CK_Paste:
794 ins_from_clip (in);
795 break;
796 case CK_HistoryPrev:
797 hist_prev (in);
798 break;
799 case CK_HistoryNext:
800 hist_next (in);
801 break;
802 case CK_History:
803 do_show_hist (in);
804 break;
805 case CK_Complete:
806 complete (in);
807 break;
808 default:
809 res = MSG_NOT_HANDLED;
812 switch (command)
814 case CK_MarkLeft:
815 case CK_MarkRight:
816 case CK_MarkToWordBegin:
817 case CK_MarkToWordEnd:
818 case CK_MarkToHome:
819 case CK_MarkToEnd:
820 /* do nothing */
821 break;
822 default:
823 in->mark = -1;
824 break;
827 return res;
830 /* --------------------------------------------------------------------------------------------- */
832 /* "history_load" event handler */
833 static gboolean
834 input_load_history (const gchar * event_group_name, const gchar * event_name,
835 gpointer init_data, gpointer data)
837 WInput *in = INPUT (init_data);
838 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
840 (void) event_group_name;
841 (void) event_name;
843 in->history.list = history_load (ev->cfg, in->history.name);
844 in->history.current = in->history.list;
846 if (in->init_from_history)
848 const char *def_text = "";
850 if (in->history.list != NULL && in->history.list->data != NULL)
851 def_text = (const char *) in->history.list->data;
853 input_assign_text (in, def_text);
856 return TRUE;
859 /* --------------------------------------------------------------------------------------------- */
861 /* "history_save" event handler */
862 static gboolean
863 input_save_history (const gchar * event_group_name, const gchar * event_name,
864 gpointer init_data, gpointer data)
866 WInput *in = INPUT (init_data);
868 (void) event_group_name;
869 (void) event_name;
871 if (!in->is_password && (WIDGET (in)->owner->ret_value != B_CANCEL))
873 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
875 push_history (in, in->buffer);
876 if (in->history.changed)
877 history_save (ev->cfg, in->history.name, in->history.list);
878 in->history.changed = FALSE;
881 return TRUE;
884 /* --------------------------------------------------------------------------------------------- */
886 static void
887 input_destroy (WInput * in)
889 if (in == NULL)
891 fprintf (stderr, "Internal error: null Input *\n");
892 exit (EXIT_FAILURE);
895 input_free_completions (in);
897 /* clean history */
898 if (in->history.list != NULL)
900 /* history is already saved before this moment */
901 in->history.list = g_list_first (in->history.list);
902 g_list_free_full (in->history.list, g_free);
904 g_free (in->history.name);
905 g_free (in->buffer);
906 MC_PTR_FREE (kill_buffer);
909 /* --------------------------------------------------------------------------------------------- */
912 * Calculates the buffer index (aka "point") corresponding to some screen coordinate.
914 static int
915 input_screen_to_point (const WInput * in, int x)
917 x += in->term_first_shown;
919 if (x < 0)
920 return 0;
922 if (x < str_term_width1 (in->buffer))
923 return str_column_to_pos (in->buffer, x);
925 return str_length (in->buffer);
928 /* --------------------------------------------------------------------------------------------- */
930 static void
931 input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
933 /* save point between MSG_MOUSE_DOWN and MSG_MOUSE_DRAG */
934 static int prev_point = 0;
935 WInput *in = INPUT (w);
937 switch (msg)
939 case MSG_MOUSE_DOWN:
940 dlg_select_widget (w);
941 in->first = FALSE;
943 if (event->x >= w->cols - HISTORY_BUTTON_WIDTH && should_show_history_button (in))
944 do_show_hist (in);
945 else
947 input_mark_cmd (in, FALSE);
948 input_set_point (in, input_screen_to_point (in, event->x));
949 /* save point for the possible following MSG_MOUSE_DRAG action */
950 prev_point = in->point;
952 break;
954 case MSG_MOUSE_DRAG:
955 /* start point: set marker using point before first MSG_MOUSE_DRAG action */
956 if (in->mark < 0)
957 in->mark = prev_point;
959 input_set_point (in, input_screen_to_point (in, event->x));
960 break;
962 default:
963 /* don't create highlight region of 0 length */
964 if (in->mark == in->point)
965 input_mark_cmd (in, FALSE);
966 break;
970 /* --------------------------------------------------------------------------------------------- */
973 * Callback for applying new options to input widget.
975 * @param w widget
976 * @param options options set
977 * @param enable TRUE if specified options should be added, FALSE if options should be removed
979 static void
980 input_set_options (Widget * w, widget_options_t options, gboolean enable)
982 WInput *in = INPUT (w);
984 widget_default_set_options (w, options, enable);
985 if (in->label != NULL)
986 widget_set_options (WIDGET (in->label), options, enable);
989 /* --------------------------------------------------------------------------------------------- */
990 /*** public functions ****************************************************************************/
991 /* --------------------------------------------------------------------------------------------- */
993 /** Create new instance of WInput object.
994 * @param y Y coordinate
995 * @param x X coordinate
996 * @param input_colors Array of used colors
997 * @param width Widget width
998 * @param def_text Default text filled in widget
999 * @param histname Name of history
1000 * @param completion_flags Flags for specify type of completions
1001 * @return WInput object
1003 WInput *
1004 input_new (int y, int x, const int *colors, int width, const char *def_text,
1005 const char *histname, input_complete_t completion_flags)
1007 WInput *in;
1008 Widget *w;
1010 in = g_new (WInput, 1);
1011 w = WIDGET (in);
1012 widget_init (w, y, x, 1, width, input_callback, input_mouse_callback);
1013 w->options |= W_IS_INPUT;
1014 w->set_options = input_set_options;
1016 in->color = colors;
1017 in->first = TRUE;
1018 in->mark = -1;
1019 in->term_first_shown = 0;
1020 in->disable_update = 0;
1021 in->is_password = FALSE;
1022 in->strip_password = FALSE;
1024 /* in->buffer will be corrected in "history_load" event handler */
1025 in->current_max_size = width + 1;
1026 in->buffer = g_new0 (char, in->current_max_size);
1028 /* init completions before input_assign_text() call */
1029 in->completions = NULL;
1030 in->completion_flags = completion_flags;
1032 in->init_from_history = (def_text == INPUT_LAST_TEXT);
1034 if (in->init_from_history || def_text == NULL)
1035 def_text = "";
1037 input_assign_text (in, def_text);
1039 /* prepare to history setup */
1040 in->history.list = NULL;
1041 in->history.current = NULL;
1042 in->history.changed = FALSE;
1043 in->history.name = NULL;
1044 if ((histname != NULL) && (*histname != '\0'))
1045 in->history.name = g_strdup (histname);
1046 /* history will be loaded later */
1048 in->label = NULL;
1050 return in;
1053 /* --------------------------------------------------------------------------------------------- */
1055 cb_ret_t
1056 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1058 WInput *in = INPUT (w);
1059 cb_ret_t v;
1061 switch (msg)
1063 case MSG_INIT:
1064 /* subscribe to "history_load" event */
1065 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1066 /* subscribe to "history_save" event */
1067 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1068 return MSG_HANDLED;
1070 case MSG_KEY:
1071 if (parm == XCTRL ('q'))
1073 quote = TRUE;
1074 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1075 quote = FALSE;
1076 return v;
1079 /* Keys we want others to handle */
1080 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1081 || parm == KEY_F (10) || parm == '\n')
1082 return MSG_NOT_HANDLED;
1084 /* When pasting multiline text, insert literal Enter */
1085 if ((parm & ~KEY_M_MASK) == '\n')
1087 quote = TRUE;
1088 v = input_handle_char (in, '\n');
1089 quote = FALSE;
1090 return v;
1093 return input_handle_char (in, parm);
1095 case MSG_ACTION:
1096 return input_execute_cmd (in, parm);
1098 case MSG_RESIZE:
1099 case MSG_FOCUS:
1100 case MSG_UNFOCUS:
1101 case MSG_DRAW:
1102 input_update (in, FALSE);
1103 return MSG_HANDLED;
1105 case MSG_CURSOR:
1106 widget_move (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1107 return MSG_HANDLED;
1109 case MSG_DESTROY:
1110 /* unsubscribe from "history_load" event */
1111 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1112 /* unsubscribe from "history_save" event */
1113 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1114 input_destroy (in);
1115 return MSG_HANDLED;
1117 default:
1118 return widget_default_callback (w, sender, msg, parm, data);
1122 /* --------------------------------------------------------------------------------------------- */
1124 void
1125 input_set_default_colors (void)
1127 input_colors[WINPUTC_MAIN] = INPUT_COLOR;
1128 input_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1129 input_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1130 input_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1133 /* --------------------------------------------------------------------------------------------- */
1135 cb_ret_t
1136 input_handle_char (WInput * in, int key)
1138 cb_ret_t v;
1139 long command;
1141 if (quote)
1143 input_free_completions (in);
1144 v = insert_char (in, key);
1145 input_update (in, TRUE);
1146 quote = FALSE;
1147 return v;
1150 command = keybind_lookup_keymap_command (input_map, key);
1152 if (command == CK_IgnoreKey)
1154 if (key > 255)
1155 return MSG_NOT_HANDLED;
1156 if (in->first)
1157 port_region_marked_for_delete (in);
1158 input_free_completions (in);
1159 v = insert_char (in, key);
1161 else
1163 if (command != CK_Complete)
1164 input_free_completions (in);
1165 input_execute_cmd (in, command);
1166 v = MSG_HANDLED;
1167 if (in->first)
1168 input_update (in, TRUE); /* needed to clear in->first */
1171 input_update (in, TRUE);
1172 return v;
1175 /* --------------------------------------------------------------------------------------------- */
1177 /* This function is a test for a special input key used in complete.c */
1178 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1179 and 2 if it is a complete key */
1181 input_key_is_in_map (WInput * in, int key)
1183 long command;
1185 (void) in;
1187 command = keybind_lookup_keymap_command (input_map, key);
1188 if (command == CK_IgnoreKey)
1189 return 0;
1191 return (command == CK_Complete) ? 2 : 1;
1194 /* --------------------------------------------------------------------------------------------- */
1196 void
1197 input_assign_text (WInput * in, const char *text)
1199 Widget *w = WIDGET (in);
1200 size_t text_len, buffer_len;
1202 if (text == NULL)
1203 text = "";
1205 input_free_completions (in);
1206 in->mark = -1;
1207 in->need_push = TRUE;
1208 in->charpoint = 0;
1210 text_len = strlen (text);
1211 buffer_len = 1 + MAX ((size_t) w->cols, text_len);
1212 in->current_max_size = buffer_len;
1213 if (buffer_len > (size_t) w->cols)
1214 in->buffer = g_realloc (in->buffer, buffer_len);
1215 memmove (in->buffer, text, text_len + 1);
1216 in->point = str_length (in->buffer);
1217 input_update (in, TRUE);
1220 /* --------------------------------------------------------------------------------------------- */
1222 gboolean
1223 input_is_empty (const WInput * in)
1225 return (in == NULL || in->buffer == NULL || in->buffer[0] == '\0');
1228 /* --------------------------------------------------------------------------------------------- */
1230 /* Inserts text in input line */
1231 void
1232 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1234 input_disable_update (in);
1235 while (*text != '\0')
1236 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1237 if (insert_extra_space)
1238 input_handle_char (in, ' ');
1239 input_enable_update (in);
1240 input_update (in, TRUE);
1243 /* --------------------------------------------------------------------------------------------- */
1245 void
1246 input_set_point (WInput * in, int pos)
1248 int max_pos;
1250 max_pos = str_length (in->buffer);
1251 pos = MIN (pos, max_pos);
1252 if (pos != in->point)
1253 input_free_completions (in);
1254 in->point = pos;
1255 in->charpoint = 0;
1256 input_update (in, TRUE);
1259 /* --------------------------------------------------------------------------------------------- */
1261 void
1262 input_update (WInput * in, gboolean clear_first)
1264 Widget *w = WIDGET (in);
1265 int has_history = 0;
1266 int buf_len;
1267 const char *cp;
1268 int pw;
1270 if (in->disable_update != 0)
1271 return;
1273 /* don't draw widget not put into dialog */
1274 if (w->owner == NULL || w->owner->state != DLG_ACTIVE)
1275 return;
1277 if (should_show_history_button (in))
1278 has_history = HISTORY_BUTTON_WIDTH;
1280 buf_len = str_length (in->buffer);
1282 /* Adjust the mark */
1283 in->mark = MIN (in->mark, buf_len);
1285 pw = str_term_width2 (in->buffer, in->point);
1287 /* Make the point visible */
1288 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + w->cols - has_history))
1290 in->term_first_shown = pw - (w->cols / 3);
1291 if (in->term_first_shown < 0)
1292 in->term_first_shown = 0;
1295 if (has_history != 0)
1296 draw_history_button (in);
1298 if ((w->options & W_DISABLED) != 0)
1299 tty_setcolor (DISABLED_COLOR);
1300 else if (in->first)
1301 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1302 else
1303 tty_setcolor (in->color[WINPUTC_MAIN]);
1305 widget_move (in, 0, 0);
1307 if (!in->is_password)
1309 if (in->mark < 0)
1310 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1311 w->cols - has_history));
1312 else
1314 long m1, m2;
1316 if (input_eval_marks (in, &m1, &m2))
1318 tty_setcolor (in->color[WINPUTC_MAIN]);
1319 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1320 tty_print_string (cp);
1321 tty_setcolor (in->color[WINPUTC_MARK]);
1322 if (m1 < in->term_first_shown)
1324 widget_move (in, 0, 0);
1325 tty_print_string (str_term_substring
1326 (in->buffer, in->term_first_shown,
1327 m2 - in->term_first_shown));
1329 else
1331 int sel_width, buf_width;
1333 widget_move (in, 0, m1 - in->term_first_shown);
1334 buf_width = str_term_width2 (in->buffer, m1);
1335 sel_width =
1336 MIN (m2 - m1, (w->cols - has_history) - (buf_width - in->term_first_shown));
1337 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1342 else
1344 int i;
1346 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1347 tty_setcolor (in->color[WINPUTC_MAIN]);
1348 for (i = 0; i < w->cols - has_history; i++)
1350 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1351 tty_print_char ('*');
1352 else
1353 tty_print_char (' ');
1354 if (cp[0] != '\0')
1355 str_cnext_char (&cp);
1359 if (clear_first)
1360 in->first = FALSE;
1363 /* --------------------------------------------------------------------------------------------- */
1365 void
1366 input_enable_update (WInput * in)
1368 in->disable_update--;
1369 input_update (in, FALSE);
1372 /* --------------------------------------------------------------------------------------------- */
1374 void
1375 input_disable_update (WInput * in)
1377 in->disable_update++;
1380 /* --------------------------------------------------------------------------------------------- */
1383 * Cleans the input line and adds the current text to the history
1385 * @param in the input line
1387 void
1388 input_clean (WInput * in)
1390 push_history (in, in->buffer);
1391 in->need_push = TRUE;
1392 in->buffer[0] = '\0';
1393 in->point = 0;
1394 in->charpoint = 0;
1395 in->mark = -1;
1396 input_free_completions (in);
1397 input_update (in, FALSE);
1400 /* --------------------------------------------------------------------------------------------- */
1402 void
1403 input_free_completions (WInput * in)
1405 g_strfreev (in->completions);
1406 in->completions = NULL;
1409 /* --------------------------------------------------------------------------------------------- */