Enable -Wunused-but-set-variable flag by default.
[midnight-commander.git] / lib / widget / input.c
blob5d671ec6d6d5c6b009e620c52548b519d6296df9
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),
199 g_list_position (in->history_current, in->history));
200 if (r != NULL)
202 input_assign_text (in, r);
203 g_free (r);
206 /* Has history cleaned up or not? */
207 if (len != get_history_length (in->history))
208 in->history_changed = TRUE;
211 /* --------------------------------------------------------------------------------------------- */
213 * Strip password from incomplete url (just user:pass@host without VFS prefix).
215 * @param url partial URL
216 * @return newly allocated string without password
219 static char *
220 input_history_strip_password (char *url)
222 char *at, *delim, *colon;
224 at = strrchr (url, '@');
225 if (at == NULL)
226 return g_strdup (url);
228 /* TODO: handle ':' and '@' in password */
230 delim = strstr (url, VFS_PATH_URL_DELIMITER);
231 if (delim != NULL)
232 colon = strchr (delim + strlen (VFS_PATH_URL_DELIMITER), ':');
233 else
234 colon = strchr (url, ':');
236 /* if 'colon' before 'at', 'colon' delimits user and password: user:password@host */
237 /* if 'colon' after 'at', 'colon' delimits host and port: user@host:port */
238 if (colon != NULL && colon > at)
239 colon = NULL;
241 if (colon == NULL)
242 return g_strdup (url);
243 *colon = '\0';
245 return g_strconcat (url, at, NULL);
248 /* --------------------------------------------------------------------------------------------- */
250 static void
251 push_history (WInput * in, const char *text)
253 char *t;
254 gboolean empty;
256 if (text == NULL)
257 return;
259 t = g_strstrip (g_strdup (text));
260 empty = *t == '\0';
261 g_free (t);
262 t = g_strdup (empty ? "" : text);
264 if (!empty && in->history_name != NULL && in->strip_password)
267 We got string user:pass@host without any VFS prefixes
268 and vfs_path_to_str_flags (t, VPF_STRIP_PASSWORD) doesn't work.
269 Therefore we want to strip password in separate algorithm
271 char *url_with_stripped_password;
273 url_with_stripped_password = input_history_strip_password (t);
274 g_free (t);
275 t = url_with_stripped_password;
278 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
279 in->history_changed)
281 in->history = list_append_unique (in->history, t);
282 in->history_current = in->history;
283 in->history_changed = TRUE;
285 else
286 g_free (t);
288 in->need_push = FALSE;
291 /* --------------------------------------------------------------------------------------------- */
293 static void
294 move_buffer_backward (WInput * in, int start, int end)
296 int i, pos, len;
297 int str_len;
299 str_len = str_length (in->buffer);
300 if (start >= str_len || end > str_len + 1)
301 return;
303 pos = str_offset_to_pos (in->buffer, start);
304 len = str_offset_to_pos (in->buffer, end) - pos;
306 for (i = pos; in->buffer[i + len - 1]; i++)
307 in->buffer[i] = in->buffer[i + len];
310 /* --------------------------------------------------------------------------------------------- */
312 static cb_ret_t
313 insert_char (WInput * in, int c_code)
315 size_t i;
316 int res;
318 if (in->highlight)
320 long m1, m2;
321 if (input_eval_marks (in, &m1, &m2))
322 delete_region (in, m1, m2);
324 if (c_code == -1)
325 return MSG_NOT_HANDLED;
327 if (in->charpoint >= MB_LEN_MAX)
328 return MSG_HANDLED;
330 in->charbuf[in->charpoint] = c_code;
331 in->charpoint++;
333 res = str_is_valid_char (in->charbuf, in->charpoint);
334 if (res < 0)
336 if (res != -2)
337 in->charpoint = 0; /* broken multibyte char, skip */
338 return MSG_HANDLED;
341 in->need_push = TRUE;
342 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
344 /* Expand the buffer */
345 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
346 char *narea = g_try_renew (char, in->buffer, new_length);
347 if (narea)
349 in->buffer = narea;
350 in->current_max_size = new_length;
354 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
356 /* bytes from begin */
357 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
358 /* move chars */
359 size_t rest_bytes = strlen (in->buffer + ins_point);
361 for (i = rest_bytes + 1; i > 0; i--)
362 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
364 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
365 in->point++;
368 in->charpoint = 0;
369 return MSG_HANDLED;
372 /* --------------------------------------------------------------------------------------------- */
374 static void
375 beginning_of_line (WInput * in)
377 in->point = 0;
378 in->charpoint = 0;
381 /* --------------------------------------------------------------------------------------------- */
383 static void
384 end_of_line (WInput * in)
386 in->point = str_length (in->buffer);
387 in->charpoint = 0;
390 /* --------------------------------------------------------------------------------------------- */
392 static void
393 backward_char (WInput * in)
395 const char *act;
397 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
398 if (in->point > 0)
399 in->point -= str_cprev_noncomb_char (&act, in->buffer);
400 in->charpoint = 0;
403 /* --------------------------------------------------------------------------------------------- */
405 static void
406 forward_char (WInput * in)
408 const char *act;
410 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
411 if (act[0] != '\0')
412 in->point += str_cnext_noncomb_char (&act);
413 in->charpoint = 0;
416 /* --------------------------------------------------------------------------------------------- */
418 static void
419 forward_word (WInput * in)
421 const char *p;
423 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
424 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
426 str_cnext_char (&p);
427 in->point++;
429 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
431 str_cnext_char (&p);
432 in->point++;
436 /* --------------------------------------------------------------------------------------------- */
438 static void
439 backward_word (WInput * in)
441 const char *p, *p_tmp;
443 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
444 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
446 while (p != in->buffer)
448 p_tmp = p;
449 str_cprev_char (&p);
450 if (!str_isspace (p) && !str_ispunct (p))
452 p = p_tmp;
453 break;
455 in->point--;
457 while (p != in->buffer)
459 str_cprev_char (&p);
460 if (str_isspace (p) || str_ispunct (p))
461 break;
463 in->point--;
467 /* --------------------------------------------------------------------------------------------- */
469 static void
470 backward_delete (WInput * in)
472 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
473 int start;
475 if (in->point == 0)
476 return;
478 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
479 move_buffer_backward (in, start, in->point);
480 in->charpoint = 0;
481 in->need_push = TRUE;
482 in->point = start;
485 /* --------------------------------------------------------------------------------------------- */
487 static void
488 delete_char (WInput * in)
490 const char *act;
491 int end = in->point;
493 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
494 end += str_cnext_noncomb_char (&act);
496 move_buffer_backward (in, in->point, end);
497 in->charpoint = 0;
498 in->need_push = TRUE;
501 /* --------------------------------------------------------------------------------------------- */
503 static void
504 copy_region (WInput * in, int x_first, int x_last)
506 int first = min (x_first, x_last);
507 int last = max (x_first, x_last);
509 if (last == first)
511 /* Copy selected files to clipboard */
512 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
513 /* try use external clipboard utility */
514 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
515 return;
518 g_free (kill_buffer);
520 first = str_offset_to_pos (in->buffer, first);
521 last = str_offset_to_pos (in->buffer, last);
523 kill_buffer = g_strndup (in->buffer + first, last - first);
525 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
526 /* try use external clipboard utility */
527 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
530 /* --------------------------------------------------------------------------------------------- */
532 static void
533 kill_word (WInput * in)
535 int old_point = in->point;
536 int new_point;
538 forward_word (in);
539 new_point = in->point;
540 in->point = old_point;
542 delete_region (in, old_point, new_point);
543 in->need_push = TRUE;
544 in->charpoint = 0;
547 /* --------------------------------------------------------------------------------------------- */
549 static void
550 back_kill_word (WInput * in)
552 int old_point = in->point;
553 int new_point;
555 backward_word (in);
556 new_point = in->point;
557 in->point = old_point;
559 delete_region (in, old_point, new_point);
560 in->need_push = TRUE;
563 /* --------------------------------------------------------------------------------------------- */
565 static void
566 yank (WInput * in)
568 if (kill_buffer != NULL)
570 char *p;
572 in->charpoint = 0;
573 for (p = kill_buffer; *p != '\0'; p++)
574 insert_char (in, *p);
575 in->charpoint = 0;
579 /* --------------------------------------------------------------------------------------------- */
581 static void
582 kill_line (WInput * in)
584 int chp;
586 chp = str_offset_to_pos (in->buffer, in->point);
587 g_free (kill_buffer);
588 kill_buffer = g_strdup (&in->buffer[chp]);
589 in->buffer[chp] = '\0';
590 in->charpoint = 0;
593 /* --------------------------------------------------------------------------------------------- */
595 static void
596 clear_line (WInput * in)
598 in->need_push = TRUE;
599 in->buffer[0] = '\0';
600 in->point = 0;
601 in->mark = 0;
602 in->highlight = FALSE;
603 in->charpoint = 0;
606 /* --------------------------------------------------------------------------------------------- */
608 static void
609 ins_from_clip (WInput * in)
611 char *p = NULL;
612 ev_clipboard_text_from_file_t event_data;
614 /* try use external clipboard utility */
615 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
617 event_data.text = &p;
618 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
619 if (event_data.ret)
621 char *pp;
623 for (pp = p; *pp != '\0'; pp++)
624 insert_char (in, *pp);
626 g_free (p);
630 /* --------------------------------------------------------------------------------------------- */
632 static void
633 hist_prev (WInput * in)
635 GList *prev;
637 if (in->history == NULL)
638 return;
640 if (in->need_push)
641 push_history (in, in->buffer);
643 prev = g_list_previous (in->history_current);
644 if (prev != NULL)
646 input_assign_text (in, (char *) prev->data);
647 in->history_current = prev;
648 in->history_changed = TRUE;
649 in->need_push = FALSE;
653 /* --------------------------------------------------------------------------------------------- */
655 static void
656 hist_next (WInput * in)
658 GList *next;
660 if (in->need_push)
662 push_history (in, in->buffer);
663 input_assign_text (in, "");
664 return;
667 if (in->history == NULL)
668 return;
670 next = g_list_next (in->history_current);
671 if (next == NULL)
673 input_assign_text (in, "");
674 in->history_current = in->history;
676 else
678 input_assign_text (in, (char *) next->data);
679 in->history_current = next;
680 in->history_changed = TRUE;
681 in->need_push = FALSE;
685 /* --------------------------------------------------------------------------------------------- */
687 static void
688 port_region_marked_for_delete (WInput * in)
690 in->buffer[0] = '\0';
691 in->point = 0;
692 in->first = FALSE;
693 in->charpoint = 0;
696 /* --------------------------------------------------------------------------------------------- */
698 static cb_ret_t
699 input_execute_cmd (WInput * in, unsigned long command)
701 cb_ret_t res = MSG_HANDLED;
703 /* a highlight command like shift-arrow */
704 if (command == CK_MarkLeft || command == CK_MarkRight ||
705 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
706 command == CK_MarkToHome || command == CK_MarkToEnd)
708 if (!in->highlight)
710 input_mark_cmd (in, FALSE); /* clear */
711 input_mark_cmd (in, TRUE); /* marking on */
715 switch (command)
717 case CK_WordRight:
718 case CK_WordLeft:
719 case CK_Right:
720 case CK_Left:
721 if (in->highlight)
722 input_mark_cmd (in, FALSE);
725 switch (command)
727 case CK_Home:
728 case CK_MarkToHome:
729 beginning_of_line (in);
730 break;
731 case CK_End:
732 case CK_MarkToEnd:
733 end_of_line (in);
734 break;
735 case CK_Left:
736 case CK_MarkLeft:
737 backward_char (in);
738 break;
739 case CK_WordLeft:
740 case CK_MarkToWordBegin:
741 backward_word (in);
742 break;
743 case CK_Right:
744 case CK_MarkRight:
745 forward_char (in);
746 break;
747 case CK_WordRight:
748 case CK_MarkToWordEnd:
749 forward_word (in);
750 break;
751 case CK_BackSpace:
752 if (in->highlight)
754 long m1, m2;
755 if (input_eval_marks (in, &m1, &m2))
756 delete_region (in, m1, m2);
758 else
759 backward_delete (in);
760 break;
761 case CK_Delete:
762 if (in->first)
763 port_region_marked_for_delete (in);
764 else if (in->highlight)
766 long m1, m2;
767 if (input_eval_marks (in, &m1, &m2))
768 delete_region (in, m1, m2);
770 else
771 delete_char (in);
772 break;
773 case CK_DeleteToWordEnd:
774 kill_word (in);
775 break;
776 case CK_DeleteToWordBegin:
777 back_kill_word (in);
778 break;
779 case CK_Mark:
780 input_mark_cmd (in, TRUE);
781 break;
782 case CK_Remove:
783 delete_region (in, in->point, in->mark);
784 break;
785 case CK_DeleteToEnd:
786 kill_line (in);
787 break;
788 case CK_Clear:
789 clear_line (in);
790 break;
791 case CK_Store:
792 copy_region (in, in->mark, in->point);
793 break;
794 case CK_Cut:
795 copy_region (in, in->mark, in->point);
796 delete_region (in, in->point, in->mark);
797 break;
798 case CK_Yank:
799 yank (in);
800 break;
801 case CK_Paste:
802 ins_from_clip (in);
803 break;
804 case CK_HistoryPrev:
805 hist_prev (in);
806 break;
807 case CK_HistoryNext:
808 hist_next (in);
809 break;
810 case CK_History:
811 do_show_hist (in);
812 break;
813 case CK_Complete:
814 complete (in);
815 break;
816 default:
817 res = MSG_NOT_HANDLED;
820 if (command != CK_MarkLeft && command != CK_MarkRight &&
821 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
822 command != CK_MarkToHome && command != CK_MarkToEnd)
823 in->highlight = FALSE;
825 return res;
828 /* --------------------------------------------------------------------------------------------- */
830 /* "history_load" event handler */
831 static gboolean
832 input_load_history (const gchar * event_group_name, const gchar * event_name,
833 gpointer init_data, gpointer data)
835 WInput *in = INPUT (init_data);
836 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
837 const char *def_text;
838 size_t buffer_len;
840 (void) event_group_name;
841 (void) event_name;
843 in->history = history_load (ev->cfg, in->history_name);
844 in->history_current = in->history;
846 if (in->init_text == NULL)
847 def_text = "";
848 else if (in->init_text == INPUT_LAST_TEXT)
850 if (in->history != NULL && in->history->data != NULL)
851 def_text = (const char *) in->history->data;
852 else
853 def_text = "";
855 in->init_text = NULL;
857 else
858 def_text = in->init_text;
860 buffer_len = strlen (def_text);
861 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
862 in->current_max_size = buffer_len;
863 if (buffer_len > (size_t) in->field_width)
864 in->buffer = g_realloc (in->buffer, buffer_len);
865 strcpy (in->buffer, def_text);
866 in->point = str_length (in->buffer);
868 return TRUE;
871 /* --------------------------------------------------------------------------------------------- */
873 /* "history_save" event handler */
874 static gboolean
875 input_save_history (const gchar * event_group_name, const gchar * event_name,
876 gpointer init_data, gpointer data)
878 WInput *in = INPUT (init_data);
880 (void) event_group_name;
881 (void) event_name;
883 if (!in->is_password && (WIDGET (in)->owner->ret_value != B_CANCEL))
885 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
887 push_history (in, in->buffer);
888 if (in->history_changed)
889 history_save (ev->cfg, in->history_name, in->history);
890 in->history_changed = FALSE;
893 return TRUE;
896 /* --------------------------------------------------------------------------------------------- */
898 static void
899 input_destroy (WInput * in)
901 if (in == NULL)
903 fprintf (stderr, "Internal error: null Input *\n");
904 exit (EXIT_FAILURE);
907 input_free_completions (in);
909 /* clean history */
910 if (in->history != NULL)
912 /* history is already saved before this moment */
913 in->history = g_list_first (in->history);
914 g_list_foreach (in->history, (GFunc) g_free, NULL);
915 g_list_free (in->history);
917 g_free (in->history_name);
919 g_free (in->buffer);
920 input_free_completions (in);
921 g_free (in->init_text);
923 g_free (kill_buffer);
924 kill_buffer = NULL;
927 /* --------------------------------------------------------------------------------------------- */
929 static int
930 input_event (Gpm_Event * event, void *data)
932 WInput *in = INPUT (data);
933 Widget *w = WIDGET (data);
935 if (!mouse_global_in_widget (event, w))
936 return MOU_UNHANDLED;
938 if ((event->type & GPM_DOWN) != 0)
940 in->first = FALSE;
941 input_mark_cmd (in, FALSE);
944 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
946 Gpm_Event local;
948 local = mouse_get_local (event, w);
950 dlg_select_widget (w);
952 if (local.x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
953 && should_show_history_button (in))
954 do_show_hist (in);
955 else
957 in->point = str_length (in->buffer);
958 if (local.x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
959 in->point = str_column_to_pos (in->buffer, local.x + in->term_first_shown - 1);
962 input_update (in, TRUE);
965 /* A lone up mustn't do anything */
966 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
967 return MOU_NORMAL;
969 if ((event->type & GPM_DRAG) == 0)
970 input_mark_cmd (in, TRUE);
972 return MOU_NORMAL;
975 /* --------------------------------------------------------------------------------------------- */
978 * Callback for applying new options to input widget.
980 * @param w widget
981 * @param options options set
982 * @param enable TRUE if specified options should be added, FALSE if options should be removed
984 static void
985 input_set_options_callback (Widget * w, widget_options_t options, gboolean enable)
987 WInput *in = INPUT (w);
989 widget_default_set_options_callback (w, options, enable);
990 if (in->label != NULL)
991 widget_set_options (WIDGET (in->label), options, enable);
994 /* --------------------------------------------------------------------------------------------- */
995 /*** public functions ****************************************************************************/
996 /* --------------------------------------------------------------------------------------------- */
998 /** Create new instance of WInput object.
999 * @param y Y coordinate
1000 * @param x X coordinate
1001 * @param input_colors Array of used colors
1002 * @param width Widget width
1003 * @param def_text Default text filled in widget
1004 * @param histname Name of history
1005 * @param completion_flags Flags for specify type of completions
1006 * @return WInput object
1008 WInput *
1009 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
1010 const char *histname, input_complete_t completion_flags)
1012 WInput *in;
1013 Widget *w;
1015 in = g_new (WInput, 1);
1016 w = WIDGET (in);
1017 init_widget (w, y, x, 1, width, input_callback, input_event);
1018 w->options |= W_IS_INPUT;
1019 w->set_options = input_set_options_callback;
1021 memmove (in->color, input_colors, sizeof (input_colors_t));
1023 in->field_width = width;
1024 in->first = TRUE;
1025 in->highlight = FALSE;
1026 in->term_first_shown = 0;
1027 in->disable_update = 0;
1028 in->mark = 0;
1029 in->need_push = TRUE;
1030 in->is_password = FALSE;
1031 in->charpoint = 0;
1032 in->strip_password = FALSE;
1034 /* in->buffer will be corrected in "history_load" event handler */
1035 in->current_max_size = width + 1;
1036 in->buffer = g_new0 (char, in->current_max_size);
1037 in->point = 0;
1039 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
1041 in->completions = NULL;
1042 in->completion_flags = completion_flags;
1044 /* prepare to history setup */
1045 in->history = NULL;
1046 in->history_current = NULL;
1047 in->history_changed = FALSE;
1048 in->history_name = NULL;
1049 if ((histname != NULL) && (*histname != '\0'))
1050 in->history_name = g_strdup (histname);
1051 /* history will be loaded later */
1053 in->label = NULL;
1055 return in;
1058 /* --------------------------------------------------------------------------------------------- */
1060 cb_ret_t
1061 input_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
1063 WInput *in = INPUT (w);
1064 cb_ret_t v;
1066 switch (msg)
1068 case MSG_INIT:
1069 /* subscribe to "history_load" event */
1070 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1071 /* subscribe to "history_save" event */
1072 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1073 return MSG_HANDLED;
1075 case MSG_KEY:
1076 if (parm == XCTRL ('q'))
1078 quote = 1;
1079 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1080 quote = 0;
1081 return v;
1084 /* Keys we want others to handle */
1085 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1086 || parm == KEY_F (10) || parm == '\n')
1087 return MSG_NOT_HANDLED;
1089 /* When pasting multiline text, insert literal Enter */
1090 if ((parm & ~KEY_M_MASK) == '\n')
1092 quote = 1;
1093 v = input_handle_char (in, '\n');
1094 quote = 0;
1095 return v;
1098 return input_handle_char (in, parm);
1100 case MSG_ACTION:
1101 return input_execute_cmd (in, parm);
1103 case MSG_FOCUS:
1104 case MSG_UNFOCUS:
1105 case MSG_DRAW:
1106 case MSG_RESIZE:
1107 input_update (in, FALSE);
1108 return MSG_HANDLED;
1110 case MSG_CURSOR:
1111 widget_move (in, 0, str_term_width2 (in->buffer, in->point) - in->term_first_shown);
1112 return MSG_HANDLED;
1114 case MSG_DESTROY:
1115 /* unsubscribe from "history_load" event */
1116 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1117 /* unsubscribe from "history_save" event */
1118 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1119 input_destroy (in);
1120 return MSG_HANDLED;
1122 default:
1123 return widget_default_callback (w, sender, msg, parm, data);
1127 /* --------------------------------------------------------------------------------------------- */
1129 /** Get default colors for WInput widget.
1130 * @return default colors
1132 const int *
1133 input_get_default_colors (void)
1135 static input_colors_t standart_colors;
1137 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1138 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1139 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1140 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1142 return standart_colors;
1145 /* --------------------------------------------------------------------------------------------- */
1147 void
1148 input_set_origin (WInput * in, int x, int field_width)
1150 WIDGET (in)->x = x;
1151 in->field_width = WIDGET (in)->cols = field_width;
1152 input_update (in, FALSE);
1155 /* --------------------------------------------------------------------------------------------- */
1157 cb_ret_t
1158 input_handle_char (WInput * in, int key)
1160 cb_ret_t v;
1161 unsigned long command;
1163 if (quote != 0)
1165 input_free_completions (in);
1166 v = insert_char (in, key);
1167 input_update (in, TRUE);
1168 quote = 0;
1169 return v;
1172 command = keybind_lookup_keymap_command (input_map, key);
1174 if (command == CK_IgnoreKey)
1176 if (key > 255)
1177 return MSG_NOT_HANDLED;
1178 if (in->first)
1179 port_region_marked_for_delete (in);
1180 input_free_completions (in);
1181 v = insert_char (in, key);
1183 else
1185 if (command != CK_Complete)
1186 input_free_completions (in);
1187 input_execute_cmd (in, command);
1188 v = MSG_HANDLED;
1189 if (in->first)
1190 input_update (in, TRUE); /* needed to clear in->first */
1193 input_update (in, TRUE);
1194 return v;
1197 /* --------------------------------------------------------------------------------------------- */
1199 /* This function is a test for a special input key used in complete.c */
1200 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1201 and 2 if it is a complete key */
1203 input_key_is_in_map (WInput * in, int key)
1205 unsigned long command;
1207 (void) in;
1209 command = keybind_lookup_keymap_command (input_map, key);
1210 if (command == CK_IgnoreKey)
1211 return 0;
1213 return (command == CK_Complete) ? 2 : 1;
1216 /* --------------------------------------------------------------------------------------------- */
1218 void
1219 input_assign_text (WInput * in, const char *text)
1221 input_free_completions (in);
1222 g_free (in->buffer);
1223 in->current_max_size = strlen (text) + 1;
1224 in->buffer = g_strndup (text, in->current_max_size); /* was in->buffer->text */
1225 in->point = str_length (in->buffer);
1226 in->mark = 0;
1227 in->need_push = TRUE;
1228 in->charpoint = 0;
1229 input_update (in, TRUE);
1232 /* --------------------------------------------------------------------------------------------- */
1234 /* Inserts text in input line */
1235 void
1236 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1238 input_disable_update (in);
1239 while (*text != '\0')
1240 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1241 if (insert_extra_space)
1242 input_handle_char (in, ' ');
1243 input_enable_update (in);
1244 input_update (in, TRUE);
1247 /* --------------------------------------------------------------------------------------------- */
1249 void
1250 input_set_point (WInput * in, int pos)
1252 int max_pos;
1254 max_pos = str_length (in->buffer);
1255 pos = min (pos, max_pos);
1256 if (pos != in->point)
1257 input_free_completions (in);
1258 in->point = pos;
1259 in->charpoint = 0;
1260 input_update (in, TRUE);
1263 /* --------------------------------------------------------------------------------------------- */
1265 void
1266 input_update (WInput * in, gboolean clear_first)
1268 int has_history = 0;
1269 int i;
1270 int buf_len;
1271 const char *cp;
1272 int pw;
1274 if (should_show_history_button (in))
1275 has_history = HISTORY_BUTTON_WIDTH;
1277 if (in->disable_update != 0)
1278 return;
1280 buf_len = str_length (in->buffer);
1281 pw = str_term_width2 (in->buffer, in->point);
1283 /* Make the point visible */
1284 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1286 in->term_first_shown = pw - (in->field_width / 3);
1287 if (in->term_first_shown < 0)
1288 in->term_first_shown = 0;
1291 /* Adjust the mark */
1292 in->mark = min (in->mark, buf_len);
1294 /* don't draw widget not put into dialog */
1295 if (WIDGET (in)->owner == NULL)
1296 return;
1298 if (has_history != 0)
1299 draw_history_button (in);
1301 if ((WIDGET (in)->options & W_DISABLED) != 0)
1302 tty_setcolor (DISABLED_COLOR);
1303 else if (in->first)
1304 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1305 else
1306 tty_setcolor (in->color[WINPUTC_MAIN]);
1308 widget_move (in, 0, 0);
1310 if (!in->is_password)
1312 if (!in->highlight)
1313 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1314 in->field_width - has_history));
1315 else
1317 long m1, m2;
1319 if (input_eval_marks (in, &m1, &m2))
1321 tty_setcolor (in->color[WINPUTC_MAIN]);
1322 cp = str_term_substring (in->buffer, in->term_first_shown,
1323 in->field_width - has_history);
1324 tty_print_string (cp);
1325 tty_setcolor (in->color[WINPUTC_MARK]);
1326 if (m1 < in->term_first_shown)
1328 widget_move (in, 0, 0);
1329 tty_print_string (str_term_substring
1330 (in->buffer, in->term_first_shown,
1331 m2 - in->term_first_shown));
1333 else
1335 int sel_width;
1337 widget_move (in, 0, m1 - in->term_first_shown);
1338 sel_width =
1339 min (m2 - m1,
1340 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1341 in->term_first_shown));
1342 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1347 else
1349 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1350 tty_setcolor (in->color[WINPUTC_MAIN]);
1351 for (i = 0; i < in->field_width - has_history; i++)
1353 if (i < (buf_len - in->term_first_shown) && cp[0] != '\0')
1354 tty_print_char ('*');
1355 else
1356 tty_print_char (' ');
1357 if (cp[0] != '\0')
1358 str_cnext_char (&cp);
1362 if (clear_first)
1363 in->first = FALSE;
1366 /* --------------------------------------------------------------------------------------------- */
1368 void
1369 input_enable_update (WInput * in)
1371 in->disable_update--;
1372 input_update (in, FALSE);
1375 /* --------------------------------------------------------------------------------------------- */
1377 void
1378 input_disable_update (WInput * in)
1380 in->disable_update++;
1383 /* --------------------------------------------------------------------------------------------- */
1386 * Cleans the input line and adds the current text to the history
1388 * @param in the input line
1390 void
1391 input_clean (WInput * in)
1393 push_history (in, in->buffer);
1394 in->need_push = TRUE;
1395 in->buffer[0] = '\0';
1396 in->point = 0;
1397 in->charpoint = 0;
1398 in->mark = 0;
1399 in->highlight = FALSE;
1400 input_free_completions (in);
1401 input_update (in, FALSE);
1404 /* --------------------------------------------------------------------------------------------- */
1406 void
1407 input_free_completions (WInput * in)
1409 g_strfreev (in->completions);
1410 in->completions = NULL;
1413 /* --------------------------------------------------------------------------------------------- */