b65aa7a42b2daf05a159b2a8228318b2eeffc4c3
[midnight-commander.git] / lib / widget / input.c
blobb65aa7a42b2daf05a159b2a8228318b2eeffc4c3
1 /*
2 Widgets for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
8 Authors:
9 Radek Doulik, 1994, 1995
10 Miguel de Icaza, 1994, 1995
11 Jakub Jelinek, 1995
12 Andrej Borsenkow, 1996
13 Norbert Warmuth, 1997
14 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
32 /** \file input.c
33 * \brief Source: WInput widget
36 #include <config.h>
38 #include <stdlib.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/mouse.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
48 #include "lib/fileloc.h"
49 #include "lib/skin.h"
50 #include "lib/strutil.h"
51 #include "lib/util.h"
52 #include "lib/keybind.h" /* global_keymap_t */
53 #include "lib/widget.h"
54 #include "lib/event.h" /* mc_event_raise() */
56 #include "input_complete.h"
58 /*** global variables ****************************************************************************/
60 int quote = 0;
62 const global_keymap_t *input_map = NULL;
64 /*** file scope macro definitions ****************************************************************/
66 #define LARGE_HISTORY_BUTTON 1
68 #ifdef LARGE_HISTORY_BUTTON
69 #define HISTORY_BUTTON_WIDTH 3
70 #else
71 #define HISTORY_BUTTON_WIDTH 1
72 #endif
74 #define should_show_history_button(in) \
75 (in->history != NULL && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 \
76 && 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 = 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, in->field_width - 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, in->field_width - HISTORY_BUTTON_WIDTH + 1);
121 #endif
123 tty_print_char (c);
126 /* --------------------------------------------------------------------------------------------- */
128 static void
129 input_set_markers (WInput * in, long m1)
131 in->mark = m1;
134 /* --------------------------------------------------------------------------------------------- */
136 static void
137 input_mark_cmd (WInput * in, gboolean mark)
139 if (mark == 0)
141 in->highlight = FALSE;
142 input_set_markers (in, 0);
144 else
146 in->highlight = TRUE;
147 input_set_markers (in, in->point);
151 /* --------------------------------------------------------------------------------------------- */
153 static gboolean
154 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
156 if (in->highlight)
158 *start_mark = min (in->mark, in->point);
159 *end_mark = max (in->mark, in->point);
160 return TRUE;
162 else
164 *start_mark = *end_mark = 0;
165 return FALSE;
169 /* --------------------------------------------------------------------------------------------- */
171 static void
172 delete_region (WInput * in, int x_first, int x_last)
174 int first = min (x_first, x_last);
175 int last = max (x_first, x_last);
176 size_t len;
178 input_mark_cmd (in, FALSE);
179 in->point = first;
180 last = str_offset_to_pos (in->buffer, last);
181 first = str_offset_to_pos (in->buffer, first);
182 len = strlen (&in->buffer[last]) + 1;
183 memmove (&in->buffer[first], &in->buffer[last], len);
184 in->charpoint = 0;
185 in->need_push = TRUE;
188 /* --------------------------------------------------------------------------------------------- */
190 static void
191 do_show_hist (WInput * in)
193 size_t len;
194 char *r;
196 len = get_history_length (in->history);
198 r = history_show (&in->history, WIDGET (in), g_list_position (in->history_current, in->history));
199 if (r != NULL)
201 input_assign_text (in, r);
202 g_free (r);
205 /* Has history cleaned up or not? */
206 if (len != get_history_length (in->history))
207 in->history_changed = TRUE;
210 /* --------------------------------------------------------------------------------------------- */
212 * Strip password from incomplete url (just user:pass@host without VFS prefix).
214 * @param url partial URL
215 * @return newly allocated string without password
218 static char *
219 input_history_strip_password (char *url)
221 char *at, *delim, *colon;
223 at = strrchr (url, '@');
224 if (at == NULL)
225 return g_strdup (url);
227 /* TODO: handle ':' and '@' in password */
229 delim = strstr (url, VFS_PATH_URL_DELIMITER);
230 if (delim != NULL)
231 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
232 else
233 colon = strchr (url, ':');
235 /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
236 /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
237 if (colon != NULL && colon > at)
238 colon = NULL;
240 if (colon == NULL)
241 return g_strdup (url);
242 *colon = '\0';
244 return g_strconcat (url, at, NULL);
247 /* --------------------------------------------------------------------------------------------- */
249 static void
250 push_history (WInput * in, const char *text)
252 char *t;
253 gboolean empty;
255 if (text == NULL)
256 return;
258 t = g_strstrip (g_strdup (text));
259 empty = *t == '\0';
260 g_free (t);
261 t = g_strdup (empty ? "" : text);
263 if (!empty && in->history_name != NULL && in->strip_password)
266 We got string user:pass@host without any VFS prefixes
267 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
268 Therefore we want to strip password in separate algorithm
270 char *url_with_stripped_password;
272 url_with_stripped_password = input_history_strip_password (t);
273 g_free (t);
274 t = url_with_stripped_password;
277 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
278 in->history_changed)
280 in->history = list_append_unique (in->history, t);
281 in->history_current = in->history;
282 in->history_changed = TRUE;
284 else
285 g_free (t);
287 in->need_push = FALSE;
290 /* --------------------------------------------------------------------------------------------- */
292 static void
293 move_buffer_backward (WInput * in, int start, int end)
295 int i, pos, len;
296 int str_len;
298 str_len = str_length (in->buffer);
299 if (start >= str_len || end > str_len + 1)
300 return;
302 pos = str_offset_to_pos (in->buffer, start);
303 len = str_offset_to_pos (in->buffer, end) - pos;
305 for (i = pos; in->buffer[i + len - 1]; i++)
306 in->buffer[i] = in->buffer[i + len];
309 /* --------------------------------------------------------------------------------------------- */
311 static cb_ret_t
312 insert_char (WInput * in, int c_code)
314 size_t i;
315 int res;
317 if (in->highlight)
319 long m1, m2;
320 if (input_eval_marks (in, &m1, &m2))
321 delete_region (in, m1, m2);
323 if (c_code == -1)
324 return MSG_NOT_HANDLED;
326 if (in->charpoint >= MB_LEN_MAX)
327 return MSG_HANDLED;
329 in->charbuf[in->charpoint] = c_code;
330 in->charpoint++;
332 res = str_is_valid_char (in->charbuf, in->charpoint);
333 if (res < 0)
335 if (res != -2)
336 in->charpoint = 0; /* broken multibyte char, skip */
337 return MSG_HANDLED;
340 in->need_push = TRUE;
341 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
343 /* Expand the buffer */
344 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
345 char *narea = g_try_renew (char, in->buffer, new_length);
346 if (narea)
348 in->buffer = narea;
349 in->current_max_size = new_length;
353 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
355 /* bytes from begin */
356 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
357 /* move chars */
358 size_t rest_bytes = strlen (in->buffer + ins_point);
360 for (i = rest_bytes + 1; i > 0; i--)
361 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
363 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
364 in->point++;
367 in->charpoint = 0;
368 return MSG_HANDLED;
371 /* --------------------------------------------------------------------------------------------- */
373 static void
374 beginning_of_line (WInput * in)
376 in->point = 0;
377 in->charpoint = 0;
380 /* --------------------------------------------------------------------------------------------- */
382 static void
383 end_of_line (WInput * in)
385 in->point = str_length (in->buffer);
386 in->charpoint = 0;
389 /* --------------------------------------------------------------------------------------------- */
391 static void
392 backward_char (WInput * in)
394 const char *act;
396 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
397 if (in->point > 0)
398 in->point -= str_cprev_noncomb_char (&act, in->buffer);
399 in->charpoint = 0;
402 /* --------------------------------------------------------------------------------------------- */
404 static void
405 forward_char (WInput * in)
407 const char *act;
409 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
410 if (act[0] != '\0')
411 in->point += str_cnext_noncomb_char (&act);
412 in->charpoint = 0;
415 /* --------------------------------------------------------------------------------------------- */
417 static void
418 forward_word (WInput * in)
420 const char *p;
422 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
423 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
425 str_cnext_char (&p);
426 in->point++;
428 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
430 str_cnext_char (&p);
431 in->point++;
435 /* --------------------------------------------------------------------------------------------- */
437 static void
438 backward_word (WInput * in)
440 const char *p, *p_tmp;
442 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
443 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
445 while (p != in->buffer)
447 p_tmp = p;
448 str_cprev_char (&p);
449 if (!str_isspace (p) && !str_ispunct (p))
451 p = p_tmp;
452 break;
454 in->point--;
456 while (p != in->buffer)
458 str_cprev_char (&p);
459 if (str_isspace (p) || str_ispunct (p))
460 break;
462 in->point--;
466 /* --------------------------------------------------------------------------------------------- */
468 static void
469 backward_delete (WInput * in)
471 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
472 int start;
474 if (in->point == 0)
475 return;
477 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
478 move_buffer_backward (in, start, in->point);
479 in->charpoint = 0;
480 in->need_push = TRUE;
481 in->point = start;
484 /* --------------------------------------------------------------------------------------------- */
486 static void
487 delete_char (WInput * in)
489 const char *act;
490 int end = in->point;
492 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
493 end += str_cnext_noncomb_char (&act);
495 move_buffer_backward (in, in->point, end);
496 in->charpoint = 0;
497 in->need_push = TRUE;
500 /* --------------------------------------------------------------------------------------------- */
502 static void
503 copy_region (WInput * in, int x_first, int x_last)
505 int first = min (x_first, x_last);
506 int last = max (x_first, x_last);
508 if (last == first)
510 /* Copy selected files to clipboard */
511 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
512 /* try use external clipboard utility */
513 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
514 return;
517 g_free (kill_buffer);
519 first = str_offset_to_pos (in->buffer, first);
520 last = str_offset_to_pos (in->buffer, last);
522 kill_buffer = g_strndup (in->buffer + first, last - first);
524 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
525 /* try use external clipboard utility */
526 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
529 /* --------------------------------------------------------------------------------------------- */
531 static void
532 kill_word (WInput * in)
534 int old_point = in->point;
535 int new_point;
537 forward_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;
543 in->charpoint = 0;
546 /* --------------------------------------------------------------------------------------------- */
548 static void
549 back_kill_word (WInput * in)
551 int old_point = in->point;
552 int new_point;
554 backward_word (in);
555 new_point = in->point;
556 in->point = old_point;
558 delete_region (in, old_point, new_point);
559 in->need_push = TRUE;
562 /* --------------------------------------------------------------------------------------------- */
564 static void
565 yank (WInput * in)
567 if (kill_buffer != NULL)
569 char *p;
571 in->charpoint = 0;
572 for (p = kill_buffer; *p != '\0'; p++)
573 insert_char (in, *p);
574 in->charpoint = 0;
578 /* --------------------------------------------------------------------------------------------- */
580 static void
581 kill_line (WInput * in)
583 int chp;
585 chp = str_offset_to_pos (in->buffer, in->point);
586 g_free (kill_buffer);
587 kill_buffer = g_strdup (&in->buffer[chp]);
588 in->buffer[chp] = '\0';
589 in->charpoint = 0;
592 /* --------------------------------------------------------------------------------------------- */
594 static void
595 clear_line (WInput * in)
597 in->need_push = TRUE;
598 in->buffer[0] = '\0';
599 in->point = 0;
600 in->mark = 0;
601 in->highlight = FALSE;
602 in->charpoint = 0;
605 /* --------------------------------------------------------------------------------------------- */
607 static void
608 ins_from_clip (WInput * in)
610 char *p = NULL;
611 ev_clipboard_text_from_file_t event_data;
613 /* try use external clipboard utility */
614 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
616 event_data.text = &p;
617 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
618 if (event_data.ret)
620 char *pp;
622 for (pp = p; *pp != '\0'; pp++)
623 insert_char (in, *pp);
625 g_free (p);
629 /* --------------------------------------------------------------------------------------------- */
631 static void
632 hist_prev (WInput * in)
634 GList *prev;
636 if (in->history == NULL)
637 return;
639 if (in->need_push)
640 push_history (in, in->buffer);
642 prev = g_list_previous (in->history_current);
643 if (prev != NULL)
645 input_assign_text (in, (char *) prev->data);
646 in->history_current = prev;
647 in->history_changed = TRUE;
648 in->need_push = FALSE;
652 /* --------------------------------------------------------------------------------------------- */
654 static void
655 hist_next (WInput * in)
657 GList *next;
659 if (in->need_push)
661 push_history (in, in->buffer);
662 input_assign_text (in, "");
663 return;
666 if (in->history == NULL)
667 return;
669 next = g_list_next (in->history_current);
670 if (next == NULL)
672 input_assign_text (in, "");
673 in->history_current = in->history;
675 else
677 input_assign_text (in, (char *) next->data);
678 in->history_current = next;
679 in->history_changed = TRUE;
680 in->need_push = FALSE;
684 /* --------------------------------------------------------------------------------------------- */
686 static void
687 port_region_marked_for_delete (WInput * in)
689 in->buffer[0] = '\0';
690 in->point = 0;
691 in->first = FALSE;
692 in->charpoint = 0;
695 /* --------------------------------------------------------------------------------------------- */
697 static cb_ret_t
698 input_execute_cmd (WInput * in, unsigned long command)
700 cb_ret_t res = MSG_HANDLED;
702 /* a highlight command like shift-arrow */
703 if (command == CK_MarkLeft || command == CK_MarkRight ||
704 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
705 command == CK_MarkToHome || command == CK_MarkToEnd)
707 if (!in->highlight)
709 input_mark_cmd (in, FALSE); /* clear */
710 input_mark_cmd (in, TRUE); /* marking on */
714 switch (command)
716 case CK_WordRight:
717 case CK_WordLeft:
718 case CK_Right:
719 case CK_Left:
720 if (in->highlight)
721 input_mark_cmd (in, FALSE);
724 switch (command)
726 case CK_Home:
727 case CK_MarkToHome:
728 beginning_of_line (in);
729 break;
730 case CK_End:
731 case CK_MarkToEnd:
732 end_of_line (in);
733 break;
734 case CK_Left:
735 case CK_MarkLeft:
736 backward_char (in);
737 break;
738 case CK_WordLeft:
739 case CK_MarkToWordBegin:
740 backward_word (in);
741 break;
742 case CK_Right:
743 case CK_MarkRight:
744 forward_char (in);
745 break;
746 case CK_WordRight:
747 case CK_MarkToWordEnd:
748 forward_word (in);
749 break;
750 case CK_BackSpace:
751 if (in->highlight)
753 long m1, m2;
754 if (input_eval_marks (in, &m1, &m2))
755 delete_region (in, m1, m2);
757 else
758 backward_delete (in);
759 break;
760 case CK_Delete:
761 if (in->first)
762 port_region_marked_for_delete (in);
763 else if (in->highlight)
765 long m1, m2;
766 if (input_eval_marks (in, &m1, &m2))
767 delete_region (in, m1, m2);
769 else
770 delete_char (in);
771 break;
772 case CK_DeleteToWordEnd:
773 kill_word (in);
774 break;
775 case CK_DeleteToWordBegin:
776 back_kill_word (in);
777 break;
778 case CK_Mark:
779 input_mark_cmd (in, TRUE);
780 break;
781 case CK_Remove:
782 delete_region (in, in->point, in->mark);
783 break;
784 case CK_DeleteToEnd:
785 kill_line (in);
786 break;
787 case CK_Clear:
788 clear_line (in);
789 break;
790 case CK_Store:
791 copy_region (in, in->mark, in->point);
792 break;
793 case CK_Cut:
794 copy_region (in, in->mark, in->point);
795 delete_region (in, in->point, in->mark);
796 break;
797 case CK_Yank:
798 yank (in);
799 break;
800 case CK_Paste:
801 ins_from_clip (in);
802 break;
803 case CK_HistoryPrev:
804 hist_prev (in);
805 break;
806 case CK_HistoryNext:
807 hist_next (in);
808 break;
809 case CK_History:
810 do_show_hist (in);
811 break;
812 case CK_Complete:
813 complete (in);
814 break;
815 default:
816 res = MSG_NOT_HANDLED;
819 if (command != CK_MarkLeft && command != CK_MarkRight &&
820 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
821 command != CK_MarkToHome && command != CK_MarkToEnd)
822 in->highlight = FALSE;
824 return res;
827 /* --------------------------------------------------------------------------------------------- */
829 /* "history_load" event handler */
830 static gboolean
831 input_load_history (const gchar * event_group_name, const gchar * event_name,
832 gpointer init_data, gpointer data)
834 WInput *in = INPUT (init_data);
835 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
836 const char *def_text;
837 size_t buffer_len;
839 (void) event_group_name;
840 (void) event_name;
842 in->history = history_load (ev->cfg, in->history_name);
843 in->history_current = in->history;
845 if (in->init_text == NULL)
846 def_text = "";
847 else if (in->init_text == INPUT_LAST_TEXT)
849 if (in->history != NULL && in->history->data != NULL)
850 def_text = (const char *) in->history->data;
851 else
852 def_text = "";
854 in->init_text = NULL;
856 else
857 def_text = in->init_text;
859 buffer_len = strlen (def_text);
860 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
861 in->current_max_size = buffer_len;
862 if (buffer_len > (size_t) in->field_width)
863 in->buffer = g_realloc (in->buffer, buffer_len);
864 strcpy (in->buffer, def_text);
865 in->point = str_length (in->buffer);
867 return TRUE;
870 /* --------------------------------------------------------------------------------------------- */
872 /* "history_save" event handler */
873 static gboolean
874 input_save_history (const gchar * event_group_name, const gchar * event_name,
875 gpointer init_data, gpointer data)
877 WInput *in = INPUT (init_data);
879 (void) event_group_name;
880 (void) event_name;
882 if (!in->is_password && (WIDGET (in)->owner->ret_value != B_CANCEL))
884 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
886 push_history (in, in->buffer);
887 if (in->history_changed)
888 history_save (ev->cfg, in->history_name, in->history);
889 in->history_changed = FALSE;
892 return TRUE;
895 /* --------------------------------------------------------------------------------------------- */
897 static void
898 input_destroy (WInput * in)
900 if (in == NULL)
902 fprintf (stderr, "Internal error: null Input *\n");
903 exit (EXIT_FAILURE);
906 input_free_completions (in);
908 /* clean history */
909 if (in->history != NULL)
911 /* history is already saved before this moment */
912 in->history = g_list_first (in->history);
913 g_list_foreach (in->history, (GFunc) g_free, NULL);
914 g_list_free (in->history);
916 g_free (in->history_name);
918 g_free (in->buffer);
919 input_free_completions (in);
920 g_free (in->init_text);
922 g_free (kill_buffer);
923 kill_buffer = NULL;
926 /* --------------------------------------------------------------------------------------------- */
928 static int
929 input_event (Gpm_Event * event, void *data)
931 WInput *in = INPUT (data);
932 Widget *w = WIDGET (data);
934 if (!mouse_global_in_widget (event, w))
935 return MOU_UNHANDLED;
937 if ((event->type & GPM_DOWN) != 0)
939 in->first = FALSE;
940 input_mark_cmd (in, FALSE);
943 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
945 Gpm_Event local;
947 local = mouse_get_local (event, w);
949 dlg_select_widget (w);
951 if (local.x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
952 && should_show_history_button (in))
953 do_show_hist (in);
954 else
956 in->point = str_length (in->buffer);
957 if (local.x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
958 in->point = str_column_to_pos (in->buffer, local.x + in->term_first_shown - 1);
961 input_update (in, TRUE);
964 /* A lone up mustn't do anything */
965 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
966 return MOU_NORMAL;
968 if ((event->type & GPM_DRAG) == 0)
969 input_mark_cmd (in, TRUE);
971 return MOU_NORMAL;
974 /* --------------------------------------------------------------------------------------------- */
977 * Callback for applying new options to input widget.
979 * @param w widget
980 * @param options options set
981 * @param enable TRUE if specified options should be added, FALSE if options should be removed
983 static void
984 input_set_options_callback (Widget * w, widget_options_t options, gboolean enable)
986 WInput *in = INPUT (w);
988 widget_default_set_options_callback (w, options, enable);
989 if (in->label != NULL)
990 widget_set_options (WIDGET (in->label), options, enable);
993 /* --------------------------------------------------------------------------------------------- */
994 /*** public functions ****************************************************************************/
995 /* --------------------------------------------------------------------------------------------- */
997 /** Create new instance of WInput object.
998 * @param y Y coordinate
999 * @param x X coordinate
1000 * @param input_colors Array of used colors
1001 * @param width Widget width
1002 * @param def_text Default text filled in widget
1003 * @param histname Name of history
1004 * @param completion_flags Flags for specify type of completions
1005 * @return WInput object
1007 WInput *
1008 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
1009 const char *histname, input_complete_t completion_flags)
1011 WInput *in;
1012 Widget *w;
1014 in = g_new (WInput, 1);
1015 w = WIDGET (in);
1016 init_widget (w, y, x, 1, width, input_callback, input_event);
1017 w->options |= W_IS_INPUT;
1018 w->set_options = input_set_options_callback;
1020 memmove (in->color, input_colors, sizeof (input_colors_t));
1022 in->field_width = width;
1023 in->first = TRUE;
1024 in->highlight = FALSE;
1025 in->term_first_shown = 0;
1026 in->disable_update = 0;
1027 in->mark = 0;
1028 in->need_push = TRUE;
1029 in->is_password = FALSE;
1030 in->charpoint = 0;
1031 in->strip_password = FALSE;
1033 /* in->buffer will be corrected in "history_load" event handler */
1034 in->current_max_size = width + 1;
1035 in->buffer = g_new0 (char, in->current_max_size);
1036 in->point = 0;
1038 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
1040 in->completions = NULL;
1041 in->completion_flags = completion_flags;
1043 /* prepare to history setup */
1044 in->history = NULL;
1045 in->history_current = NULL;
1046 in->history_changed = FALSE;
1047 in->history_name = NULL;
1048 if ((histname != NULL) && (*histname != '\0'))
1049 in->history_name = g_strdup (histname);
1050 /* history will be loaded later */
1052 in->label = NULL;
1054 return in;
1057 /* --------------------------------------------------------------------------------------------- */
1059 cb_ret_t
1060 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1062 WInput *in = INPUT (w);
1063 cb_ret_t v;
1065 switch (msg)
1067 case MSG_INIT:
1068 /* subscribe to "history_load" event */
1069 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1070 /* subscribe to "history_save" event */
1071 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1072 return MSG_HANDLED;
1074 case MSG_KEY:
1075 if (parm == XCTRL ('q'))
1077 quote = 1;
1078 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1079 quote = 0;
1080 return v;
1083 /* Keys we want others to handle */
1084 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1085 || parm == KEY_F (10) || parm == '\n')
1086 return MSG_NOT_HANDLED;
1088 /* When pasting multiline text, insert literal Enter */
1089 if ((parm & ~KEY_M_MASK) == '\n')
1091 quote = 1;
1092 v = input_handle_char (in, '\n');
1093 quote = 0;
1094 return v;
1097 return input_handle_char (in, parm);
1099 case MSG_ACTION:
1100 return input_execute_cmd (in, parm);
1102 case MSG_FOCUS:
1103 case MSG_UNFOCUS:
1104 case MSG_DRAW:
1105 case MSG_RESIZE:
1106 input_update (in, FALSE);
1107 return MSG_HANDLED;
1109 case MSG_CURSOR:
1110 widget_move (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1111 return MSG_HANDLED;
1113 case MSG_DESTROY:
1114 /* unsubscribe from "history_load" event */
1115 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1116 /* unsubscribe from "history_save" event */
1117 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1118 input_destroy (in);
1119 return MSG_HANDLED;
1121 default:
1122 return widget_default_callback (w, sender, msg, parm, data);
1126 /* --------------------------------------------------------------------------------------------- */
1128 /** Get default colors for WInput widget.
1129 * @return default colors
1131 const int *
1132 input_get_default_colors (void)
1134 static input_colors_t standart_colors;
1136 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1137 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1138 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1139 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1141 return standart_colors;
1144 /* --------------------------------------------------------------------------------------------- */
1146 void
1147 input_set_origin (WInput * in, int x, int field_width)
1149 WIDGET (in)->x = x;
1150 in->field_width = WIDGET (in)->cols = field_width;
1151 input_update (in, FALSE);
1154 /* --------------------------------------------------------------------------------------------- */
1156 cb_ret_t
1157 input_handle_char (WInput * in, int key)
1159 cb_ret_t v;
1160 unsigned long command;
1162 if (quote != 0)
1164 input_free_completions (in);
1165 v = insert_char (in, key);
1166 input_update (in, TRUE);
1167 quote = 0;
1168 return v;
1171 command = keybind_lookup_keymap_command (input_map, key);
1173 if (command == CK_IgnoreKey)
1175 if (key > 255)
1176 return MSG_NOT_HANDLED;
1177 if (in->first)
1178 port_region_marked_for_delete (in);
1179 input_free_completions (in);
1180 v = insert_char (in, key);
1182 else
1184 if (command != CK_Complete)
1185 input_free_completions (in);
1186 input_execute_cmd (in, command);
1187 v = MSG_HANDLED;
1188 if (in->first)
1189 input_update (in, TRUE); /* needed to clear in->first */
1192 input_update (in, TRUE);
1193 return v;
1196 /* --------------------------------------------------------------------------------------------- */
1198 /* This function is a test for a special input key used in complete.c */
1199 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1200 and 2 if it is a complete key */
1202 input_key_is_in_map (WInput * in, int key)
1204 unsigned long command;
1206 (void) in;
1208 command = keybind_lookup_keymap_command (input_map, key);
1209 if (command == CK_IgnoreKey)
1210 return 0;
1212 return (command == CK_Complete) ? 2 : 1;
1215 /* --------------------------------------------------------------------------------------------- */
1217 void
1218 input_assign_text (WInput * in, const char *text)
1220 input_free_completions (in);
1221 g_free (in->buffer);
1222 in->current_max_size = strlen (text) + 1;
1223 in->buffer = g_strndup (text, in->current_max_size); /* was in->buffer->text */
1224 in->point = str_length (in->buffer);
1225 in->mark = 0;
1226 in->need_push = TRUE;
1227 in->charpoint = 0;
1228 input_update (in, TRUE);
1231 /* --------------------------------------------------------------------------------------------- */
1233 /* Inserts text in input line */
1234 void
1235 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1237 input_disable_update (in);
1238 while (*text != '\0')
1239 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1240 if (insert_extra_space)
1241 input_handle_char (in, ' ');
1242 input_enable_update (in);
1243 input_update (in, TRUE);
1246 /* --------------------------------------------------------------------------------------------- */
1248 void
1249 input_set_point (WInput * in, int pos)
1251 int max_pos;
1253 max_pos = str_length (in->buffer);
1254 pos = min (pos, max_pos);
1255 if (pos != in->point)
1256 input_free_completions (in);
1257 in->point = pos;
1258 in->charpoint = 0;
1259 input_update (in, TRUE);
1262 /* --------------------------------------------------------------------------------------------- */
1264 void
1265 input_update (WInput * in, gboolean clear_first)
1267 int has_history = 0;
1268 int i;
1269 int buf_len;
1270 const char *cp;
1271 int pw;
1273 if (should_show_history_button (in))
1274 has_history = HISTORY_BUTTON_WIDTH;
1276 if (in->disable_update != 0)
1277 return;
1279 buf_len = str_length (in->buffer);
1280 pw = str_term_width2 (in->buffer, in->point);
1282 /* Make the point visible */
1283 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1285 in->term_first_shown = pw - (in->field_width / 3);
1286 if (in->term_first_shown < 0)
1287 in->term_first_shown = 0;
1290 /* Adjust the mark */
1291 in->mark = min (in->mark, buf_len);
1293 /* don't draw widget not put into dialog */
1294 if (WIDGET(in)->owner == NULL)
1295 return;
1297 if (has_history != 0)
1298 draw_history_button (in);
1300 if ((WIDGET (in)->options & W_DISABLED) != 0)
1301 tty_setcolor (DISABLED_COLOR);
1302 else if (in->first)
1303 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1304 else
1305 tty_setcolor (in->color[WINPUTC_MAIN]);
1307 widget_move (in, 0, 0);
1309 if (!in->is_password)
1311 if (!in->highlight)
1312 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1313 in->field_width - has_history));
1314 else
1316 long m1, m2;
1318 if (input_eval_marks (in, &m1, &m2))
1320 tty_setcolor (in->color[WINPUTC_MAIN]);
1321 cp = str_term_substring (in->buffer, in->term_first_shown,
1322 in->field_width - has_history);
1323 tty_print_string (cp);
1324 tty_setcolor (in->color[WINPUTC_MARK]);
1325 if (m1 < in->term_first_shown)
1327 widget_move (in, 0, 0);
1328 tty_print_string (str_term_substring
1329 (in->buffer, in->term_first_shown,
1330 m2 - in->term_first_shown));
1332 else
1334 int sel_width;
1336 widget_move (in, 0, m1 - in->term_first_shown);
1337 sel_width =
1338 min (m2 - m1,
1339 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1340 in->term_first_shown));
1341 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1346 else
1348 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1349 tty_setcolor (in->color[WINPUTC_MAIN]);
1350 for (i = 0; i < in->field_width - has_history; i++)
1352 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1353 tty_print_char ('*');
1354 else
1355 tty_print_char (' ');
1356 if (cp[0] != '\0')
1357 str_cnext_char (&cp);
1361 if (clear_first)
1362 in->first = FALSE;
1365 /* --------------------------------------------------------------------------------------------- */
1367 void
1368 input_enable_update (WInput * in)
1370 in->disable_update--;
1371 input_update (in, FALSE);
1374 /* --------------------------------------------------------------------------------------------- */
1376 void
1377 input_disable_update (WInput * in)
1379 in->disable_update++;
1382 /* --------------------------------------------------------------------------------------------- */
1385 * Cleans the input line and adds the current text to the history
1387 * @param in the input line
1389 void
1390 input_clean (WInput * in)
1392 push_history (in, in->buffer);
1393 in->need_push = TRUE;
1394 in->buffer[0] = '\0';
1395 in->point = 0;
1396 in->charpoint = 0;
1397 in->mark = 0;
1398 in->highlight = FALSE;
1399 input_free_completions (in);
1400 input_update (in, FALSE);
1403 /* --------------------------------------------------------------------------------------------- */
1405 void
1406 input_free_completions (WInput * in)
1408 g_strfreev (in->completions);
1409 in->completions = NULL;
1412 /* --------------------------------------------------------------------------------------------- */