(panel_save_curent_file_to_clip_file): 'res' might be used uninitialized here.
[midnight-commander.git] / lib / widget / input.c
blob5adf0701298e22d0881cef031b7b70fa897d9bbc
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/vfs/mc-vfs/vfs.h"
46 #include "lib/fileloc.h"
47 #include "lib/skin.h"
48 #include "lib/strutil.h"
49 #include "lib/util.h"
50 #include "lib/keybind.h" /* global_keymap_t */
51 #include "lib/widget.h"
53 #include "src/main.h" /* home_dir */
54 #include "src/filemanager/midnight.h" /* current_panel */
55 #include "src/clipboard.h" /* copy_file_to_ext_clip, paste_to_file_from_ext_clip */
56 #include "src/keybind-defaults.h" /* input_map */
58 /*** global variables ****************************************************************************/
60 int quote = 0;
62 /*** file scope macro definitions ****************************************************************/
64 #define LARGE_HISTORY_BUTTON 1
66 #ifdef LARGE_HISTORY_BUTTON
67 #define HISTORY_BUTTON_WIDTH 3
68 #else
69 #define HISTORY_BUTTON_WIDTH 1
70 #endif
72 #define should_show_history_button(in) \
73 (in->history != NULL && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 \
74 && in->widget.owner != NULL)
76 /*** file scope type declarations ****************************************************************/
78 /*** file scope variables ************************************************************************/
80 /* Input widgets have a global kill ring */
81 /* Pointer to killed data */
82 static char *kill_buffer = NULL;
84 /*** file scope functions ************************************************************************/
86 static gboolean
87 save_text_to_clip_file (const char *text)
89 int file;
90 char *fname = NULL;
91 ssize_t ret;
92 size_t str_len;
94 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
95 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
96 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
97 g_free (fname);
99 if (file == -1)
100 return FALSE;
102 str_len = strlen (text);
103 ret = mc_write (file, (char *) text, str_len);
104 mc_close (file);
105 return ret == (ssize_t) str_len;
108 /* --------------------------------------------------------------------------------------------- */
110 static gboolean
111 load_text_from_clip_file (char **text)
113 char buf[BUF_LARGE];
114 FILE *f;
115 char *fname = NULL;
116 gboolean first = TRUE;
118 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
119 f = fopen (fname, "r");
120 g_free (fname);
122 if (f == NULL)
123 return FALSE;
125 *text = NULL;
127 while (fgets (buf, sizeof (buf), f))
129 size_t len;
131 len = strlen (buf);
132 if (len > 0)
134 if (buf[len - 1] == '\n')
135 buf[len - 1] = '\0';
137 if (first)
139 first = FALSE;
140 *text = g_strdup (buf);
142 else
144 /* remove \n on EOL */
145 char *tmp;
147 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
148 g_free (*text);
149 *text = tmp;
154 fclose (f);
156 return (*text != NULL);
159 /* --------------------------------------------------------------------------------------------- */
161 static gboolean
162 panel_save_curent_file_to_clip_file (void)
164 gboolean res = FALSE;
166 if (current_panel->marked == 0)
167 res = save_text_to_clip_file (selection (current_panel)->fname);
168 else
170 int i;
171 gboolean first = TRUE;
172 char *flist = NULL;
174 for (i = 0; i < current_panel->count; i++)
175 if (current_panel->dir.list[i].f.marked != 0)
176 { /* Skip the unmarked ones */
177 if (first)
179 flist = g_strdup (current_panel->dir.list[i].fname);
180 first = FALSE;
182 else
184 /* Add empty lines after the file */
185 char *tmp;
187 tmp =
188 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
189 g_free (flist);
190 flist = tmp;
194 if (flist != NULL)
196 res = save_text_to_clip_file (flist);
197 g_free (flist);
200 return res;
203 /* --------------------------------------------------------------------------------------------- */
205 static void
206 draw_history_button (WInput * in)
208 char c;
209 gboolean disabled = (((Widget *) in)->options & W_DISABLED) != 0;
211 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
212 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
213 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
214 #ifdef LARGE_HISTORY_BUTTON
216 Dlg_head *h;
217 h = in->widget.owner;
218 tty_print_string ("[ ]");
219 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
221 #endif
222 tty_print_char (c);
225 /* --------------------------------------------------------------------------------------------- */
227 static void
228 input_set_markers (WInput * in, long m1)
230 in->mark = m1;
233 /* --------------------------------------------------------------------------------------------- */
235 static void
236 input_mark_cmd (WInput * in, gboolean mark)
238 if (mark == 0)
240 in->highlight = FALSE;
241 input_set_markers (in, 0);
243 else
245 in->highlight = TRUE;
246 input_set_markers (in, in->point);
250 /* --------------------------------------------------------------------------------------------- */
252 static gboolean
253 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
255 if (in->highlight)
257 *start_mark = min (in->mark, in->point);
258 *end_mark = max (in->mark, in->point);
259 return TRUE;
261 else
263 *start_mark = *end_mark = 0;
264 return FALSE;
268 /* --------------------------------------------------------------------------------------------- */
270 static void
271 delete_region (WInput * in, int x_first, int x_last)
273 int first = min (x_first, x_last);
274 int last = max (x_first, x_last);
275 size_t len;
277 input_mark_cmd (in, FALSE);
278 in->point = first;
279 last = str_offset_to_pos (in->buffer, last);
280 first = str_offset_to_pos (in->buffer, first);
281 len = strlen (&in->buffer[last]) + 1;
282 memmove (&in->buffer[first], &in->buffer[last], len);
283 in->charpoint = 0;
284 in->need_push = TRUE;
287 /* --------------------------------------------------------------------------------------------- */
289 static void
290 do_show_hist (WInput * in)
292 char *r;
294 r = history_show (&in->history, &in->widget);
295 if (r != NULL)
297 input_assign_text (in, r);
298 g_free (r);
302 /* --------------------------------------------------------------------------------------------- */
304 static void
305 push_history (WInput * in, const char *text)
307 /* input widget where urls with passwords are entered without any
308 vfs prefix */
309 const char *password_input_fields[] = {
310 " Link to a remote machine ",
311 " FTP to machine ",
312 " SMB link to machine "
314 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
316 char *t;
317 size_t i;
318 gboolean empty;
320 if (text == NULL)
321 return;
323 #ifdef ENABLE_NLS
324 for (i = 0; i < ELEMENTS; i++)
325 password_input_fields[i] = _(password_input_fields[i]);
326 #endif
328 t = g_strstrip (g_strdup (text));
329 empty = *t == '\0';
330 g_free (t);
331 t = g_strdup (empty ? "" : text);
333 if (in->history_name != NULL)
335 /* FIXME: It is the strange code. Rewrite is needed. */
337 const char *p = in->history_name + 3;
339 for (i = 0; i < ELEMENTS; i++)
340 if (strcmp (p, password_input_fields[i]) == 0)
341 break;
343 strip_password (t, i >= ELEMENTS);
346 in->history = list_append_unique (in->history, t);
347 in->need_push = FALSE;
350 /* --------------------------------------------------------------------------------------------- */
352 static void
353 move_buffer_backward (WInput * in, int start, int end)
355 int i, pos, len;
356 int str_len;
358 str_len = str_length (in->buffer);
359 if (start >= str_len || end > str_len + 1)
360 return;
362 pos = str_offset_to_pos (in->buffer, start);
363 len = str_offset_to_pos (in->buffer, end) - pos;
365 for (i = pos; in->buffer[i + len - 1]; i++)
366 in->buffer[i] = in->buffer[i + len];
369 /* --------------------------------------------------------------------------------------------- */
371 static cb_ret_t
372 insert_char (WInput * in, int c_code)
374 size_t i;
375 int res;
377 if (in->highlight)
379 long m1, m2;
380 if (input_eval_marks (in, &m1, &m2))
381 delete_region (in, m1, m2);
383 if (c_code == -1)
384 return MSG_NOT_HANDLED;
386 if (in->charpoint >= MB_LEN_MAX)
387 return MSG_HANDLED;
389 in->charbuf[in->charpoint] = c_code;
390 in->charpoint++;
392 res = str_is_valid_char (in->charbuf, in->charpoint);
393 if (res < 0)
395 if (res != -2)
396 in->charpoint = 0; /* broken multibyte char, skip */
397 return MSG_HANDLED;
400 in->need_push = TRUE;
401 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
403 /* Expand the buffer */
404 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
405 char *narea = g_try_renew (char, in->buffer, new_length);
406 if (narea)
408 in->buffer = narea;
409 in->current_max_size = new_length;
413 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
415 /* bytes from begin */
416 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
417 /* move chars */
418 size_t rest_bytes = strlen (in->buffer + ins_point);
420 for (i = rest_bytes + 1; i > 0; i--)
421 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
423 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
424 in->point++;
427 in->charpoint = 0;
428 return MSG_HANDLED;
431 /* --------------------------------------------------------------------------------------------- */
433 static void
434 beginning_of_line (WInput * in)
436 in->point = 0;
437 in->charpoint = 0;
440 /* --------------------------------------------------------------------------------------------- */
442 static void
443 end_of_line (WInput * in)
445 in->point = str_length (in->buffer);
446 in->charpoint = 0;
449 /* --------------------------------------------------------------------------------------------- */
451 static void
452 backward_char (WInput * in)
454 const char *act;
456 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
457 if (in->point > 0)
458 in->point -= str_cprev_noncomb_char (&act, in->buffer);
459 in->charpoint = 0;
462 /* --------------------------------------------------------------------------------------------- */
464 static void
465 forward_char (WInput * in)
467 const char *act;
469 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
470 if (act[0] != '\0')
471 in->point += str_cnext_noncomb_char (&act);
472 in->charpoint = 0;
475 /* --------------------------------------------------------------------------------------------- */
477 static void
478 forward_word (WInput * in)
480 const char *p;
482 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
483 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
485 str_cnext_char (&p);
486 in->point++;
488 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
490 str_cnext_char (&p);
491 in->point++;
495 /* --------------------------------------------------------------------------------------------- */
497 static void
498 backward_word (WInput * in)
500 const char *p, *p_tmp;
502 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
503 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
505 while (p != in->buffer)
507 p_tmp = p;
508 str_cprev_char (&p);
509 if (!str_isspace (p) && !str_ispunct (p))
511 p = p_tmp;
512 break;
514 in->point--;
516 while (p != in->buffer)
518 str_cprev_char (&p);
519 if (str_isspace (p) || str_ispunct (p))
520 break;
522 in->point--;
526 /* --------------------------------------------------------------------------------------------- */
528 static void
529 backward_delete (WInput * in)
531 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
532 int start;
534 if (in->point == 0)
535 return;
537 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
538 move_buffer_backward (in, start, in->point);
539 in->charpoint = 0;
540 in->need_push = TRUE;
541 in->point = start;
544 /* --------------------------------------------------------------------------------------------- */
546 static void
547 delete_char (WInput * in)
549 const char *act;
550 int end = in->point;
552 act = in->buffer + str_offset_to_pos (in->buffer, in->point);
553 end += str_cnext_noncomb_char (&act);
555 move_buffer_backward (in, in->point, end);
556 in->charpoint = 0;
557 in->need_push = TRUE;
560 /* --------------------------------------------------------------------------------------------- */
562 static void
563 copy_region (WInput * in, int x_first, int x_last)
565 int first = min (x_first, x_last);
566 int last = max (x_first, x_last);
568 if (last == first)
570 /* Copy selected files to clipboard */
571 panel_save_curent_file_to_clip_file ();
572 /* try use external clipboard utility */
573 copy_file_to_ext_clip ();
574 return;
577 g_free (kill_buffer);
579 first = str_offset_to_pos (in->buffer, first);
580 last = str_offset_to_pos (in->buffer, last);
582 kill_buffer = g_strndup (in->buffer + first, last - first);
584 save_text_to_clip_file (kill_buffer);
585 /* try use external clipboard utility */
586 copy_file_to_ext_clip ();
589 /* --------------------------------------------------------------------------------------------- */
591 static void
592 kill_word (WInput * in)
594 int old_point = in->point;
595 int new_point;
597 forward_word (in);
598 new_point = in->point;
599 in->point = old_point;
601 delete_region (in, old_point, new_point);
602 in->need_push = TRUE;
603 in->charpoint = 0;
606 /* --------------------------------------------------------------------------------------------- */
608 static void
609 back_kill_word (WInput * in)
611 int old_point = in->point;
612 int new_point;
614 backward_word (in);
615 new_point = in->point;
616 in->point = old_point;
618 delete_region (in, old_point, new_point);
619 in->need_push = TRUE;
622 /* --------------------------------------------------------------------------------------------- */
624 static void
625 yank (WInput * in)
627 if (kill_buffer != NULL)
629 char *p;
631 in->charpoint = 0;
632 for (p = kill_buffer; *p != '\0'; p++)
633 insert_char (in, *p);
634 in->charpoint = 0;
638 /* --------------------------------------------------------------------------------------------- */
640 static void
641 kill_line (WInput * in)
643 int chp;
645 chp = str_offset_to_pos (in->buffer, in->point);
646 g_free (kill_buffer);
647 kill_buffer = g_strdup (&in->buffer[chp]);
648 in->buffer[chp] = '\0';
649 in->charpoint = 0;
652 /* --------------------------------------------------------------------------------------------- */
654 static void
655 clear_line (WInput * in)
657 in->need_push = 1;
658 in->buffer[0] = '\0';
659 in->point = 0;
660 in->mark = 0;
661 in->highlight = FALSE;
662 in->charpoint = 0;
665 static void
666 ins_from_clip (WInput * in)
668 char *p = NULL;
670 /* try use external clipboard utility */
671 paste_to_file_from_ext_clip ();
673 if (load_text_from_clip_file (&p))
675 char *pp;
677 for (pp = p; *pp != '\0'; pp++)
678 insert_char (in, *pp);
680 g_free (p);
684 /* --------------------------------------------------------------------------------------------- */
686 static void
687 hist_prev (WInput * in)
689 GList *prev;
691 if (in->history == NULL)
692 return;
694 if (in->need_push)
695 push_history (in, in->buffer);
697 prev = g_list_previous (in->history);
698 if (prev != NULL)
700 in->history = prev;
701 input_assign_text (in, (char *) prev->data);
702 in->need_push = FALSE;
706 /* --------------------------------------------------------------------------------------------- */
708 static void
709 hist_next (WInput * in)
711 if (in->need_push)
713 push_history (in, in->buffer);
714 input_assign_text (in, "");
715 return;
718 if (in->history == NULL)
719 return;
721 if (in->history->next == NULL)
722 input_assign_text (in, "");
723 else
725 in->history = g_list_next (in->history);
726 input_assign_text (in, (char *) in->history->data);
727 in->need_push = FALSE;
731 /* --------------------------------------------------------------------------------------------- */
733 static void
734 port_region_marked_for_delete (WInput * in)
736 in->buffer[0] = '\0';
737 in->point = 0;
738 in->first = FALSE;
739 in->charpoint = 0;
742 /* --------------------------------------------------------------------------------------------- */
744 static cb_ret_t
745 input_execute_cmd (WInput * in, unsigned long command)
747 cb_ret_t res = MSG_HANDLED;
749 /* a highlight command like shift-arrow */
750 if (command == CK_InputLeftHighlight ||
751 command == CK_InputRightHighlight ||
752 command == CK_InputWordLeftHighlight ||
753 command == CK_InputWordRightHighlight ||
754 command == CK_InputBolHighlight || command == CK_InputEolHighlight)
756 if (!in->highlight)
758 input_mark_cmd (in, FALSE); /* clear */
759 input_mark_cmd (in, TRUE); /* marking on */
763 switch (command)
765 case CK_InputForwardWord:
766 case CK_InputBackwardWord:
767 case CK_InputForwardChar:
768 case CK_InputBackwardChar:
769 if (in->highlight)
770 input_mark_cmd (in, FALSE);
773 switch (command)
775 case CK_InputBol:
776 case CK_InputBolHighlight:
777 beginning_of_line (in);
778 break;
779 case CK_InputEol:
780 case CK_InputEolHighlight:
781 end_of_line (in);
782 break;
783 case CK_InputMoveLeft:
784 case CK_InputLeftHighlight:
785 backward_char (in);
786 break;
787 case CK_InputWordLeft:
788 case CK_InputWordLeftHighlight:
789 backward_word (in);
790 break;
791 case CK_InputMoveRight:
792 case CK_InputRightHighlight:
793 forward_char (in);
794 break;
795 case CK_InputWordRight:
796 case CK_InputWordRightHighlight:
797 forward_word (in);
798 break;
799 case CK_InputBackwardChar:
800 backward_char (in);
801 break;
802 case CK_InputBackwardWord:
803 backward_word (in);
804 break;
805 case CK_InputForwardChar:
806 forward_char (in);
807 break;
808 case CK_InputForwardWord:
809 forward_word (in);
810 break;
811 case CK_InputBackwardDelete:
812 if (in->highlight)
814 long m1, m2;
815 if (input_eval_marks (in, &m1, &m2))
816 delete_region (in, m1, m2);
818 else
819 backward_delete (in);
820 break;
821 case CK_InputDeleteChar:
822 if (in->first)
823 port_region_marked_for_delete (in);
824 else if (in->highlight)
826 long m1, m2;
827 if (input_eval_marks (in, &m1, &m2))
828 delete_region (in, m1, m2);
830 else
831 delete_char (in);
832 break;
833 case CK_InputKillWord:
834 kill_word (in);
835 break;
836 case CK_InputBackwardKillWord:
837 back_kill_word (in);
838 break;
839 case CK_InputSetMark:
840 input_mark_cmd (in, TRUE);
841 break;
842 case CK_InputKillRegion:
843 delete_region (in, in->point, in->mark);
844 break;
845 case CK_InputKillLine:
846 /* clear command line from cursor to the EOL */
847 kill_line (in);
848 break;
849 case CK_InputClearLine:
850 /* clear command line */
851 clear_line (in);
852 break;
853 case CK_InputCopyRegion:
854 copy_region (in, in->mark, in->point);
855 break;
856 case CK_InputKillSave:
857 copy_region (in, in->mark, in->point);
858 delete_region (in, in->point, in->mark);
859 break;
860 case CK_InputYank:
861 yank (in);
862 break;
863 case CK_InputPaste:
864 ins_from_clip (in);
865 break;
866 case CK_InputHistoryPrev:
867 hist_prev (in);
868 break;
869 case CK_InputHistoryNext:
870 hist_next (in);
871 break;
872 case CK_InputHistoryShow:
873 do_show_hist (in);
874 break;
875 case CK_InputComplete:
876 complete (in);
877 break;
878 default:
879 res = MSG_NOT_HANDLED;
882 if (command != CK_InputLeftHighlight &&
883 command != CK_InputRightHighlight &&
884 command != CK_InputWordLeftHighlight &&
885 command != CK_InputWordRightHighlight &&
886 command != CK_InputBolHighlight && command != CK_InputEolHighlight)
887 in->highlight = FALSE;
889 return res;
892 /* --------------------------------------------------------------------------------------------- */
894 static void
895 input_destroy (WInput * in)
897 if (in == NULL)
899 fprintf (stderr, "Internal error: null Input *\n");
900 exit (EXIT_FAILURE);
903 input_clean (in);
905 if (in->history != NULL)
907 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
908 history_put (in->history_name, in->history);
910 in->history = g_list_first (in->history);
911 g_list_foreach (in->history, (GFunc) g_free, NULL);
912 g_list_free (in->history);
915 g_free (in->buffer);
916 input_free_completions (in);
917 g_free (in->history_name);
919 g_free (kill_buffer);
920 kill_buffer = NULL;
923 /* --------------------------------------------------------------------------------------------- */
925 static int
926 input_event (Gpm_Event * event, void *data)
928 WInput *in = (WInput *) data;
930 if ((event->type & GPM_DOWN) != 0)
932 in->first = FALSE;
933 input_mark_cmd (in, FALSE);
936 if ((event->type & (GPM_DOWN | GPM_DRAG)) != 0)
938 dlg_select_widget (in);
940 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
941 && should_show_history_button (in))
942 do_show_hist (in);
943 else
945 in->point = str_length (in->buffer);
946 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
947 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
949 input_update (in, TRUE);
951 /* A lone up mustn't do anything */
952 if (in->highlight && (event->type & (GPM_UP | GPM_DRAG)) != 0)
953 return MOU_NORMAL;
955 if ((event->type & GPM_DRAG) == 0)
956 input_mark_cmd (in, TRUE);
958 return MOU_NORMAL;
961 /* --------------------------------------------------------------------------------------------- */
962 /*** public functions ****************************************************************************/
963 /* --------------------------------------------------------------------------------------------- */
965 /** Create new instance of WInput object.
966 * @param y Y coordinate
967 * @param x X coordinate
968 * @param input_colors Array of used colors
969 * @param width Widget width
970 * @param def_text Default text filled in widget
971 * @param histname Name of history
972 * @param completion_flags Flags for specify type of completions
973 * @returns WInput object
975 WInput *
976 input_new (int y, int x, const int *input_colors, int width, const char *def_text,
977 const char *histname, input_complete_t completion_flags)
979 WInput *in = g_new (WInput, 1);
980 size_t initial_buffer_len;
982 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
984 /* history setup */
985 in->history_name = NULL;
986 in->history = NULL;
987 if ((histname != NULL) && (*histname != '\0'))
989 in->history_name = g_strdup (histname);
990 in->history = history_get (histname);
993 if (def_text == NULL)
994 def_text = "";
995 else if (def_text == INPUT_LAST_TEXT)
997 if ((in->history != NULL) && (in->history->data != NULL))
998 def_text = (char *) in->history->data;
999 else
1000 def_text = "";
1003 initial_buffer_len = strlen (def_text);
1004 initial_buffer_len = 1 + max ((size_t) width, initial_buffer_len);
1005 in->widget.options |= W_IS_INPUT;
1006 in->completions = NULL;
1007 in->completion_flags = completion_flags;
1008 in->current_max_size = initial_buffer_len;
1009 in->buffer = g_new (char, initial_buffer_len);
1011 memmove (in->color, input_colors, sizeof (input_colors_t));
1013 in->field_width = width;
1014 in->first = TRUE;
1015 in->highlight = FALSE;
1016 in->term_first_shown = 0;
1017 in->disable_update = 0;
1018 in->mark = 0;
1019 in->need_push = TRUE;
1020 in->is_password = FALSE;
1022 strcpy (in->buffer, def_text);
1023 in->point = str_length (in->buffer);
1024 in->charpoint = 0;
1026 return in;
1029 /* --------------------------------------------------------------------------------------------- */
1031 cb_ret_t
1032 input_callback (Widget * w, widget_msg_t msg, int parm)
1034 WInput *in = (WInput *) w;
1035 cb_ret_t v;
1037 switch (msg)
1039 case WIDGET_KEY:
1040 if (parm == XCTRL ('q'))
1042 quote = 1;
1043 v = input_handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1044 quote = 0;
1045 return v;
1048 /* Keys we want others to handle */
1049 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1050 || parm == KEY_F (10) || parm == '\n')
1051 return MSG_NOT_HANDLED;
1053 /* When pasting multiline text, insert literal Enter */
1054 if ((parm & ~KEY_M_MASK) == '\n')
1056 quote = 1;
1057 v = input_handle_char (in, '\n');
1058 quote = 0;
1059 return v;
1062 return input_handle_char (in, parm);
1064 case WIDGET_COMMAND:
1065 return input_execute_cmd (in, parm);
1067 case WIDGET_FOCUS:
1068 case WIDGET_UNFOCUS:
1069 case WIDGET_DRAW:
1070 input_update (in, FALSE);
1071 return MSG_HANDLED;
1073 case WIDGET_CURSOR:
1074 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1075 - in->term_first_shown);
1076 return MSG_HANDLED;
1078 case WIDGET_DESTROY:
1079 input_destroy (in);
1080 return MSG_HANDLED;
1082 default:
1083 return default_proc (msg, parm);
1087 /* --------------------------------------------------------------------------------------------- */
1089 /** Get default colors for WInput widget.
1090 * @returns default colors
1092 const int *
1093 input_get_default_colors (void)
1095 static input_colors_t standart_colors;
1097 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
1098 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
1099 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
1100 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
1102 return standart_colors;
1105 /* --------------------------------------------------------------------------------------------- */
1107 void
1108 input_set_origin (WInput * in, int x, int field_width)
1110 in->widget.x = x;
1111 in->field_width = in->widget.cols = field_width;
1112 input_update (in, FALSE);
1115 /* --------------------------------------------------------------------------------------------- */
1117 cb_ret_t
1118 input_handle_char (WInput * in, int key)
1120 cb_ret_t v;
1121 unsigned long command;
1123 v = MSG_NOT_HANDLED;
1125 if (quote != 0)
1127 input_free_completions (in);
1128 v = insert_char (in, key);
1129 input_update (in, TRUE);
1130 quote = 0;
1131 return v;
1134 command = keybind_lookup_keymap_command (input_map, key);
1136 if (command == CK_Ignore_Key)
1138 if (key > 255)
1139 return MSG_NOT_HANDLED;
1140 if (in->first)
1141 port_region_marked_for_delete (in);
1142 input_free_completions (in);
1143 v = insert_char (in, key);
1145 else
1147 if (command != CK_InputComplete)
1148 input_free_completions (in);
1149 input_execute_cmd (in, command);
1150 v = MSG_HANDLED;
1151 if (in->first)
1152 input_update (in, TRUE); /* needed to clear in->first */
1155 input_update (in, TRUE);
1156 return v;
1159 /* --------------------------------------------------------------------------------------------- */
1161 /* This function is a test for a special input key used in complete.c */
1162 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1163 and 2 if it is a complete key */
1165 input_key_is_in_map (WInput * in, int key)
1167 unsigned long command;
1169 (void) in;
1171 command = keybind_lookup_keymap_command (input_map, key);
1172 if (command == CK_Ignore_Key)
1173 return 0;
1175 return (command == CK_InputComplete) ? 2 : 1;
1178 /* --------------------------------------------------------------------------------------------- */
1180 void
1181 input_assign_text (WInput * in, const char *text)
1183 input_free_completions (in);
1184 g_free (in->buffer);
1185 in->buffer = g_strdup (text); /* was in->buffer->text */
1186 in->current_max_size = strlen (in->buffer) + 1;
1187 in->point = str_length (in->buffer);
1188 in->mark = 0;
1189 in->need_push = TRUE;
1190 in->charpoint = 0;
1193 /* --------------------------------------------------------------------------------------------- */
1195 /* Inserts text in input line */
1196 void
1197 input_insert (WInput * in, const char *text, gboolean insert_extra_space)
1199 input_disable_update (in);
1200 while (*text != '\0')
1201 input_handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1202 if (insert_extra_space)
1203 input_handle_char (in, ' ');
1204 input_enable_update (in);
1205 input_update (in, TRUE);
1208 /* --------------------------------------------------------------------------------------------- */
1210 void
1211 input_set_point (WInput * in, int pos)
1213 int max_pos;
1215 max_pos = str_length (in->buffer);
1216 pos = min (pos, max_pos);
1217 if (pos != in->point)
1218 input_free_completions (in);
1219 in->point = pos;
1220 in->charpoint = 0;
1221 input_update (in, TRUE);
1224 /* --------------------------------------------------------------------------------------------- */
1226 void
1227 input_update (WInput * in, gboolean clear_first)
1229 int has_history = 0;
1230 int i;
1231 int buf_len;
1232 const char *cp;
1233 int pw;
1235 if (should_show_history_button (in))
1236 has_history = HISTORY_BUTTON_WIDTH;
1238 if (in->disable_update != 0)
1239 return;
1241 buf_len = str_length (in->buffer);
1242 pw = str_term_width2 (in->buffer, in->point);
1244 /* Make the point visible */
1245 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1247 in->term_first_shown = pw - (in->field_width / 3);
1248 if (in->term_first_shown < 0)
1249 in->term_first_shown = 0;
1252 /* Adjust the mark */
1253 in->mark = min (in->mark, buf_len);
1255 if (has_history != 0)
1256 draw_history_button (in);
1258 if ((((Widget *) in)->options & W_DISABLED) != 0)
1259 tty_setcolor (DISABLED_COLOR);
1260 else if (in->first)
1261 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1262 else
1263 tty_setcolor (in->color[WINPUTC_MAIN]);
1265 widget_move (&in->widget, 0, 0);
1267 if (!in->is_password)
1269 if (!in->highlight)
1270 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1271 in->field_width - has_history));
1272 else
1274 long m1, m2;
1276 if (input_eval_marks (in, &m1, &m2))
1278 tty_setcolor (in->color[WINPUTC_MAIN]);
1279 cp = str_term_substring (in->buffer, in->term_first_shown,
1280 in->field_width - has_history);
1281 tty_print_string (cp);
1282 tty_setcolor (in->color[WINPUTC_MARK]);
1283 if (m1 < in->term_first_shown)
1285 widget_move (&in->widget, 0, 0);
1286 tty_print_string (str_term_substring
1287 (in->buffer, in->term_first_shown,
1288 m2 - in->term_first_shown));
1290 else
1292 int sel_width;
1294 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1295 sel_width =
1296 min (m2 - m1,
1297 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1298 in->term_first_shown));
1299 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1304 else
1306 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1307 for (i = 0; i < in->field_width - has_history; i++)
1309 if (i >= 0)
1311 tty_setcolor (in->color[WINPUTC_MAIN]);
1312 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1314 if (cp[0] != '\0')
1315 str_cnext_char (&cp);
1319 if (clear_first)
1320 in->first = FALSE;
1323 /* --------------------------------------------------------------------------------------------- */
1325 void
1326 input_enable_update (WInput * in)
1328 in->disable_update--;
1329 input_update (in, FALSE);
1332 /* --------------------------------------------------------------------------------------------- */
1334 void
1335 input_disable_update (WInput * in)
1337 in->disable_update++;
1340 /* --------------------------------------------------------------------------------------------- */
1342 /* Cleans the input line and adds the current text to the history */
1343 void
1344 input_clean (WInput * in)
1346 push_history (in, in->buffer);
1347 in->need_push = TRUE;
1348 in->buffer[0] = '\0';
1349 in->point = 0;
1350 in->charpoint = 0;
1351 in->mark = 0;
1352 in->highlight = FALSE;
1353 input_free_completions (in);
1354 input_update (in, FALSE);
1357 /* --------------------------------------------------------------------------------------------- */
1359 void
1360 input_free_completions (WInput * in)
1362 g_strfreev (in->completions);
1363 in->completions = NULL;
1366 /* --------------------------------------------------------------------------------------------- */