Ticket #2598: u7z: Improve handling of missing p7zip binaries.
[midnight-commander.git] / lib / widget / input.c
blob790254b295f2e39dab63c0698c7b236ec9b6c80d
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
11 2009, 2010 Andrew Borodin
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 /** \file input.c
30 * \brief Source: WInput widget
33 #include <config.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/mouse.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 int quote = 0;
59 const global_keymap_t *input_map = NULL;
61 /*** file scope macro definitions ****************************************************************/
63 #define LARGE_HISTORY_BUTTON 1
65 #ifdef LARGE_HISTORY_BUTTON
66 #define HISTORY_BUTTON_WIDTH 3
67 #else
68 #define HISTORY_BUTTON_WIDTH 1
69 #endif
71 #define should_show_history_button(in) \
72 (in->history != NULL && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 \
73 && in->widget.owner != NULL)
75 /*** file scope type declarations ****************************************************************/
77 /*** file scope variables ************************************************************************/
79 /* Input widgets have a global kill ring */
80 /* Pointer to killed data */
81 static char *kill_buffer = NULL;
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 static size_t
87 get_history_length (const GList *history)
89 size_t len = 0;
91 for (; history != NULL; history = g_list_previous (history))
92 len++;
94 return len;
97 /* --------------------------------------------------------------------------------------------- */
99 static void
100 draw_history_button (WInput * in)
102 char c;
103 gboolean disabled = (((Widget *) in)->options & W_DISABLED) != 0;
105 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
106 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
107 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
108 #ifdef LARGE_HISTORY_BUTTON
110 Dlg_head *h;
111 h = in->widget.owner;
112 tty_print_string ("[ ]");
113 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
115 #endif
116 tty_print_char (c);
119 /* --------------------------------------------------------------------------------------------- */
121 static void
122 input_set_markers (WInput * in, long m1)
124 in->mark = m1;
127 /* --------------------------------------------------------------------------------------------- */
129 static void
130 input_mark_cmd (WInput * in, gboolean mark)
132 if (mark == 0)
134 in->highlight = FALSE;
135 input_set_markers (in, 0);
137 else
139 in->highlight = TRUE;
140 input_set_markers (in, in->point);
144 /* --------------------------------------------------------------------------------------------- */
146 static gboolean
147 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
149 if (in->highlight)
151 *start_mark = min (in->mark, in->point);
152 *end_mark = max (in->mark, in->point);
153 return TRUE;
155 else
157 *start_mark = *end_mark = 0;
158 return FALSE;
162 /* --------------------------------------------------------------------------------------------- */
164 static void
165 delete_region (WInput * in, int x_first, int x_last)
167 int first = min (x_first, x_last);
168 int last = max (x_first, x_last);
169 size_t len;
171 input_mark_cmd (in, FALSE);
172 in->point = first;
173 last = str_offset_to_pos (in->buffer, last);
174 first = str_offset_to_pos (in->buffer, first);
175 len = strlen (&in->buffer[last]) + 1;
176 memmove (&in->buffer[first], &in->buffer[last], len);
177 in->charpoint = 0;
178 in->need_push = TRUE;
181 /* --------------------------------------------------------------------------------------------- */
183 static void
184 do_show_hist (WInput * in)
186 size_t len;
187 char *r;
189 len = get_history_length (in->history);
191 r = history_show (&in->history, &in->widget);
192 if (r != NULL)
194 input_assign_text (in, r);
195 g_free (r);
198 /* Has history cleaned up or not? */
199 if (len != get_history_length (in->history))
200 in->history_changed = TRUE;
203 /* --------------------------------------------------------------------------------------------- */
205 static void
206 push_history (WInput * in, const char *text)
208 /* input widget where urls with passwords are entered without any
209 vfs prefix */
210 const char *password_input_fields[] = {
211 " Link to a remote machine ",
212 " FTP to machine ",
213 " SMB link to machine "
215 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
217 char *t;
218 size_t i;
219 gboolean empty;
221 if (text == NULL)
222 return;
224 #ifdef ENABLE_NLS
225 for (i = 0; i < ELEMENTS; i++)
226 password_input_fields[i] = _(password_input_fields[i]);
227 #endif
229 t = g_strstrip (g_strdup (text));
230 empty = *t == '\0';
231 g_free (t);
232 t = g_strdup (empty ? "" : text);
234 if (in->history_name != NULL)
236 /* FIXME: It is the strange code. Rewrite is needed. */
238 const char *p = in->history_name + 3;
240 for (i = 0; i < ELEMENTS; i++)
241 if (strcmp (p, password_input_fields[i]) == 0)
242 break;
244 strip_password (t, i >= ELEMENTS);
247 if (in->history == NULL || in->history->data == NULL || strcmp (in->history->data, t) != 0 ||
248 in->history_changed)
250 in->history = list_append_unique (in->history, t);
251 in->history_changed = TRUE;
253 else
254 g_free (t);
256 in->need_push = FALSE;
259 /* --------------------------------------------------------------------------------------------- */
261 static void
262 move_buffer_backward (WInput * in, int start, int end)
264 int i, pos, len;
265 int str_len;
267 str_len = str_length (in->buffer);
268 if (start >= str_len || end > str_len + 1)
269 return;
271 pos = str_offset_to_pos (in->buffer, start);
272 len = str_offset_to_pos (in->buffer, end) - pos;
274 for (i = pos; in->buffer[i + len - 1]; i++)
275 in->buffer[i] = in->buffer[i + len];
278 /* --------------------------------------------------------------------------------------------- */
280 static cb_ret_t
281 insert_char (WInput * in, int c_code)
283 size_t i;
284 int res;
286 if (in->highlight)
288 long m1, m2;
289 if (input_eval_marks (in, &m1, &m2))
290 delete_region (in, m1, m2);
292 if (c_code == -1)
293 return MSG_NOT_HANDLED;
295 if (in->charpoint >= MB_LEN_MAX)
296 return MSG_HANDLED;
298 in->charbuf[in->charpoint] = c_code;
299 in->charpoint++;
301 res = str_is_valid_char (in->charbuf, in->charpoint);
302 if (res < 0)
304 if (res != -2)
305 in->charpoint = 0; /* broken multibyte char, skip */
306 return MSG_HANDLED;
309 in->need_push = TRUE;
310 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
312 /* Expand the buffer */
313 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
314 char *narea = g_try_renew (char, in->buffer, new_length);
315 if (narea)
317 in->buffer = narea;
318 in->current_max_size = new_length;
322 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
324 /* bytes from begin */
325 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
326 /* move chars */
327 size_t rest_bytes = strlen (in->buffer + ins_point);
329 for (i = rest_bytes + 1; i > 0; i--)
330 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
332 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
333 in->point++;
336 in->charpoint = 0;
337 return MSG_HANDLED;
340 /* --------------------------------------------------------------------------------------------- */
342 static void
343 beginning_of_line (WInput * in)
345 in->point = 0;
346 in->charpoint = 0;
349 /* --------------------------------------------------------------------------------------------- */
351 static void
352 end_of_line (WInput * in)
354 in->point = str_length (in->buffer);
355 in->charpoint = 0;
358 /* --------------------------------------------------------------------------------------------- */
360 static void
361 backward_char (WInput * in)
363 const char *act;
365 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
366 if (in->point > 0)
367 in->point -= str_cprev_noncomb_char (&act, in->buffer);
368 in->charpoint = 0;
371 /* --------------------------------------------------------------------------------------------- */
373 static void
374 forward_char (WInput * in)
376 const char *act;
378 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
379 if (act[0] != '\0')
380 in->point += str_cnext_noncomb_char (&act);
381 in->charpoint = 0;
384 /* --------------------------------------------------------------------------------------------- */
386 static void
387 forward_word (WInput * in)
389 const char *p;
391 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
392 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
394 str_cnext_char (&p);
395 in->point++;
397 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
399 str_cnext_char (&p);
400 in->point++;
404 /* --------------------------------------------------------------------------------------------- */
406 static void
407 backward_word (WInput * in)
409 const char *p, *p_tmp;
411 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
412 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
414 while (p != in->buffer)
416 p_tmp = p;
417 str_cprev_char (&p);
418 if (!str_isspace (p) && !str_ispunct (p))
420 p = p_tmp;
421 break;
423 in->point--;
425 while (p != in->buffer)
427 str_cprev_char (&p);
428 if (str_isspace (p) || str_ispunct (p))
429 break;
431 in->point--;
435 /* --------------------------------------------------------------------------------------------- */
437 static void
438 backward_delete (WInput * in)
440 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
441 int start;
443 if (in->point == 0)
444 return;
446 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
447 move_buffer_backward (in, start, in->point);
448 in->charpoint = 0;
449 in->need_push = TRUE;
450 in->point = start;
453 /* --------------------------------------------------------------------------------------------- */
455 static void
456 delete_char (WInput * in)
458 const char *act;
459 int end = in->point;
461 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
462 end += str_cnext_noncomb_char (&act);
464 move_buffer_backward (in, in->point, end);
465 in->charpoint = 0;
466 in->need_push = TRUE;
469 /* --------------------------------------------------------------------------------------------- */
471 static void
472 copy_region (WInput * in, int x_first, int x_last)
474 int first = min (x_first, x_last);
475 int last = max (x_first, x_last);
477 if (last == first)
479 /* Copy selected files to clipboard */
480 mc_event_raise (MCEVENT_GROUP_FILEMANAGER, "panel_save_curent_file_to_clip_file", NULL);
481 /* try use external clipboard utility */
482 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
483 return;
486 g_free (kill_buffer);
488 first = str_offset_to_pos (in->buffer, first);
489 last = str_offset_to_pos (in->buffer, last);
491 kill_buffer = g_strndup (in->buffer + first, last - first);
493 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_to_file", kill_buffer);
494 /* try use external clipboard utility */
495 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
498 /* --------------------------------------------------------------------------------------------- */
500 static void
501 kill_word (WInput * in)
503 int old_point = in->point;
504 int new_point;
506 forward_word (in);
507 new_point = in->point;
508 in->point = old_point;
510 delete_region (in, old_point, new_point);
511 in->need_push = TRUE;
512 in->charpoint = 0;
515 /* --------------------------------------------------------------------------------------------- */
517 static void
518 back_kill_word (WInput * in)
520 int old_point = in->point;
521 int new_point;
523 backward_word (in);
524 new_point = in->point;
525 in->point = old_point;
527 delete_region (in, old_point, new_point);
528 in->need_push = TRUE;
531 /* --------------------------------------------------------------------------------------------- */
533 static void
534 yank (WInput * in)
536 if (kill_buffer != NULL)
538 char *p;
540 in->charpoint = 0;
541 for (p = kill_buffer; *p != '\0'; p++)
542 insert_char (in, *p);
543 in->charpoint = 0;
547 /* --------------------------------------------------------------------------------------------- */
549 static void
550 kill_line (WInput * in)
552 int chp;
554 chp = str_offset_to_pos (in->buffer, in->point);
555 g_free (kill_buffer);
556 kill_buffer = g_strdup (&in->buffer[chp]);
557 in->buffer[chp] = '\0';
558 in->charpoint = 0;
561 /* --------------------------------------------------------------------------------------------- */
563 static void
564 clear_line (WInput * in)
566 in->need_push = TRUE;
567 in->buffer[0] = '\0';
568 in->point = 0;
569 in->mark = 0;
570 in->highlight = FALSE;
571 in->charpoint = 0;
574 /* --------------------------------------------------------------------------------------------- */
576 static void
577 ins_from_clip (WInput * in)
579 char *p = NULL;
580 ev_clipboard_text_from_file_t event_data;
582 /* try use external clipboard utility */
583 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
585 event_data.text = &p;
586 mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_text_from_file", &event_data);
587 if (event_data.ret)
589 char *pp;
591 for (pp = p; *pp != '\0'; pp++)
592 insert_char (in, *pp);
594 g_free (p);
598 /* --------------------------------------------------------------------------------------------- */
600 static void
601 hist_prev (WInput * in)
603 GList *prev;
605 if (in->history == NULL)
606 return;
608 if (in->need_push)
609 push_history (in, in->buffer);
611 prev = g_list_previous (in->history);
612 if (prev != NULL)
614 in->history = prev;
615 in->history_changed = TRUE;
616 input_assign_text (in, (char *) prev->data);
617 in->need_push = FALSE;
621 /* --------------------------------------------------------------------------------------------- */
623 static void
624 hist_next (WInput * in)
626 GList *next;
628 if (in->need_push)
630 push_history (in, in->buffer);
631 input_assign_text (in, "");
632 return;
635 if (in->history == NULL)
636 return;
638 next = g_list_next (in->history);
639 if (next == NULL)
640 input_assign_text (in, "");
641 else
643 in->history = next;
644 in->history_changed = TRUE;
645 input_assign_text (in, (char *) next->data);
646 in->need_push = FALSE;
650 /* --------------------------------------------------------------------------------------------- */
652 static void
653 port_region_marked_for_delete (WInput * in)
655 in->buffer[0] = '\0';
656 in->point = 0;
657 in->first = FALSE;
658 in->charpoint = 0;
661 /* --------------------------------------------------------------------------------------------- */
663 static cb_ret_t
664 input_execute_cmd (WInput * in, unsigned long command)
666 cb_ret_t res = MSG_HANDLED;
668 /* a highlight command like shift-arrow */
669 if (command == CK_MarkLeft || command == CK_MarkRight ||
670 command == CK_MarkToWordBegin || command == CK_MarkToWordEnd ||
671 command == CK_MarkToHome || command == CK_MarkToEnd)
673 if (!in->highlight)
675 input_mark_cmd (in, FALSE); /* clear */
676 input_mark_cmd (in, TRUE); /* marking on */
680 switch (command)
682 case CK_WordRight:
683 case CK_WordLeft:
684 case CK_Right:
685 case CK_Left:
686 if (in->highlight)
687 input_mark_cmd (in, FALSE);
690 switch (command)
692 case CK_Home:
693 case CK_MarkToHome:
694 beginning_of_line (in);
695 break;
696 case CK_End:
697 case CK_MarkToEnd:
698 end_of_line (in);
699 break;
700 case CK_Left:
701 case CK_MarkLeft:
702 backward_char (in);
703 break;
704 case CK_WordLeft:
705 case CK_MarkToWordBegin:
706 backward_word (in);
707 break;
708 case CK_Right:
709 case CK_MarkRight:
710 forward_char (in);
711 break;
712 case CK_WordRight:
713 case CK_MarkToWordEnd:
714 forward_word (in);
715 break;
716 case CK_BackSpace:
717 if (in->highlight)
719 long m1, m2;
720 if (input_eval_marks (in, &m1, &m2))
721 delete_region (in, m1, m2);
723 else
724 backward_delete (in);
725 break;
726 case CK_Delete:
727 if (in->first)
728 port_region_marked_for_delete (in);
729 else if (in->highlight)
731 long m1, m2;
732 if (input_eval_marks (in, &m1, &m2))
733 delete_region (in, m1, m2);
735 else
736 delete_char (in);
737 break;
738 case CK_DeleteToWordEnd:
739 kill_word (in);
740 break;
741 case CK_DeleteToWordBegin:
742 back_kill_word (in);
743 break;
744 case CK_Mark:
745 input_mark_cmd (in, TRUE);
746 break;
747 case CK_Remove:
748 delete_region (in, in->point, in->mark);
749 break;
750 case CK_DeleteToEnd:
751 kill_line (in);
752 break;
753 case CK_Clear:
754 clear_line (in);
755 break;
756 case CK_Store:
757 copy_region (in, in->mark, in->point);
758 break;
759 case CK_Cut:
760 copy_region (in, in->mark, in->point);
761 delete_region (in, in->point, in->mark);
762 break;
763 case CK_Yank:
764 yank (in);
765 break;
766 case CK_Paste:
767 ins_from_clip (in);
768 break;
769 case CK_HistoryPrev:
770 hist_prev (in);
771 break;
772 case CK_HistoryNext:
773 hist_next (in);
774 break;
775 case CK_History:
776 do_show_hist (in);
777 break;
778 case CK_Complete:
779 complete (in);
780 break;
781 default:
782 res = MSG_NOT_HANDLED;
785 if (command != CK_MarkLeft && command != CK_MarkRight &&
786 command != CK_MarkToWordBegin && command != CK_MarkToWordEnd &&
787 command != CK_MarkToHome && command != CK_MarkToEnd)
788 in->highlight = FALSE;
790 return res;
793 /* --------------------------------------------------------------------------------------------- */
795 /* "history_load" event handler */
796 static gboolean
797 input_load_history (const gchar * event_group_name, const gchar * event_name,
798 gpointer init_data, gpointer data)
800 WInput *in = (WInput *) init_data;
801 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
802 const char *def_text;
803 size_t buffer_len;
805 (void) event_group_name;
806 (void) event_name;
808 in->history = history_load (ev->cfg, in->history_name);
810 if (in->init_text == NULL)
811 def_text = "";
812 else if (in->init_text == INPUT_LAST_TEXT)
814 if (in->history != NULL && in->history->data != NULL)
815 def_text = (const char *) in->history->data;
816 else
817 def_text = "";
819 in->init_text = NULL;
821 else
822 def_text = in->init_text;
824 buffer_len = strlen (def_text);
825 buffer_len = 1 + max ((size_t) in->field_width, buffer_len);
826 in->current_max_size = buffer_len;
827 if (buffer_len > (size_t) in->field_width)
828 in->buffer = g_realloc (in->buffer, buffer_len);
829 strcpy (in->buffer, def_text);
830 in->point = str_length (in->buffer);
832 return TRUE;
835 /* --------------------------------------------------------------------------------------------- */
837 /* "history_save" event handler */
838 static gboolean
839 input_save_history (const gchar * event_group_name, const gchar * event_name,
840 gpointer init_data, gpointer data)
842 WInput *in = (WInput *) init_data;
844 (void) event_group_name;
845 (void) event_name;
847 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
849 ev_history_load_save_t *ev = (ev_history_load_save_t *) data;
851 push_history (in, in->buffer);
852 if (in->history_changed)
853 history_save (ev->cfg, in->history_name, in->history);
854 in->history_changed = FALSE;
857 return TRUE;
860 /* --------------------------------------------------------------------------------------------- */
862 static void
863 input_destroy (WInput * in)
865 if (in == NULL)
867 fprintf (stderr, "Internal error: null Input *\n");
868 exit (EXIT_FAILURE);
871 input_free_completions (in);
873 /* clean history */
874 if (in->history != NULL)
876 /* history is already saved before this moment */
877 in->history = g_list_first (in->history);
878 g_list_foreach (in->history, (GFunc) g_free, NULL);
879 g_list_free (in->history);
881 g_free (in->history_name);
883 g_free (in->buffer);
884 input_free_completions (in);
885 g_free (in->init_text);
887 g_free (kill_buffer);
888 kill_buffer = NULL;
891 /* --------------------------------------------------------------------------------------------- */
893 static int
894 input_event (Gpm_Event * event, void *data)
896 WInput *in = (WInput *) data;
898 if ((event->type & GPM_DOWN) != 0)
900 in->first = FALSE;
901 input_mark_cmd (in, FALSE);
904 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
906 dlg_select_widget (in);
908 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
909 && should_show_history_button (in))
910 do_show_hist (in);
911 else
913 in->point = str_length (in->buffer);
914 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
915 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
917 input_update (in, TRUE);
919 /* A lone up mustn't do anything */
920 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
921 return MOU_NORMAL;
923 if ((event->type & GPM_DRAG) == 0)
924 input_mark_cmd (in, TRUE);
926 return MOU_NORMAL;
929 /* --------------------------------------------------------------------------------------------- */
930 /*** public functions ****************************************************************************/
931 /* --------------------------------------------------------------------------------------------- */
933 /** Create new instance of WInput object.
934 * @param y Y coordinate
935 * @param x X coordinate
936 * @param input_colors Array of used colors
937 * @param width Widget width
938 * @param def_text Default text filled in widget
939 * @param histname Name of history
940 * @param completion_flags Flags for specify type of completions
941 * @returns WInput object
943 WInput *
944 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
945 const char *histname, input_complete_t completion_flags)
947 WInput *in;
949 in = g_new (WInput, 1);
950 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
951 in->widget.options |= W_IS_INPUT;
953 memmove (in->color, input_colors, sizeof (input_colors_t));
955 in->field_width = width;
956 in->first = TRUE;
957 in->highlight = FALSE;
958 in->term_first_shown = 0;
959 in->disable_update = 0;
960 in->mark = 0;
961 in->need_push = TRUE;
962 in->is_password = FALSE;
963 in->charpoint = 0;
965 /* in->buffer will be corrected in "history_load" event handler */
966 in->current_max_size = width + 1;
967 in->buffer = g_new0 (char, in->current_max_size);
968 in->point = 0;
970 in->init_text = (def_text == INPUT_LAST_TEXT) ? INPUT_LAST_TEXT : g_strdup (def_text);
972 in->completions = NULL;
973 in->completion_flags = completion_flags;
975 /* prepare to history setup */
976 in->history = NULL;
977 in->history_changed = FALSE;
978 in->history_name = NULL;
979 if ((histname != NULL) && (*histname != '\0'))
980 in->history_name = g_strdup (histname);
982 /* history will be loaded later */
984 return in;
987 /* --------------------------------------------------------------------------------------------- */
989 cb_ret_t
990 input_callback (Widget * w, widget_msg_t msg, int parm)
992 WInput *in = (WInput *) w;
993 cb_ret_t v;
995 switch (msg)
997 case WIDGET_INIT:
998 /* subscribe to "history_load" event */
999 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w, NULL);
1000 /* subscribe to "history_save" event */
1001 mc_event_add (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w, NULL);
1002 return MSG_HANDLED;
1004 case WIDGET_KEY:
1005 if (parm == XCTRL ('q'))
1007 quote = 1;
1008 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1009 quote = 0;
1010 return v;
1013 /* Keys we want others to handle */
1014 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1015 || parm == KEY_F (10) || parm == '\n')
1016 return MSG_NOT_HANDLED;
1018 /* When pasting multiline text, insert literal Enter */
1019 if ((parm & ~KEY_M_MASK) == '\n')
1021 quote = 1;
1022 v = input_handle_char (in, '\n');
1023 quote = 0;
1024 return v;
1027 return input_handle_char (in, parm);
1029 case WIDGET_COMMAND:
1030 return input_execute_cmd (in, parm);
1032 case WIDGET_FOCUS:
1033 case WIDGET_UNFOCUS:
1034 case WIDGET_DRAW:
1035 input_update (in, FALSE);
1036 return MSG_HANDLED;
1038 case WIDGET_CURSOR:
1039 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1040 - in->term_first_shown);
1041 return MSG_HANDLED;
1043 case WIDGET_DESTROY:
1044 /* unsubscribe from "history_load" event */
1045 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_LOAD, input_load_history, w);
1046 /* unsubscribe from "history_save" event */
1047 mc_event_del (w->owner->event_group, MCEVENT_HISTORY_SAVE, input_save_history, w);
1048 input_destroy (in);
1049 return MSG_HANDLED;
1051 default:
1052 return default_proc (msg, parm);
1056 /* --------------------------------------------------------------------------------------------- */
1058 /** Get default colors for WInput widget.
1059 * @returns default colors
1061 const int *
1062 input_get_default_colors (void)
1064 static input_colors_t standart_colors;
1066 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1067 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1068 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1069 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1071 return standart_colors;
1074 /* --------------------------------------------------------------------------------------------- */
1076 void
1077 input_set_origin (WInput * in, int x, int field_width)
1079 in->widget.x = x;
1080 in->field_width = in->widget.cols = field_width;
1081 input_update (in, FALSE);
1084 /* --------------------------------------------------------------------------------------------- */
1086 cb_ret_t
1087 input_handle_char (WInput * in, int key)
1089 cb_ret_t v;
1090 unsigned long command;
1092 v = MSG_NOT_HANDLED;
1094 if (quote != 0)
1096 input_free_completions (in);
1097 v = insert_char (in, key);
1098 input_update (in, TRUE);
1099 quote = 0;
1100 return v;
1103 command = keybind_lookup_keymap_command (input_map, key);
1105 if (command == CK_IgnoreKey)
1107 if (key > 255)
1108 return MSG_NOT_HANDLED;
1109 if (in->first)
1110 port_region_marked_for_delete (in);
1111 input_free_completions (in);
1112 v = insert_char (in, key);
1114 else
1116 if (command != CK_Complete)
1117 input_free_completions (in);
1118 input_execute_cmd (in, command);
1119 v = MSG_HANDLED;
1120 if (in->first)
1121 input_update (in, TRUE); /* needed to clear in->first */
1124 input_update (in, TRUE);
1125 return v;
1128 /* --------------------------------------------------------------------------------------------- */
1130 /* This function is a test for a special input key used in complete.c */
1131 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1132 and 2 if it is a complete key */
1134 input_key_is_in_map (WInput * in, int key)
1136 unsigned long command;
1138 (void) in;
1140 command = keybind_lookup_keymap_command (input_map, key);
1141 if (command == CK_IgnoreKey)
1142 return 0;
1144 return (command == CK_Complete) ? 2 : 1;
1147 /* --------------------------------------------------------------------------------------------- */
1149 void
1150 input_assign_text (WInput * in, const char *text)
1152 input_free_completions (in);
1153 g_free (in->buffer);
1154 in->buffer = g_strdup (text); /* was in->buffer->text */
1155 in->current_max_size = strlen (in->buffer) + 1;
1156 in->point = str_length (in->buffer);
1157 in->mark = 0;
1158 in->need_push = TRUE;
1159 in->charpoint = 0;
1162 /* --------------------------------------------------------------------------------------------- */
1164 /* Inserts text in input line */
1165 void
1166 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1168 input_disable_update (in);
1169 while (*text != '\0')
1170 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1171 if (insert_extra_space)
1172 input_handle_char (in, ' ');
1173 input_enable_update (in);
1174 input_update (in, TRUE);
1177 /* --------------------------------------------------------------------------------------------- */
1179 void
1180 input_set_point (WInput * in, int pos)
1182 int max_pos;
1184 max_pos = str_length (in->buffer);
1185 pos = min (pos, max_pos);
1186 if (pos != in->point)
1187 input_free_completions (in);
1188 in->point = pos;
1189 in->charpoint = 0;
1190 input_update (in, TRUE);
1193 /* --------------------------------------------------------------------------------------------- */
1195 void
1196 input_update (WInput * in, gboolean clear_first)
1198 int has_history = 0;
1199 int i;
1200 int buf_len;
1201 const char *cp;
1202 int pw;
1204 if (should_show_history_button (in))
1205 has_history = HISTORY_BUTTON_WIDTH;
1207 if (in->disable_update != 0)
1208 return;
1210 buf_len = str_length (in->buffer);
1211 pw = str_term_width2 (in->buffer, in->point);
1213 /* Make the point visible */
1214 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1216 in->term_first_shown = pw - (in->field_width / 3);
1217 if (in->term_first_shown < 0)
1218 in->term_first_shown = 0;
1221 /* Adjust the mark */
1222 in->mark = min (in->mark, buf_len);
1224 if (has_history != 0)
1225 draw_history_button (in);
1227 if ((((Widget *) in)->options & W_DISABLED) != 0)
1228 tty_setcolor (DISABLED_COLOR);
1229 else if (in->first)
1230 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1231 else
1232 tty_setcolor (in->color[WINPUTC_MAIN]);
1234 widget_move (&in->widget, 0, 0);
1236 if (!in->is_password)
1238 if (!in->highlight)
1239 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1240 in->field_width - has_history));
1241 else
1243 long m1, m2;
1245 if (input_eval_marks (in, &m1, &m2))
1247 tty_setcolor (in->color[WINPUTC_MAIN]);
1248 cp = str_term_substring (in->buffer, in->term_first_shown,
1249 in->field_width - has_history);
1250 tty_print_string (cp);
1251 tty_setcolor (in->color[WINPUTC_MARK]);
1252 if (m1 < in->term_first_shown)
1254 widget_move (&in->widget, 0, 0);
1255 tty_print_string (str_term_substring
1256 (in->buffer, in->term_first_shown,
1257 m2 - in->term_first_shown));
1259 else
1261 int sel_width;
1263 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1264 sel_width =
1265 min (m2 - m1,
1266 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1267 in->term_first_shown));
1268 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1273 else
1275 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1276 for (i = 0; i < in->field_width - has_history; i++)
1278 if (i >= 0)
1280 tty_setcolor (in->color[WINPUTC_MAIN]);
1281 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1283 if (cp[0] != '\0')
1284 str_cnext_char (&cp);
1288 if (clear_first)
1289 in->first = FALSE;
1292 /* --------------------------------------------------------------------------------------------- */
1294 void
1295 input_enable_update (WInput * in)
1297 in->disable_update--;
1298 input_update (in, FALSE);
1301 /* --------------------------------------------------------------------------------------------- */
1303 void
1304 input_disable_update (WInput * in)
1306 in->disable_update++;
1309 /* --------------------------------------------------------------------------------------------- */
1312 * Cleans the input line and adds the current text to the history
1314 * @param in the input line
1316 void
1317 input_clean (WInput * in)
1319 push_history (in, in->buffer);
1320 in->need_push = TRUE;
1321 in->buffer[0] = '\0';
1322 in->point = 0;
1323 in->charpoint = 0;
1324 in->mark = 0;
1325 in->highlight = FALSE;
1326 input_free_completions (in);
1327 input_update (in, FALSE);
1330 /* --------------------------------------------------------------------------------------------- */
1332 void
1333 input_free_completions (WInput * in)
1335 g_strfreev (in->completions);
1336 in->completions = NULL;
1339 /* --------------------------------------------------------------------------------------------- */