If widget wants cursor, define that explicitly.
[midnight-commander.git] / lib / widget / input.c
blob21891f51e1831c0defcbeaed9878241a8cf70ac5
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;
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);
158 size_t len;
160 input_mark_cmd (in, FALSE);
161 in->point = first;
162 last = str_offset_to_pos (in->buffer, last);
163 first = str_offset_to_pos (in->buffer, first);
164 len = strlen (&in->buffer[last]) + 1;
165 memmove (&in->buffer[first], &in->buffer[last], len);
166 in->charpoint = 0;
167 in->need_push = TRUE;
170 /* --------------------------------------------------------------------------------------------- */
172 static void
173 do_show_hist (WInput * in)
175 size_t len;
176 char *r;
178 len = get_history_length (in->history.list);
180 r = history_show (&in->history.list, WIDGET (in),
181 g_list_position (in->history.list, in->history.list));
182 if (r != NULL)
184 input_assign_text (in, r);
185 g_free (r);
188 /* Has history cleaned up or not? */
189 if (len != get_history_length (in->history.list))
190 in->history.changed = TRUE;
193 /* --------------------------------------------------------------------------------------------- */
195 * Strip password from incomplete url (just user:pass@host without VFS prefix).
197 * @param url partial URL
198 * @return newly allocated string without password
201 static char *
202 input_history_strip_password (char *url)
204 char *at, *delim, *colon;
206 at = strrchr (url, '@');
207 if (at == NULL)
208 return g_strdup (url);
210 /* TODO: handle ':' and '@' in password */
212 delim = strstr (url, VFS_PATH_URL_DELIMITER);
213 if (delim != NULL)
214 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
215 else
216 colon = strchr (url, ':');
218 /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
219 /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
220 if (colon != NULL && colon > at)
221 colon = NULL;
223 if (colon == NULL)
224 return g_strdup (url);
225 *colon = '\0';
227 return g_strconcat (url, at, (char *) NULL);
230 /* --------------------------------------------------------------------------------------------- */
232 static void
233 push_history (WInput * in, const char *text)
235 char *t;
236 gboolean empty;
238 if (text == NULL)
239 return;
241 t = g_strstrip (g_strdup (text));
242 empty = *t == '\0';
243 g_free (t);
244 t = g_strdup (empty ? "" : text);
246 if (!empty && in->history.name != NULL && in->strip_password)
249 We got string user:pass@host without any VFS prefixes
250 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
251 Therefore we want to strip password in separate algorithm
253 char *url_with_stripped_password;
255 url_with_stripped_password = input_history_strip_password (t);
256 g_free (t);
257 t = url_with_stripped_password;
260 if (in->history.list == NULL || in->history.list->data == NULL
261 || strcmp (in->history.list->data, t) != 0 || in->history.changed)
263 in->history.list = list_append_unique (in->history.list, t);
264 in->history.current = in->history.list;
265 in->history.changed = TRUE;
267 else
268 g_free (t);
270 in->need_push = FALSE;
273 /* --------------------------------------------------------------------------------------------- */
275 static void
276 move_buffer_backward (WInput * in, int start, int end)
278 int i, pos, len;
279 int str_len;
281 str_len = str_length (in->buffer);
282 if (start >= str_len || end > str_len + 1)
283 return;
285 pos = str_offset_to_pos (in->buffer, start);
286 len = str_offset_to_pos (in->buffer, end) - pos;
288 for (i = pos; in->buffer[i + len - 1]; i++)
289 in->buffer[i] = in->buffer[i + len];
292 /* --------------------------------------------------------------------------------------------- */
294 static cb_ret_t
295 insert_char (WInput * in, int c_code)
297 int res;
298 long m1, m2;
300 if (input_eval_marks (in, &m1, &m2))
301 delete_region (in, m1, m2);
303 if (c_code == -1)
304 return MSG_NOT_HANDLED;
306 if (in->charpoint >= MB_LEN_MAX)
307 return MSG_HANDLED;
309 in->charbuf[in->charpoint] = c_code;
310 in->charpoint++;
312 res = str_is_valid_char (in->charbuf, in->charpoint);
313 if (res < 0)
315 if (res != -2)
316 in->charpoint = 0; /* broken multibyte char, skip */
317 return MSG_HANDLED;
320 in->need_push = TRUE;
321 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
323 /* Expand the buffer */
324 size_t new_length;
325 char *narea;
327 new_length = in->current_max_size + WIDGET (in)->cols + in->charpoint;
328 narea = g_try_renew (char, in->buffer, new_length);
329 if (narea != NULL)
331 in->buffer = narea;
332 in->current_max_size = new_length;
336 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
338 size_t i;
339 /* bytes from begin */
340 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
341 /* move chars */
342 size_t rest_bytes = strlen (in->buffer + ins_point);
344 for (i = rest_bytes + 1; i > 0; i--)
345 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
347 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
348 in->point++;
351 in->charpoint = 0;
352 return MSG_HANDLED;
355 /* --------------------------------------------------------------------------------------------- */
357 static void
358 beginning_of_line (WInput * in)
360 in->point = 0;
361 in->charpoint = 0;
364 /* --------------------------------------------------------------------------------------------- */
366 static void
367 end_of_line (WInput * in)
369 in->point = str_length (in->buffer);
370 in->charpoint = 0;
373 /* --------------------------------------------------------------------------------------------- */
375 static void
376 backward_char (WInput * in)
378 const char *act;
380 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
381 if (in->point > 0)
382 in->point -= str_cprev_noncomb_char (&act, in->buffer);
383 in->charpoint = 0;
386 /* --------------------------------------------------------------------------------------------- */
388 static void
389 forward_char (WInput * in)
391 const char *act;
393 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
394 if (act[0] != '\0')
395 in->point += str_cnext_noncomb_char (&act);
396 in->charpoint = 0;
399 /* --------------------------------------------------------------------------------------------- */
401 static void
402 forward_word (WInput * in)
404 const char *p;
406 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
407 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
409 str_cnext_char (&p);
410 in->point++;
412 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
414 str_cnext_char (&p);
415 in->point++;
419 /* --------------------------------------------------------------------------------------------- */
421 static void
422 backward_word (WInput * in)
424 const char *p;
426 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
428 while (p != in->buffer)
430 const char *p_tmp;
432 p_tmp = p;
433 str_cprev_char (&p);
434 if (!str_isspace (p) && !str_ispunct (p))
436 p = p_tmp;
437 break;
439 in->point--;
441 while (p != in->buffer)
443 str_cprev_char (&p);
444 if (str_isspace (p) || str_ispunct (p))
445 break;
447 in->point--;
451 /* --------------------------------------------------------------------------------------------- */
453 static void
454 backward_delete (WInput * in)
456 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
457 int start;
459 if (in->point == 0)
460 return;
462 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
463 move_buffer_backward (in, start, in->point);
464 in->charpoint = 0;
465 in->need_push = TRUE;
466 in->point = start;
469 /* --------------------------------------------------------------------------------------------- */
471 static void
472 delete_char (WInput * in)
474 const char *act;
475 int end = in->point;
477 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
478 end += str_cnext_noncomb_char (&act);
480 move_buffer_backward (in, in->point, end);
481 in->charpoint = 0;
482 in->need_push = TRUE;
485 /* --------------------------------------------------------------------------------------------- */
487 static void
488 copy_region (WInput * in, int x_first, int x_last)
490 int first = MIN (x_first, x_last);
491 int last = MAX (x_first, x_last);
493 if (last == first)
495 /* Copy selected files to clipboard */
496 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_current_file_to_clip_file", NULL);
497 /* try use external clipboard utility */
498 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
499 return;
502 g_free (kill_buffer);
504 first = str_offset_to_pos (in->buffer, first);
505 last = str_offset_to_pos (in->buffer, last);
507 kill_buffer = g_strndup (in->buffer + first, last - first);
509 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
510 /* try use external clipboard utility */
511 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
514 /* --------------------------------------------------------------------------------------------- */
516 static void
517 kill_word (WInput * in)
519 int old_point = in->point;
520 int new_point;
522 forward_word (in);
523 new_point = in->point;
524 in->point = old_point;
526 delete_region (in, old_point, new_point);
527 in->need_push = TRUE;
528 in->charpoint = 0;
531 /* --------------------------------------------------------------------------------------------- */
533 static void
534 back_kill_word (WInput * in)
536 int old_point = in->point;
537 int new_point;
539 backward_word (in);
540 new_point = in->point;
541 in->point = old_point;
543 delete_region (in, old_point, new_point);
544 in->need_push = TRUE;
547 /* --------------------------------------------------------------------------------------------- */
549 static void
550 yank (WInput * in)
552 if (kill_buffer != NULL)
554 char *p;
556 in->charpoint = 0;
557 for (p = kill_buffer; *p != '\0'; p++)
558 insert_char (in, *p);
559 in->charpoint = 0;
563 /* --------------------------------------------------------------------------------------------- */
565 static void
566 kill_line (WInput * in)
568 int chp;
570 chp = str_offset_to_pos (in->buffer, in->point);
571 g_free (kill_buffer);
572 kill_buffer = g_strdup (&in->buffer[chp]);
573 in->buffer[chp] = '\0';
574 in->charpoint = 0;
577 /* --------------------------------------------------------------------------------------------- */
579 static void
580 clear_line (WInput * in)
582 in->need_push = TRUE;
583 in->buffer[0] = '\0';
584 in->point = 0;
585 in->mark = -1;
586 in->charpoint = 0;
589 /* --------------------------------------------------------------------------------------------- */
591 static void
592 ins_from_clip (WInput * in)
594 char *p = NULL;
595 ev_clipboard_text_from_file_t event_data;
597 /* try use external clipboard utility */
598 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
600 event_data.text = &p;
601 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
602 if (event_data.ret)
604 char *pp;
606 for (pp = p; *pp != '\0'; pp++)
607 insert_char (in, *pp);
609 g_free (p);
613 /* --------------------------------------------------------------------------------------------- */
615 static void
616 hist_prev (WInput * in)
618 GList *prev;
620 if (in->history.list == NULL)
621 return;
623 if (in->need_push)
624 push_history (in, in->buffer);
626 prev = g_list_previous (in->history.current);
627 if (prev != NULL)
629 input_assign_text (in, (char *) prev->data);
630 in->history.current = prev;
631 in->history.changed = TRUE;
632 in->need_push = FALSE;
636 /* --------------------------------------------------------------------------------------------- */
638 static void
639 hist_next (WInput * in)
641 GList *next;
643 if (in->need_push)
645 push_history (in, in->buffer);
646 input_assign_text (in, "");
647 return;
650 if (in->history.list == NULL)
651 return;
653 next = g_list_next (in->history.current);
654 if (next == NULL)
656 input_assign_text (in, "");
657 in->history.current = in->history.list;
659 else
661 input_assign_text (in, (char *) next->data);
662 in->history.current = next;
663 in->history.changed = TRUE;
664 in->need_push = FALSE;
668 /* --------------------------------------------------------------------------------------------- */
670 static void
671 port_region_marked_for_delete (WInput * in)
673 in->buffer[0] = '\0';
674 in->point = 0;
675 in->first = FALSE;
676 in->charpoint = 0;
679 /* --------------------------------------------------------------------------------------------- */
681 static cb_ret_t
682 input_execute_cmd (WInput * in, long command)
684 cb_ret_t res = MSG_HANDLED;
686 switch (command)
688 case CK_MarkLeft:
689 case CK_MarkRight:
690 case CK_MarkToWordBegin:
691 case CK_MarkToWordEnd:
692 case CK_MarkToHome:
693 case CK_MarkToEnd:
694 /* a highlight command like shift-arrow */
695 if (in->mark < 0)
697 input_mark_cmd (in, FALSE); /* clear */
698 input_mark_cmd (in, TRUE); /* marking on */
700 break;
701 case CK_WordRight:
702 case CK_WordLeft:
703 case CK_Right:
704 case CK_Left:
705 if (in->mark >= 0)
706 input_mark_cmd (in, FALSE);
707 break;
708 default:
709 break;
712 switch (command)
714 case CK_Home:
715 case CK_MarkToHome:
716 beginning_of_line (in);
717 break;
718 case CK_End:
719 case CK_MarkToEnd:
720 end_of_line (in);
721 break;
722 case CK_Left:
723 case CK_MarkLeft:
724 backward_char (in);
725 break;
726 case CK_WordLeft:
727 case CK_MarkToWordBegin:
728 backward_word (in);
729 break;
730 case CK_Right:
731 case CK_MarkRight:
732 forward_char (in);
733 break;
734 case CK_WordRight:
735 case CK_MarkToWordEnd:
736 forward_word (in);
737 break;
738 case CK_BackSpace:
740 long m1, m2;
742 if (input_eval_marks (in, &m1, &m2))
743 delete_region (in, m1, m2);
744 else
745 backward_delete (in);
747 break;
748 case CK_Delete:
749 if (in->first)
750 port_region_marked_for_delete (in);
751 else
753 long m1, m2;
755 if (input_eval_marks (in, &m1, &m2))
756 delete_region (in, m1, m2);
757 else
758 delete_char (in);
760 break;
761 case CK_DeleteToWordEnd:
762 kill_word (in);
763 break;
764 case CK_DeleteToWordBegin:
765 back_kill_word (in);
766 break;
767 case CK_Mark:
768 input_mark_cmd (in, TRUE);
769 break;
770 case CK_Remove:
771 delete_region (in, in->point, MAX (in->mark, 0));
772 break;
773 case CK_DeleteToEnd:
774 kill_line (in);
775 break;
776 case CK_Clear:
777 clear_line (in);
778 break;
779 case CK_Store:
780 copy_region (in, MAX (in->mark, 0), in->point);
781 break;
782 case CK_Cut:
784 long m;
786 m = MAX (in->mark, 0);
787 copy_region (in, m, in->point);
788 delete_region (in, in->point, m);
790 break;
791 case CK_Yank:
792 yank (in);
793 break;
794 case CK_Paste:
795 ins_from_clip (in);
796 break;
797 case CK_HistoryPrev:
798 hist_prev (in);
799 break;
800 case CK_HistoryNext:
801 hist_next (in);
802 break;
803 case CK_History:
804 do_show_hist (in);
805 break;
806 case CK_Complete:
807 complete (in);
808 break;
809 default:
810 res = MSG_NOT_HANDLED;
813 switch (command)
815 case CK_MarkLeft:
816 case CK_MarkRight:
817 case CK_MarkToWordBegin:
818 case CK_MarkToWordEnd:
819 case CK_MarkToHome:
820 case CK_MarkToEnd:
821 /* do nothing */
822 break;
823 default:
824 in->mark = -1;
825 break;
828 return res;
831 /* --------------------------------------------------------------------------------------------- */
833 /* "history_load" event handler */
834 static gboolean
835 input_load_history (const gchar * event_group_name, const gchar * event_name,
836 gpointer init_data, gpointer data)
838 WInput *in = INPUT (init_data);
839 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
841 (void) event_group_name;
842 (void) event_name;
844 in->history.list = history_load (ev->cfg, in->history.name);
845 in->history.current = in->history.list;
847 if (in->init_from_history)
849 const char *def_text = "";
851 if (in->history.list != NULL && in->history.list->data != NULL)
852 def_text = (const char *) in->history.list->data;
854 input_assign_text (in, def_text);
857 return TRUE;
860 /* --------------------------------------------------------------------------------------------- */
862 /* "history_save" event handler */
863 static gboolean
864 input_save_history (const gchar * event_group_name, const gchar * event_name,
865 gpointer init_data, gpointer data)
867 WInput *in = INPUT (init_data);
869 (void) event_group_name;
870 (void) event_name;
872 if (!in->is_password && (WIDGET (in)->owner->ret_value != B_CANCEL))
874 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
876 push_history (in, in->buffer);
877 if (in->history.changed)
878 history_save (ev->cfg, in->history.name, in->history.list);
879 in->history.changed = FALSE;
882 return TRUE;
885 /* --------------------------------------------------------------------------------------------- */
887 static void
888 input_destroy (WInput * in)
890 if (in == NULL)
892 fprintf (stderr, "Internal error: null Input *\n");
893 exit (EXIT_FAILURE);
896 input_free_completions (in);
898 /* clean history */
899 if (in->history.list != NULL)
901 /* history is already saved before this moment */
902 in->history.list = g_list_first (in->history.list);
903 g_list_free_full (in->history.list, g_free);
905 g_free (in->history.name);
906 g_free (in->buffer);
907 MC_PTR_FREE (kill_buffer);
910 /* --------------------------------------------------------------------------------------------- */
913 * Calculates the buffer index (aka "point") corresponding to some screen coordinate.
915 static int
916 input_screen_to_point (const WInput * in, int x)
918 x += in->term_first_shown;
920 if (x < 0)
921 return 0;
923 if (x < str_term_width1 (in->buffer))
924 return str_column_to_pos (in->buffer, x);
926 return str_length (in->buffer);
929 /* --------------------------------------------------------------------------------------------- */
931 static void
932 input_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
934 /* save point between MSG_MOUSE_DOWN and MSG_MOUSE_DRAG */
935 static int prev_point = 0;
936 WInput *in = INPUT (w);
938 switch (msg)
940 case MSG_MOUSE_DOWN:
941 dlg_select_widget (w);
942 in->first = FALSE;
944 if (event->x >= w->cols - HISTORY_BUTTON_WIDTH && should_show_history_button (in))
945 do_show_hist (in);
946 else
948 input_mark_cmd (in, FALSE);
949 input_set_point (in, input_screen_to_point (in, event->x));
950 /* save point for the possible following MSG_MOUSE_DRAG action */
951 prev_point = in->point;
953 break;
955 case MSG_MOUSE_DRAG:
956 /* start point: set marker using point before first MSG_MOUSE_DRAG action */
957 if (in->mark < 0)
958 in->mark = prev_point;
960 input_set_point (in, input_screen_to_point (in, event->x));
961 break;
963 default:
964 /* don't create highlight region of 0 length */
965 if (in->mark == in->point)
966 input_mark_cmd (in, FALSE);
967 break;
971 /* --------------------------------------------------------------------------------------------- */
972 /*** public functions ****************************************************************************/
973 /* --------------------------------------------------------------------------------------------- */
975 /** Create new instance of WInput object.
976 * @param y Y coordinate
977 * @param x X coordinate
978 * @param input_colors Array of used colors
979 * @param width Widget width
980 * @param def_text Default text filled in widget
981 * @param histname Name of history
982 * @param completion_flags Flags for specify type of completions
983 * @return WInput object
985 WInput *
986 input_new (int y, int x, const int *colors, int width, const char *def_text,
987 const char *histname, input_complete_t completion_flags)
989 WInput *in;
990 Widget *w;
992 in = g_new (WInput, 1);
993 w = WIDGET (in);
994 widget_init (w, y, x, 1, width, input_callback, input_mouse_callback);
995 w->options |= WOP_IS_INPUT | WOP_WANT_CURSOR;
997 in->color = colors;
998 in->first = TRUE;
999 in->mark = -1;
1000 in->term_first_shown = 0;
1001 in->disable_update = 0;
1002 in->is_password = FALSE;
1003 in->strip_password = FALSE;
1005 /* in->buffer will be corrected in "history_load" event handler */
1006 in->current_max_size = width + 1;
1007 in->buffer = g_new0 (char, in->current_max_size);
1009 /* init completions before input_assign_text() call */
1010 in->completions = NULL;
1011 in->completion_flags = completion_flags;
1013 in->init_from_history = (def_text == INPUT_LAST_TEXT);
1015 if (in->init_from_history || def_text == NULL)
1016 def_text = "";
1018 input_assign_text (in, def_text);
1020 /* prepare to history setup */
1021 in->history.list = NULL;
1022 in->history.current = NULL;
1023 in->history.changed = FALSE;
1024 in->history.name = NULL;
1025 if ((histname != NULL) && (*histname != '\0'))
1026 in->history.name = g_strdup (histname);
1027 /* history will be loaded later */
1029 in->label = NULL;
1031 return in;
1034 /* --------------------------------------------------------------------------------------------- */
1036 cb_ret_t
1037 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1039 WInput *in = INPUT (w);
1040 cb_ret_t v;
1042 switch (msg)
1044 case MSG_INIT:
1045 /* subscribe to "history_load" event */
1046 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1047 /* subscribe to "history_save" event */
1048 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1049 if (in->label != NULL)
1050 widget_set_state (WIDGET (in->label), WST_DISABLED, widget_get_state (w, WST_DISABLED));
1051 return MSG_HANDLED;
1053 case MSG_KEY:
1054 if (parm == XCTRL ('q'))
1056 quote = TRUE;
1057 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1058 quote = FALSE;
1059 return v;
1062 /* Keys we want others to handle */
1063 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1064 || parm == KEY_F (10) || parm == '\n')
1065 return MSG_NOT_HANDLED;
1067 /* When pasting multiline text, insert literal Enter */
1068 if ((parm & ~KEY_M_MASK) == '\n')
1070 quote = TRUE;
1071 v = input_handle_char (in, '\n');
1072 quote = FALSE;
1073 return v;
1076 return input_handle_char (in, parm);
1078 case MSG_ACTION:
1079 return input_execute_cmd (in, parm);
1081 case MSG_RESIZE:
1082 case MSG_FOCUS:
1083 case MSG_UNFOCUS:
1084 case MSG_DRAW:
1085 input_update (in, FALSE);
1086 return MSG_HANDLED;
1088 case MSG_ENABLE:
1089 case MSG_DISABLE:
1090 if (in->label != NULL)
1091 widget_set_state (WIDGET (in->label), WST_DISABLED, msg == MSG_DISABLE);
1092 return MSG_HANDLED;
1094 case MSG_CURSOR:
1095 widget_move (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1096 return MSG_HANDLED;
1098 case MSG_DESTROY:
1099 /* unsubscribe from "history_load" event */
1100 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1101 /* unsubscribe from "history_save" event */
1102 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1103 input_destroy (in);
1104 return MSG_HANDLED;
1106 default:
1107 return widget_default_callback (w, sender, msg, parm, data);
1111 /* --------------------------------------------------------------------------------------------- */
1113 void
1114 input_set_default_colors (void)
1116 input_colors[WINPUTC_MAIN] = INPUT_COLOR;
1117 input_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1118 input_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1119 input_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1122 /* --------------------------------------------------------------------------------------------- */
1124 cb_ret_t
1125 input_handle_char (WInput * in, int key)
1127 cb_ret_t v;
1128 long command;
1130 if (quote)
1132 input_free_completions (in);
1133 v = insert_char (in, key);
1134 input_update (in, TRUE);
1135 quote = FALSE;
1136 return v;
1139 command = keybind_lookup_keymap_command (input_map, key);
1141 if (command == CK_IgnoreKey)
1143 if (key > 255)
1144 return MSG_NOT_HANDLED;
1145 if (in->first)
1146 port_region_marked_for_delete (in);
1147 input_free_completions (in);
1148 v = insert_char (in, key);
1150 else
1152 if (command != CK_Complete)
1153 input_free_completions (in);
1154 input_execute_cmd (in, command);
1155 v = MSG_HANDLED;
1156 if (in->first)
1157 input_update (in, TRUE); /* needed to clear in->first */
1160 input_update (in, TRUE);
1161 return v;
1164 /* --------------------------------------------------------------------------------------------- */
1166 /* This function is a test for a special input key used in complete.c */
1167 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1168 and 2 if it is a complete key */
1170 input_key_is_in_map (WInput * in, int key)
1172 long command;
1174 (void) in;
1176 command = keybind_lookup_keymap_command (input_map, key);
1177 if (command == CK_IgnoreKey)
1178 return 0;
1180 return (command == CK_Complete) ? 2 : 1;
1183 /* --------------------------------------------------------------------------------------------- */
1185 void
1186 input_assign_text (WInput * in, const char *text)
1188 Widget *w = WIDGET (in);
1189 size_t text_len, buffer_len;
1191 if (text == NULL)
1192 text = "";
1194 input_free_completions (in);
1195 in->mark = -1;
1196 in->need_push = TRUE;
1197 in->charpoint = 0;
1199 text_len = strlen (text);
1200 buffer_len = 1 + MAX ((size_t) w->cols, text_len);
1201 in->current_max_size = buffer_len;
1202 if (buffer_len > (size_t) w->cols)
1203 in->buffer = g_realloc (in->buffer, buffer_len);
1204 memmove (in->buffer, text, text_len + 1);
1205 in->point = str_length (in->buffer);
1206 input_update (in, TRUE);
1209 /* --------------------------------------------------------------------------------------------- */
1211 gboolean
1212 input_is_empty (const WInput * in)
1214 return (in == NULL || in->buffer == NULL || in->buffer[0] == '\0');
1217 /* --------------------------------------------------------------------------------------------- */
1219 /* Inserts text in input line */
1220 void
1221 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1223 input_disable_update (in);
1224 while (*text != '\0')
1225 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1226 if (insert_extra_space)
1227 input_handle_char (in, ' ');
1228 input_enable_update (in);
1229 input_update (in, TRUE);
1232 /* --------------------------------------------------------------------------------------------- */
1234 void
1235 input_set_point (WInput * in, int pos)
1237 int max_pos;
1239 max_pos = str_length (in->buffer);
1240 pos = MIN (pos, max_pos);
1241 if (pos != in->point)
1242 input_free_completions (in);
1243 in->point = pos;
1244 in->charpoint = 0;
1245 input_update (in, TRUE);
1248 /* --------------------------------------------------------------------------------------------- */
1250 void
1251 input_update (WInput * in, gboolean clear_first)
1253 Widget *w = WIDGET (in);
1254 int has_history = 0;
1255 int buf_len;
1256 const char *cp;
1257 int pw;
1259 if (in->disable_update != 0)
1260 return;
1262 /* don't draw widget not put into dialog */
1263 if (w->owner == NULL || !widget_get_state (WIDGET (w->owner), WST_ACTIVE))
1264 return;
1266 if (should_show_history_button (in))
1267 has_history = HISTORY_BUTTON_WIDTH;
1269 buf_len = str_length (in->buffer);
1271 /* Adjust the mark */
1272 in->mark = MIN (in->mark, buf_len);
1274 pw = str_term_width2 (in->buffer, in->point);
1276 /* Make the point visible */
1277 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + w->cols - has_history))
1279 in->term_first_shown = pw - (w->cols / 3);
1280 if (in->term_first_shown < 0)
1281 in->term_first_shown = 0;
1284 if (has_history != 0)
1285 draw_history_button (in);
1287 if (widget_get_state (w, WST_DISABLED))
1288 tty_setcolor (DISABLED_COLOR);
1289 else if (in->first)
1290 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1291 else
1292 tty_setcolor (in->color[WINPUTC_MAIN]);
1294 widget_move (in, 0, 0);
1296 if (!in->is_password)
1298 if (in->mark < 0)
1299 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1300 w->cols - has_history));
1301 else
1303 long m1, m2;
1305 if (input_eval_marks (in, &m1, &m2))
1307 tty_setcolor (in->color[WINPUTC_MAIN]);
1308 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1309 tty_print_string (cp);
1310 tty_setcolor (in->color[WINPUTC_MARK]);
1311 if (m1 < in->term_first_shown)
1313 widget_move (in, 0, 0);
1314 tty_print_string (str_term_substring
1315 (in->buffer, in->term_first_shown,
1316 m2 - in->term_first_shown));
1318 else
1320 int sel_width, buf_width;
1322 widget_move (in, 0, m1 - in->term_first_shown);
1323 buf_width = str_term_width2 (in->buffer, m1);
1324 sel_width =
1325 MIN (m2 - m1, (w->cols - has_history) - (buf_width - in->term_first_shown));
1326 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1331 else
1333 int i;
1335 cp = str_term_substring (in->buffer, in->term_first_shown, w->cols - has_history);
1336 tty_setcolor (in->color[WINPUTC_MAIN]);
1337 for (i = 0; i < w->cols - has_history; i++)
1339 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1340 tty_print_char ('*');
1341 else
1342 tty_print_char (' ');
1343 if (cp[0] != '\0')
1344 str_cnext_char (&cp);
1348 if (clear_first)
1349 in->first = FALSE;
1352 /* --------------------------------------------------------------------------------------------- */
1354 void
1355 input_enable_update (WInput * in)
1357 in->disable_update--;
1358 input_update (in, FALSE);
1361 /* --------------------------------------------------------------------------------------------- */
1363 void
1364 input_disable_update (WInput * in)
1366 in->disable_update++;
1369 /* --------------------------------------------------------------------------------------------- */
1372 * Cleans the input line and adds the current text to the history
1374 * @param in the input line
1376 void
1377 input_clean (WInput * in)
1379 push_history (in, in->buffer);
1380 in->need_push = TRUE;
1381 in->buffer[0] = '\0';
1382 in->point = 0;
1383 in->charpoint = 0;
1384 in->mark = -1;
1385 input_free_completions (in);
1386 input_update (in, FALSE);
1389 /* --------------------------------------------------------------------------------------------- */
1391 void
1392 input_free_completions (WInput * in)
1394 g_strfreev (in->completions);
1395 in->completions = NULL;
1398 /* --------------------------------------------------------------------------------------------- */