src/filemanager/filegui.c: fix coding style.
[midnight-commander.git] / lib / widget / input.c
blob20abe8bf908d9aa29387b8655c45a596e623a48a
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994-2019
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;
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 disabled = widget_get_state (WIDGET (in), WST_DISABLED);
117 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
119 #ifdef LARGE_HISTORY_BUTTON
120 tty_print_string ("[ ]");
121 widget_move (in, 0, WIDGET (in)->cols - HISTORY_BUTTON_WIDTH + 1);
122 #endif
124 tty_print_char (c);
127 /* --------------------------------------------------------------------------------------------- */
129 static void
130 input_mark_cmd (WInput * in, gboolean mark)
132 in->mark = mark ? in->point : -1;
135 /* --------------------------------------------------------------------------------------------- */
137 static gboolean
138 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
140 if (in->mark >= 0)
142 *start_mark = MIN (in->mark, in->point);
143 *end_mark = MAX (in->mark, in->point);
144 return TRUE;
147 *start_mark = *end_mark = -1;
148 return FALSE;
151 /* --------------------------------------------------------------------------------------------- */
153 static void
154 delete_region (WInput * in, int x_first, int x_last)
156 int first = MIN (x_first, x_last);
157 int last = MAX (x_first, x_last);
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 str_move (in->buffer + first, in->buffer + last);
164 in->charpoint = 0;
165 in->need_push = TRUE;
168 /* --------------------------------------------------------------------------------------------- */
170 static void
171 do_show_hist (WInput * in)
173 size_t len;
174 char *r;
176 len = get_history_length (in->history.list);
178 r = history_show (&in->history.list, WIDGET (in),
179 g_list_position (in->history.list, in->history.list));
180 if (r != NULL)
182 input_assign_text (in, r);
183 g_free (r);
186 /* Has history cleaned up or not? */
187 if (len != get_history_length (in->history.list))
188 in->history.changed = TRUE;
191 /* --------------------------------------------------------------------------------------------- */
193 * Strip password from incomplete url (just user:pass@host without VFS prefix).
195 * @param url partial URL
196 * @return newly allocated string without password
199 static char *
200 input_history_strip_password (char *url)
202 char *at, *delim, *colon;
204 at = strrchr (url, '@');
205 if (at == NULL)
206 return g_strdup (url);
208 /* TODO: handle ':' and '@' in password */
210 delim = strstr (url, VFS_PATH_URL_DELIMITER);
211 if (delim != NULL)
212 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
213 else
214 colon = strchr (url, ':');
216 /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
217 /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
218 if (colon != NULL && colon > at)
219 colon = NULL;
221 if (colon == NULL)
222 return g_strdup (url);
223 *colon = '\0';
225 return g_strconcat (url, at, (char *) NULL);
228 /* --------------------------------------------------------------------------------------------- */
230 static void
231 push_history (WInput * in, const char *text)
233 char *t;
234 gboolean empty;
236 if (text == NULL)
237 return;
239 t = g_strstrip (g_strdup (text));
240 empty = *t == '\0';
241 g_free (t);
242 t = g_strdup (empty ? "" : text);
244 if (!empty && in->history.name != NULL && in->strip_password)
247 We got string user:pass@host without any VFS prefixes
248 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
249 Therefore we want to strip password in separate algorithm
251 char *url_with_stripped_password;
253 url_with_stripped_password = input_history_strip_password (t);
254 g_free (t);
255 t = url_with_stripped_password;
258 if (in->history.list == NULL || in->history.list->data == NULL
259 || strcmp (in->history.list->data, t) != 0 || in->history.changed)
261 in->history.list = list_append_unique (in->history.list, t);
262 in->history.current = in->history.list;
263 in->history.changed = TRUE;
265 else
266 g_free (t);
268 in->need_push = FALSE;
271 /* --------------------------------------------------------------------------------------------- */
273 static void
274 move_buffer_backward (WInput * in, int start, int end)
276 int i, pos, len;
277 int str_len;
279 str_len = str_length (in->buffer);
280 if (start >= str_len || end > str_len + 1)
281 return;
283 pos = str_offset_to_pos (in->buffer, start);
284 len = str_offset_to_pos (in->buffer, end) - pos;
286 for (i = pos; in->buffer[i + len - 1]; i++)
287 in->buffer[i] = in->buffer[i + len];
290 /* --------------------------------------------------------------------------------------------- */
292 static cb_ret_t
293 insert_char (WInput * in, int c_code)
295 int res;
296 long m1, m2;
298 if (input_eval_marks (in, &m1, &m2))
299 delete_region (in, m1, m2);
301 if (c_code == -1)
302 return MSG_NOT_HANDLED;
304 if (in->charpoint >= MB_LEN_MAX)
305 return MSG_HANDLED;
307 in->charbuf[in->charpoint] = c_code;
308 in->charpoint++;
310 res = str_is_valid_char (in->charbuf, in->charpoint);
311 if (res < 0)
313 if (res != -2)
314 in->charpoint = 0; /* broken multibyte char, skip */
315 return MSG_HANDLED;
318 in->need_push = TRUE;
319 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
321 /* Expand the buffer */
322 size_t new_length;
323 char *narea;
325 new_length = in->current_max_size + WIDGET (in)->cols + in->charpoint;
326 narea = g_try_renew (char, in->buffer, new_length);
327 if (narea != NULL)
329 in->buffer = narea;
330 in->current_max_size = new_length;
334 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
336 size_t i;
337 /* bytes from begin */
338 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
339 /* move chars */
340 size_t rest_bytes = strlen (in->buffer + ins_point);
342 for (i = rest_bytes + 1; i > 0; i--)
343 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
345 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
346 in->point++;
349 in->charpoint = 0;
350 return MSG_HANDLED;
353 /* --------------------------------------------------------------------------------------------- */
355 static void
356 beginning_of_line (WInput * in)
358 in->point = 0;
359 in->charpoint = 0;
362 /* --------------------------------------------------------------------------------------------- */
364 static void
365 end_of_line (WInput * in)
367 in->point = str_length (in->buffer);
368 in->charpoint = 0;
371 /* --------------------------------------------------------------------------------------------- */
373 static void
374 backward_char (WInput * in)
376 const char *act;
378 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
379 if (in->point > 0)
380 in->point -= str_cprev_noncomb_char (&act, in->buffer);
381 in->charpoint = 0;
384 /* --------------------------------------------------------------------------------------------- */
386 static void
387 forward_char (WInput * in)
389 const char *act;
391 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
392 if (act[0] != '\0')
393 in->point += str_cnext_noncomb_char (&act);
394 in->charpoint = 0;
397 /* --------------------------------------------------------------------------------------------- */
399 static void
400 forward_word (WInput * in)
402 const char *p;
404 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
405 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
407 str_cnext_char (&p);
408 in->point++;
410 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
412 str_cnext_char (&p);
413 in->point++;
417 /* --------------------------------------------------------------------------------------------- */
419 static void
420 backward_word (WInput * in)
422 const char *p;
424 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
426 while (p != in->buffer)
428 const char *p_tmp;
430 p_tmp = p;
431 str_cprev_char (&p);
432 if (!str_isspace (p) && !str_ispunct (p))
434 p = p_tmp;
435 break;
437 in->point--;
439 while (p != in->buffer)
441 str_cprev_char (&p);
442 if (str_isspace (p) || str_ispunct (p))
443 break;
445 in->point--;
449 /* --------------------------------------------------------------------------------------------- */
451 static void
452 backward_delete (WInput * in)
454 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
455 int start;
457 if (in->point == 0)
458 return;
460 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
461 move_buffer_backward (in, start, in->point);
462 in->charpoint = 0;
463 in->need_push = TRUE;
464 in->point = start;
467 /* --------------------------------------------------------------------------------------------- */
469 static void
470 delete_char (WInput * in)
472 const char *act;
473 int end = in->point;
475 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
476 end += str_cnext_noncomb_char (&act);
478 move_buffer_backward (in, in->point, end);
479 in->charpoint = 0;
480 in->need_push = TRUE;
483 /* --------------------------------------------------------------------------------------------- */
485 static void
486 copy_region (WInput * in, int x_first, int x_last)
488 int first = MIN (x_first, x_last);
489 int last = MAX (x_first, x_last);
491 if (last == first)
493 /* Copy selected files to clipboard */
494 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file", NULL);
495 /* try use external clipboard utility */
496 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
497 return;
500 g_free (kill_buffer);
502 first = str_offset_to_pos (in->buffer, first);
503 last = str_offset_to_pos (in->buffer, last);
505 kill_buffer = g_strndup (in->buffer + first, last - first);
507 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
508 /* try use external clipboard utility */
509 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
512 /* --------------------------------------------------------------------------------------------- */
514 static void
515 kill_word (WInput * in)
517 int old_point = in->point;
518 int new_point;
520 forward_word (in);
521 new_point = in->point;
522 in->point = old_point;
524 delete_region (in, old_point, new_point);
525 in->need_push = TRUE;
526 in->charpoint = 0;
529 /* --------------------------------------------------------------------------------------------- */
531 static void
532 back_kill_word (WInput * in)
534 int old_point = in->point;
535 int new_point;
537 backward_word (in);
538 new_point = in->point;
539 in->point = old_point;
541 delete_region (in, old_point, new_point);
542 in->need_push = TRUE;
545 /* --------------------------------------------------------------------------------------------- */
547 static void
548 yank (WInput * in)
550 if (kill_buffer != NULL)
552 char *p;
554 in->charpoint = 0;
555 for (p = kill_buffer; *p != '\0'; p++)
556 insert_char (in, *p);
557 in->charpoint = 0;
561 /* --------------------------------------------------------------------------------------------- */
563 static void
564 kill_line (WInput * in)
566 int chp;
568 chp = str_offset_to_pos (in->buffer, in->point);
569 g_free (kill_buffer);
570 kill_buffer = g_strdup (&in->buffer[chp]);
571 in->buffer[chp] = '\0';
572 in->charpoint = 0;
575 /* --------------------------------------------------------------------------------------------- */
577 static void
578 clear_line (WInput * in)
580 in->need_push = TRUE;
581 in->buffer[0] = '\0';
582 in->point = 0;
583 in->mark = -1;
584 in->charpoint = 0;
587 /* --------------------------------------------------------------------------------------------- */
589 static void
590 ins_from_clip (WInput * in)
592 char *p = NULL;
593 ev_clipboard_text_from_file_t event_data;
595 /* try use external clipboard utility */
596 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
598 event_data.text = &p;
599 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
600 if (event_data.ret)
602 char *pp;
604 for (pp = p; *pp != '\0'; pp++)
605 insert_char (in, *pp);
607 g_free (p);
611 /* --------------------------------------------------------------------------------------------- */
613 static void
614 hist_prev (WInput * in)
616 GList *prev;
618 if (in->history.list == NULL)
619 return;
621 if (in->need_push)
622 push_history (in, in->buffer);
624 prev = g_list_previous (in->history.current);
625 if (prev != NULL)
627 input_assign_text (in, (char *) prev->data);
628 in->history.current = prev;
629 in->history.changed = TRUE;
630 in->need_push = FALSE;
634 /* --------------------------------------------------------------------------------------------- */
636 static void
637 hist_next (WInput * in)
639 GList *next;
641 if (in->need_push)
643 push_history (in, in->buffer);
644 input_assign_text (in, "");
645 return;
648 if (in->history.list == NULL)
649 return;
651 next = g_list_next (in->history.current);
652 if (next == NULL)
654 input_assign_text (in, "");
655 in->history.current = in->history.list;
657 else
659 input_assign_text (in, (char *) next->data);
660 in->history.current = next;
661 in->history.changed = TRUE;
662 in->need_push = FALSE;
666 /* --------------------------------------------------------------------------------------------- */
668 static void
669 port_region_marked_for_delete (WInput * in)
671 in->buffer[0] = '\0';
672 in->point = 0;
673 in->first = FALSE;
674 in->charpoint = 0;
677 /* --------------------------------------------------------------------------------------------- */
679 static cb_ret_t
680 input_execute_cmd (WInput * in, long command)
682 cb_ret_t res = MSG_HANDLED;
684 switch (command)
686 case CK_MarkLeft:
687 case CK_MarkRight:
688 case CK_MarkToWordBegin:
689 case CK_MarkToWordEnd:
690 case CK_MarkToHome:
691 case CK_MarkToEnd:
692 /* a highlight command like shift-arrow */
693 if (in->mark < 0)
695 input_mark_cmd (in, FALSE); /* clear */
696 input_mark_cmd (in, TRUE); /* marking on */
698 break;
699 case CK_WordRight:
700 case CK_WordLeft:
701 case CK_Right:
702 case CK_Left:
703 if (in->mark >= 0)
704 input_mark_cmd (in, FALSE);
705 break;
706 default:
707 break;
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:
738 long m1, m2;
740 if (input_eval_marks (in, &m1, &m2))
741 delete_region (in, m1, m2);
742 else
743 backward_delete (in);
745 break;
746 case CK_Delete:
747 if (in->first)
748 port_region_marked_for_delete (in);
749 else
751 long m1, m2;
753 if (input_eval_marks (in, &m1, &m2))
754 delete_region (in, m1, m2);
755 else
756 delete_char (in);
758 break;
759 case CK_DeleteToWordEnd:
760 kill_word (in);
761 break;
762 case CK_DeleteToWordBegin:
763 back_kill_word (in);
764 break;
765 case CK_Mark:
766 input_mark_cmd (in, TRUE);
767 break;
768 case CK_Remove:
769 delete_region (in, in->point, MAX (in->mark, 0));
770 break;
771 case CK_DeleteToEnd:
772 kill_line (in);
773 break;
774 case CK_Clear:
775 clear_line (in);
776 break;
777 case CK_Store:
778 copy_region (in, MAX (in->mark, 0), in->point);
779 break;
780 case CK_Cut:
782 long m;
784 m = MAX (in->mark, 0);
785 copy_region (in, m, in->point);
786 delete_region (in, in->point, m);
788 break;
789 case CK_Yank:
790 yank (in);
791 break;
792 case CK_Paste:
793 ins_from_clip (in);
794 break;
795 case CK_HistoryPrev:
796 hist_prev (in);
797 break;
798 case CK_HistoryNext:
799 hist_next (in);
800 break;
801 case CK_History:
802 do_show_hist (in);
803 break;
804 case CK_Complete:
805 complete (in);
806 break;
807 default:
808 res = MSG_NOT_HANDLED;
811 switch (command)
813 case CK_MarkLeft:
814 case CK_MarkRight:
815 case CK_MarkToWordBegin:
816 case CK_MarkToWordEnd:
817 case CK_MarkToHome:
818 case CK_MarkToEnd:
819 /* do nothing */
820 break;
821 default:
822 in->mark = -1;
823 break;
826 return res;
829 /* --------------------------------------------------------------------------------------------- */
831 /* "history_load" event handler */
832 static gboolean
833 input_load_history (const gchar * event_group_name, const gchar * event_name,
834 gpointer init_data, gpointer data)
836 WInput *in = INPUT (init_data);
837 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
839 (void) event_group_name;
840 (void) event_name;
842 in->history.list = history_load (ev->cfg, in->history.name);
843 in->history.current = in->history.list;
845 if (in->init_from_history)
847 const char *def_text = "";
849 if (in->history.list != NULL && in->history.list->data != NULL)
850 def_text = (const char *) in->history.list->data;
852 input_assign_text (in, def_text);
855 return TRUE;
858 /* --------------------------------------------------------------------------------------------- */
860 /* "history_save" event handler */
861 static gboolean
862 input_save_history (const gchar * event_group_name, const gchar * event_name,
863 gpointer init_data, gpointer data)
865 WInput *in = INPUT (init_data);
867 (void) event_group_name;
868 (void) event_name;
870 if (!in->is_password && (WIDGET (in)->owner->ret_value != B_CANCEL))
872 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
874 push_history (in, in->buffer);
875 if (in->history.changed)
876 history_save (ev->cfg, in->history.name, in->history.list);
877 in->history.changed = FALSE;
880 return TRUE;
883 /* --------------------------------------------------------------------------------------------- */
885 static void
886 input_destroy (WInput * in)
888 if (in == NULL)
890 fprintf (stderr, "Internal error: null Input *\n");
891 exit (EXIT_FAILURE);
894 input_free_completions (in);
896 /* clean history */
897 if (in->history.list != NULL)
899 /* history is already saved before this moment */
900 in->history.list = g_list_first (in->history.list);
901 g_list_free_full (in->history.list, g_free);
903 g_free (in->history.name);
904 g_free (in->buffer);
905 MC_PTR_FREE (kill_buffer);
908 /* --------------------------------------------------------------------------------------------- */
911 * Calculates the buffer index (aka "point") corresponding to some screen coordinate.
913 static int
914 input_screen_to_point (const WInput * in, int x)
916 x += in->term_first_shown;
918 if (x < 0)
919 return 0;
921 if (x < str_term_width1 (in->buffer))
922 return str_column_to_pos (in->buffer, x);
924 return str_length (in->buffer);
927 /* --------------------------------------------------------------------------------------------- */
929 static void
930 input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
932 /* save point between MSG_MOUSE_DOWN and MSG_MOUSE_DRAG */
933 static int prev_point = 0;
934 WInput *in = INPUT (w);
936 switch (msg)
938 case MSG_MOUSE_DOWN:
939 widget_select (w);
940 in->first = FALSE;
942 if (event->x >= w->cols - HISTORY_BUTTON_WIDTH && should_show_history_button (in))
943 do_show_hist (in);
944 else
946 input_mark_cmd (in, FALSE);
947 input_set_point (in, input_screen_to_point (in, event->x));
948 /* save point for the possible following MSG_MOUSE_DRAG action */
949 prev_point = in->point;
951 break;
953 case MSG_MOUSE_DRAG:
954 /* start point: set marker using point before first MSG_MOUSE_DRAG action */
955 if (in->mark < 0)
956 in->mark = prev_point;
958 input_set_point (in, input_screen_to_point (in, event->x));
959 break;
961 default:
962 /* don't create highlight region of 0 length */
963 if (in->mark == in->point)
964 input_mark_cmd (in, FALSE);
965 break;
969 /* --------------------------------------------------------------------------------------------- */
970 /*** public functions ****************************************************************************/
971 /* --------------------------------------------------------------------------------------------- */
973 /** Create new instance of WInput object.
974 * @param y Y coordinate
975 * @param x X coordinate
976 * @param input_colors Array of used colors
977 * @param width Widget width
978 * @param def_text Default text filled in widget
979 * @param histname Name of history
980 * @param completion_flags Flags for specify type of completions
981 * @return WInput object
983 WInput *
984 input_new (int y, int x, const int *colors, int width, const char *def_text,
985 const char *histname, input_complete_t completion_flags)
987 WInput *in;
988 Widget *w;
990 in = g_new (WInput, 1);
991 w = WIDGET (in);
992 widget_init (w, y, x, 1, width, input_callback, input_mouse_callback);
993 w->options |= WOP_SELECTABLE | WOP_IS_INPUT | WOP_WANT_CURSOR;
995 in->color = colors;
996 in->first = TRUE;
997 in->mark = -1;
998 in->term_first_shown = 0;
999 in->disable_update = 0;
1000 in->is_password = FALSE;
1001 in->strip_password = FALSE;
1003 /* in->buffer will be corrected in "history_load" event handler */
1004 in->current_max_size = width + 1;
1005 in->buffer = g_new0 (char, in->current_max_size);
1007 /* init completions before input_assign_text() call */
1008 in->completions = NULL;
1009 in->completion_flags = completion_flags;
1011 in->init_from_history = (def_text == INPUT_LAST_TEXT);
1013 if (in->init_from_history || def_text == NULL)
1014 def_text = "";
1016 input_assign_text (in, def_text);
1018 /* prepare to history setup */
1019 in->history.list = NULL;
1020 in->history.current = NULL;
1021 in->history.changed = FALSE;
1022 in->history.name = NULL;
1023 if ((histname != NULL) && (*histname != '\0'))
1024 in->history.name = g_strdup (histname);
1025 /* history will be loaded later */
1027 in->label = NULL;
1029 return in;
1032 /* --------------------------------------------------------------------------------------------- */
1034 cb_ret_t
1035 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1037 WInput *in = INPUT (w);
1038 cb_ret_t v;
1040 switch (msg)
1042 case MSG_INIT:
1043 /* subscribe to "history_load" event */
1044 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1045 /* subscribe to "history_save" event */
1046 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1047 if (in->label != NULL)
1048 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED));
1049 return MSG_HANDLED;
1051 case MSG_KEY:
1052 if (parm == XCTRL ('q'))
1054 quote = TRUE;
1055 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1056 quote = FALSE;
1057 return v;
1060 /* Keys we want others to handle */
1061 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1062 || parm == KEY_F (10) || parm == '\n')
1063 return MSG_NOT_HANDLED;
1065 /* When pasting multiline text, insert literal Enter */
1066 if ((parm & ~KEY_M_MASK) == '\n')
1068 quote = TRUE;
1069 v = input_handle_char (in, '\n');
1070 quote = FALSE;
1071 return v;
1074 return input_handle_char (in, parm);
1076 case MSG_ACTION:
1077 return input_execute_cmd (in, parm);
1079 case MSG_DRAW:
1080 input_update (in, FALSE);
1081 return MSG_HANDLED;
1083 case MSG_ENABLE:
1084 case MSG_DISABLE:
1085 if (in->label != NULL)
1086 widget_set_state (WIDGET (in->label), WST_DISABLED, msg == MSG_DISABLE);
1087 return MSG_HANDLED;
1089 case MSG_CURSOR:
1090 widget_move (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1091 return MSG_HANDLED;
1093 case MSG_DESTROY:
1094 /* unsubscribe from "history_load" event */
1095 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1096 /* unsubscribe from "history_save" event */
1097 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1098 input_destroy (in);
1099 return MSG_HANDLED;
1101 default:
1102 return widget_default_callback (w, sender, msg, parm, data);
1106 /* --------------------------------------------------------------------------------------------- */
1108 void
1109 input_set_default_colors (void)
1111 input_colors[WINPUTC_MAIN] = INPUT_COLOR;
1112 input_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1113 input_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1114 input_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1117 /* --------------------------------------------------------------------------------------------- */
1119 cb_ret_t
1120 input_handle_char (WInput * in, int key)
1122 cb_ret_t v;
1123 long command;
1125 if (quote)
1127 input_free_completions (in);
1128 v = insert_char (in, key);
1129 input_update (in, TRUE);
1130 quote = FALSE;
1131 return v;
1134 command = keybind_lookup_keymap_command (input_map, key);
1136 if (command == CK_IgnoreKey)
1138 if (key > 255)
1139 return MSG_NOT_HANDLED;
1140 if (in->first)
1141 port_region_marked_for_delete (in);
1142 input_free_completions (in);
1143 v = insert_char (in, key);
1145 else
1147 if (command != CK_Complete)
1148 input_free_completions (in);
1149 input_execute_cmd (in, command);
1150 v = MSG_HANDLED;
1151 if (in->first)
1152 input_update (in, TRUE); /* needed to clear in->first */
1155 input_update (in, TRUE);
1156 return v;
1159 /* --------------------------------------------------------------------------------------------- */
1161 /* This function is a test for a special input key used in complete.c */
1162 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1163 and 2 if it is a complete key */
1165 input_key_is_in_map (WInput * in, int key)
1167 long command;
1169 (void) in;
1171 command = keybind_lookup_keymap_command (input_map, key);
1172 if (command == CK_IgnoreKey)
1173 return 0;
1175 return (command == CK_Complete) ? 2 : 1;
1178 /* --------------------------------------------------------------------------------------------- */
1180 void
1181 input_assign_text (WInput * in, const char *text)
1183 Widget *w = WIDGET (in);
1184 size_t text_len, buffer_len;
1186 if (text == NULL)
1187 text = "";
1189 input_free_completions (in);
1190 in->mark = -1;
1191 in->need_push = TRUE;
1192 in->charpoint = 0;
1194 text_len = strlen (text);
1195 buffer_len = 1 + MAX ((size_t) w->cols, text_len);
1196 in->current_max_size = buffer_len;
1197 if (buffer_len > (size_t) w->cols)
1198 in->buffer = g_realloc (in->buffer, buffer_len);
1199 memmove (in->buffer, text, text_len + 1);
1200 in->point = str_length (in->buffer);
1201 input_update (in, TRUE);
1204 /* --------------------------------------------------------------------------------------------- */
1206 gboolean
1207 input_is_empty (const WInput * in)
1209 return (in == NULL || in->buffer == NULL || in->buffer[0] == '\0');
1212 /* --------------------------------------------------------------------------------------------- */
1214 /* Inserts text in input line */
1215 void
1216 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1218 input_disable_update (in);
1219 while (*text != '\0')
1220 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1221 if (insert_extra_space)
1222 input_handle_char (in, ' ');
1223 input_enable_update (in);
1224 input_update (in, TRUE);
1227 /* --------------------------------------------------------------------------------------------- */
1229 void
1230 input_set_point (WInput * in, int pos)
1232 int max_pos;
1234 max_pos = str_length (in->buffer);
1235 pos = MIN (pos, max_pos);
1236 if (pos != in->point)
1237 input_free_completions (in);
1238 in->point = pos;
1239 in->charpoint = 0;
1240 input_update (in, TRUE);
1243 /* --------------------------------------------------------------------------------------------- */
1245 void
1246 input_update (WInput * in, gboolean clear_first)
1248 Widget *w = WIDGET (in);
1249 int has_history = 0;
1250 int buf_len;
1251 const char *cp;
1252 int pw;
1254 if (in->disable_update != 0)
1255 return;
1257 /* don't draw widget not put into dialog */
1258 if (w->owner == NULL || !widget_get_state (WIDGET (w->owner), WST_ACTIVE))
1259 return;
1261 if (should_show_history_button (in))
1262 has_history = HISTORY_BUTTON_WIDTH;
1264 buf_len = str_length (in->buffer);
1266 /* Adjust the mark */
1267 in->mark = MIN (in->mark, buf_len);
1269 pw = str_term_width2 (in->buffer, in->point);
1271 /* Make the point visible */
1272 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + w->cols - has_history))
1274 in->term_first_shown = pw - (w->cols / 3);
1275 if (in->term_first_shown < 0)
1276 in->term_first_shown = 0;
1279 if (has_history != 0)
1280 draw_history_button (in);
1282 if (widget_get_state (w, WST_DISABLED))
1283 tty_setcolor (DISABLED_COLOR);
1284 else if (in->first)
1285 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1286 else
1287 tty_setcolor (in->color[WINPUTC_MAIN]);
1289 widget_move (in, 0, 0);
1291 if (!in->is_password)
1293 if (in->mark < 0)
1294 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1295 w->cols - has_history));
1296 else
1298 long m1, m2;
1300 if (input_eval_marks (in, &m1, &m2))
1302 tty_setcolor (in->color[WINPUTC_MAIN]);
1303 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1304 tty_print_string (cp);
1305 tty_setcolor (in->color[WINPUTC_MARK]);
1306 if (m1 < in->term_first_shown)
1308 widget_move (in, 0, 0);
1309 tty_print_string (str_term_substring
1310 (in->buffer, in->term_first_shown,
1311 m2 - in->term_first_shown));
1313 else
1315 int sel_width, buf_width;
1317 widget_move (in, 0, m1 - in->term_first_shown);
1318 buf_width = str_term_width2 (in->buffer, m1);
1319 sel_width =
1320 MIN (m2 - m1, (w->cols - has_history) - (buf_width - in->term_first_shown));
1321 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1326 else
1328 int i;
1330 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1331 tty_setcolor (in->color[WINPUTC_MAIN]);
1332 for (i = 0; i < w->cols - has_history; i++)
1334 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1335 tty_print_char ('*');
1336 else
1337 tty_print_char (' ');
1338 if (cp[0] != '\0')
1339 str_cnext_char (&cp);
1343 if (clear_first)
1344 in->first = FALSE;
1347 /* --------------------------------------------------------------------------------------------- */
1349 void
1350 input_enable_update (WInput * in)
1352 in->disable_update--;
1353 input_update (in, FALSE);
1356 /* --------------------------------------------------------------------------------------------- */
1358 void
1359 input_disable_update (WInput * in)
1361 in->disable_update++;
1364 /* --------------------------------------------------------------------------------------------- */
1367 * Cleans the input line and adds the current text to the history
1369 * @param in the input line
1371 void
1372 input_clean (WInput * in)
1374 push_history (in, in->buffer);
1375 in->need_push = TRUE;
1376 in->buffer[0] = '\0';
1377 in->point = 0;
1378 in->charpoint = 0;
1379 in->mark = -1;
1380 input_free_completions (in);
1381 input_update (in, FALSE);
1384 /* --------------------------------------------------------------------------------------------- */
1386 void
1387 input_free_completions (WInput * in)
1389 g_strfreev (in->completions);
1390 in->completions = NULL;
1393 /* --------------------------------------------------------------------------------------------- */