1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
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.
30 * \brief Source: widgets
42 #include <sys/types.h>
44 #include "lib/global.h"
46 #include "lib/tty/tty.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
50 #include "lib/mcconfig.h" /* for history loading and saving */
51 #include "lib/vfs/mc-vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
59 #include "cmddef.h" /* CK_ cmd name const */
60 #include "keybind.h" /* global_keymap_t */
61 #include "panel.h" /* current_panel */
62 #include "main.h" /* confirm_history_cleanup */
63 #include "setup.h" /* num_history_items_recorded */
64 #include "clipboard.h" /* copy_file_to_ext_clip, paste_to_file_from_ext_clip */
67 widget_selectcolor (Widget
* w
, gboolean focused
, gboolean hotkey
)
69 Dlg_head
*h
= w
->owner
;
74 : DLG_HOT_NORMALC (h
)) : (focused
? DLG_FOCUSC (h
) : DLG_NORMALC (h
)));
78 parse_hotkey (const char *text
)
80 struct hotkey_t result
;
83 /* search for '&', that is not on the of text */
84 cp
= strchr (text
, '&');
85 if (cp
!= NULL
&& cp
[1] != '\0')
87 result
.start
= g_strndup (text
, cp
- text
);
91 p
= str_cget_next_char (cp
);
92 result
.hotkey
= g_strndup (cp
, p
- cp
);
95 result
.end
= g_strdup (cp
);
99 result
.start
= g_strdup (text
);
100 result
.hotkey
= NULL
;
108 release_hotkey (const struct hotkey_t hotkey
)
110 g_free (hotkey
.start
);
111 g_free (hotkey
.hotkey
);
116 hotkey_width (const struct hotkey_t hotkey
)
120 result
= str_term_width1 (hotkey
.start
);
121 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
122 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
127 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
129 widget_selectcolor (w
, focused
, FALSE
);
130 tty_print_string (hotkey
.start
);
132 if (hotkey
.hotkey
!= NULL
)
134 widget_selectcolor (w
, focused
, TRUE
);
135 tty_print_string (hotkey
.hotkey
);
136 widget_selectcolor (w
, focused
, FALSE
);
139 if (hotkey
.end
!= NULL
)
140 tty_print_string (hotkey
.end
);
144 /* Default callback for widgets */
146 default_proc (widget_msg_t msg
, int parm
)
162 return MSG_NOT_HANDLED
;
166 static int button_event (Gpm_Event
* event
, void *);
171 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
173 WButton
*b
= (WButton
*) w
;
176 Dlg_head
*h
= b
->widget
.owner
;
182 * Don't let the default button steal Enter from the current
183 * button. This is a workaround for the flawed event model
184 * when hotkeys are sent to all widgets before the key is
185 * handled by the current widget.
187 if (parm
== '\n' && (Widget
*) h
->current
->data
== &b
->widget
)
189 button_callback (w
, WIDGET_KEY
, ' ');
193 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
195 button_callback (w
, WIDGET_KEY
, ' ');
199 if (b
->text
.hotkey
!= NULL
)
201 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
203 button_callback (w
, WIDGET_KEY
, ' ');
207 return MSG_NOT_HANDLED
;
210 if (parm
!= ' ' && parm
!= '\n')
211 return MSG_NOT_HANDLED
;
214 stop
= (*b
->callback
) (b
->action
);
215 if (!b
->callback
|| stop
)
217 h
->ret_value
= b
->action
;
239 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
245 if (msg
== WIDGET_UNFOCUS
)
247 else if (msg
== WIDGET_FOCUS
)
250 widget_selectcolor (w
, b
->selected
, FALSE
);
251 widget_move (w
, 0, 0);
256 tty_print_string ("[< ");
259 tty_print_string ("[ ");
262 tty_print_string ("[");
269 draw_hotkey (w
, b
->text
, b
->selected
);
274 tty_print_string (" >]");
277 tty_print_string (" ]");
280 tty_print_string ("]");
286 release_hotkey (b
->text
);
290 return default_proc (msg
, parm
);
295 button_event (Gpm_Event
* event
, void *data
)
299 if (event
->type
& (GPM_DOWN
| GPM_UP
))
301 Dlg_head
*h
= b
->widget
.owner
;
302 dlg_select_widget (b
);
303 if (event
->type
& GPM_UP
)
305 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
306 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
314 button_get_len (const WButton
* b
)
316 int ret
= hotkey_width (b
->text
);
336 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
338 WButton
*b
= g_new (WButton
, 1);
342 b
->text
= parse_hotkey (text
);
344 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
347 b
->callback
= callback
;
348 widget_want_hotkey (b
->widget
, 1);
349 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
355 button_get_text (const WButton
* b
)
357 if (b
->text
.hotkey
!= NULL
)
358 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
360 return g_strdup (b
->text
.start
);
364 button_set_text (WButton
* b
, const char *text
)
366 release_hotkey (b
->text
);
367 b
->text
= parse_hotkey (text
);
368 b
->widget
.cols
= button_get_len (b
);
369 dlg_redraw (b
->widget
.owner
);
373 /* Radio button widget */
374 static int radio_event (Gpm_Event
* event
, void *);
377 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
379 WRadio
*r
= (WRadio
*) w
;
381 Dlg_head
*h
= r
->widget
.owner
;
387 int lp
= g_ascii_tolower ((gchar
) parm
);
389 for (i
= 0; i
< r
->count
; i
++)
391 if (r
->texts
[i
].hotkey
!= NULL
)
393 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
400 radio_callback (w
, WIDGET_KEY
, ' ');
405 return MSG_NOT_HANDLED
;
412 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
413 radio_callback (w
, WIDGET_FOCUS
, ' ');
423 return MSG_NOT_HANDLED
;
427 if (r
->count
- 1 > r
->pos
)
433 return MSG_NOT_HANDLED
;
436 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
437 radio_callback (w
, WIDGET_FOCUS
, ' ');
438 widget_move (&r
->widget
, r
->pos
, 1);
444 for (i
= 0; i
< r
->count
; i
++)
446 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
447 widget_selectcolor (w
, focused
, FALSE
);
448 widget_move (&r
->widget
, i
, 0);
449 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
450 draw_hotkey (w
, r
->texts
[i
], focused
);
455 for (i
= 0; i
< r
->count
; i
++)
457 release_hotkey (r
->texts
[i
]);
463 return default_proc (msg
, parm
);
468 radio_event (Gpm_Event
* event
, void *data
)
473 if (event
->type
& (GPM_DOWN
| GPM_UP
))
475 Dlg_head
*h
= r
->widget
.owner
;
477 r
->pos
= event
->y
- 1;
478 dlg_select_widget (r
);
479 if (event
->type
& GPM_UP
)
481 radio_callback (w
, WIDGET_KEY
, ' ');
482 radio_callback (w
, WIDGET_FOCUS
, 0);
483 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
491 radio_new (int y
, int x
, int count
, const char **texts
)
493 WRadio
*result
= g_new (WRadio
, 1);
496 /* Compute the longest string */
497 result
->texts
= g_new (struct hotkey_t
, count
);
500 for (i
= 0; i
< count
; i
++)
502 result
->texts
[i
] = parse_hotkey (texts
[i
]);
503 m
= hotkey_width (result
->texts
[i
]);
508 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
512 result
->count
= count
;
513 widget_want_hotkey (result
->widget
, 1);
519 /* Checkbutton widget */
521 static int check_event (Gpm_Event
* event
, void *);
524 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
526 WCheck
*c
= (WCheck
*) w
;
527 Dlg_head
*h
= c
->widget
.owner
;
532 if (c
->text
.hotkey
!= NULL
)
534 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
537 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
541 return MSG_NOT_HANDLED
;
545 return MSG_NOT_HANDLED
;
547 c
->state
^= C_CHANGE
;
548 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
549 check_callback (w
, WIDGET_FOCUS
, ' ');
553 widget_move (&c
->widget
, 0, 1);
559 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
560 widget_move (&c
->widget
, 0, 0);
561 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
562 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
566 release_hotkey (c
->text
);
570 return default_proc (msg
, parm
);
575 check_event (Gpm_Event
* event
, void *data
)
580 if (event
->type
& (GPM_DOWN
| GPM_UP
))
582 Dlg_head
*h
= c
->widget
.owner
;
584 dlg_select_widget (c
);
585 if (event
->type
& GPM_UP
)
587 check_callback (w
, WIDGET_KEY
, ' ');
588 check_callback (w
, WIDGET_FOCUS
, 0);
589 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
597 check_new (int y
, int x
, int state
, const char *text
)
599 WCheck
*c
= g_new (WCheck
, 1);
601 c
->text
= parse_hotkey (text
);
603 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
604 c
->state
= state
? C_BOOL
: 0;
605 widget_want_hotkey (c
->widget
, 1);
611 save_text_to_clip_file (const char *text
)
618 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
619 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
620 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
626 str_len
= strlen (text
);
627 ret
= mc_write (file
, (char *) text
, str_len
);
629 return ret
== (ssize_t
) str_len
;
633 load_text_from_clip_file (char **text
)
638 gboolean first
= TRUE
;
640 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
641 f
= fopen (fname
, "r");
649 while (fgets (buf
, sizeof (buf
), f
))
656 if (buf
[len
- 1] == '\n')
662 *text
= g_strdup (buf
);
666 /* remove \n on EOL */
669 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
678 return (*text
!= NULL
);
682 panel_save_curent_file_to_clip_file (void)
686 if (current_panel
->marked
== 0)
687 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
691 gboolean first
= TRUE
;
694 for (i
= 0; i
< current_panel
->count
; i
++)
695 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
696 { /* Skip the unmarked ones */
699 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
704 /* Add empty lines after the file */
708 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
716 res
= save_text_to_clip_file (flist
);
726 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
728 WLabel
*l
= (WLabel
*) w
;
729 Dlg_head
*h
= l
->widget
.owner
;
736 /* We don't want to get the focus */
738 return MSG_NOT_HANDLED
;
742 char *p
= l
->text
, *q
, c
= 0;
749 tty_setcolor (DEFAULT_COLOR
);
751 tty_setcolor (DLG_NORMALC (h
));
755 q
= strchr (p
, '\n');
762 widget_move (&l
->widget
, y
, 0);
763 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
779 return default_proc (msg
, parm
);
784 label_set_text (WLabel
* label
, const char *text
)
786 int newcols
= label
->widget
.cols
;
789 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
790 return; /* Flickering is not nice */
792 g_free (label
->text
);
796 label
->text
= g_strdup (text
);
797 if (label
->auto_adjust_cols
)
799 str_msg_term_size (text
, &newlines
, &newcols
);
800 if (newcols
> label
->widget
.cols
)
801 label
->widget
.cols
= newcols
;
802 if (newlines
> label
->widget
.lines
)
803 label
->widget
.lines
= newlines
;
809 if (label
->widget
.owner
)
810 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
812 if (newcols
< label
->widget
.cols
)
813 label
->widget
.cols
= newcols
;
817 label_new (int y
, int x
, const char *text
)
824 str_msg_term_size (text
, &lines
, &cols
);
826 l
= g_new (WLabel
, 1);
827 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
828 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
829 l
->auto_adjust_cols
= 1;
831 widget_want_cursor (l
->widget
, 0);
836 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
838 WHLine
*l
= (WHLine
*) w
;
839 Dlg_head
*h
= l
->widget
.owner
;
845 if (l
->auto_adjust_cols
)
847 if (((w
->owner
->flags
& DLG_COMPACT
) != 0))
850 w
->cols
= w
->owner
->cols
;
854 w
->x
= w
->owner
->x
+ 1;
855 w
->cols
= w
->owner
->cols
- 2;
860 /* We don't want to get the focus */
861 return MSG_NOT_HANDLED
;
865 tty_setcolor (DEFAULT_COLOR
);
867 tty_setcolor (DLG_NORMALC (h
));
869 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
871 if (l
->auto_adjust_cols
)
873 widget_move (w
, 0, 0);
874 tty_print_alt_char (ACS_LTEE
, FALSE
);
875 widget_move (w
, 0, w
->cols
- 1);
876 tty_print_alt_char (ACS_RTEE
, FALSE
);
881 return default_proc (msg
, parm
);
887 hline_new (int y
, int x
, int width
)
893 l
= g_new (WHLine
, 1);
894 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
895 l
->auto_adjust_cols
= (width
< 0);
896 l
->transparent
= FALSE
;
897 widget_want_cursor (l
->widget
, 0);
901 /* Gauge widget (progress indicator) */
902 /* Currently width is hardcoded here for text mode */
906 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
908 WGauge
*g
= (WGauge
*) w
;
909 Dlg_head
*h
= g
->widget
.owner
;
911 if (msg
== WIDGET_INIT
)
914 /* We don't want to get the focus */
915 if (msg
== WIDGET_FOCUS
)
916 return MSG_NOT_HANDLED
;
918 if (msg
== WIDGET_DRAW
)
920 widget_move (&g
->widget
, 0, 0);
921 tty_setcolor (DLG_NORMALC (h
));
923 tty_printf ("%*s", gauge_len
, "");
926 int percentage
, columns
;
927 long total
= g
->max
, done
= g
->current
;
929 if (total
<= 0 || done
< 0)
936 while (total
> 65535)
941 percentage
= (200 * done
/ total
+ 1) / 2;
942 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
943 tty_print_char ('[');
944 if (g
->from_left_to_right
)
946 tty_setcolor (GAUGE_COLOR
);
947 tty_printf ("%*s", (int) columns
, "");
948 tty_setcolor (DLG_NORMALC (h
));
949 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
953 tty_setcolor (DLG_NORMALC (h
));
954 tty_printf ("%*s", gauge_len
- columns
- 7, "");
955 tty_setcolor (GAUGE_COLOR
);
956 tty_printf ("%*s", columns
, "");
957 tty_setcolor (DLG_NORMALC (h
));
958 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
964 return default_proc (msg
, parm
);
968 gauge_set_value (WGauge
* g
, int max
, int current
)
970 if (g
->current
== current
&& g
->max
== max
)
971 return; /* Do not flicker */
973 max
= 1; /* I do not like division by zero :) */
975 g
->current
= current
;
977 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
981 gauge_show (WGauge
* g
, int shown
)
983 if (g
->shown
== shown
)
986 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
990 gauge_new (int y
, int x
, int shown
, int max
, int current
)
992 WGauge
*g
= g_new (WGauge
, 1);
994 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
997 max
= 1; /* I do not like division by zero :) */
999 g
->current
= current
;
1000 g
->from_left_to_right
= TRUE
;
1001 widget_want_cursor (g
->widget
, 0);
1008 /* {{{ history button */
1010 #define LARGE_HISTORY_BUTTON 1
1012 #ifdef LARGE_HISTORY_BUTTON
1013 # define HISTORY_BUTTON_WIDTH 3
1015 # define HISTORY_BUTTON_WIDTH 1
1018 #define should_show_history_button(in) \
1019 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1022 draw_history_button (WInput
* in
)
1025 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1026 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1027 #ifdef LARGE_HISTORY_BUTTON
1030 h
= in
->widget
.owner
;
1031 tty_setcolor (NORMAL_COLOR
);
1032 tty_print_string ("[ ]");
1033 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1034 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1038 tty_setcolor (MARKED_COLOR
);
1043 /* }}} history button */
1046 /* Input widgets now have a global kill ring */
1047 /* Pointer to killed data */
1048 static char *kill_buffer
= NULL
;
1051 input_set_markers (WInput
* in
, long m1
)
1057 input_mark_cmd (WInput
* in
, gboolean mark
)
1061 in
->highlight
= FALSE
;
1062 input_set_markers (in
, 0);
1066 in
->highlight
= TRUE
;
1067 input_set_markers (in
, in
->point
);
1072 input_eval_marks (WInput
* in
, long *start_mark
, long *end_mark
)
1076 *start_mark
= min (in
->mark
, in
->point
);
1077 *end_mark
= max (in
->mark
, in
->point
);
1082 *start_mark
= *end_mark
= 0;
1088 delete_region (WInput
* in
, int x_first
, int x_last
)
1090 int first
= min (x_first
, x_last
);
1091 int last
= max (x_first
, x_last
);
1094 input_mark_cmd (in
, FALSE
);
1096 last
= str_offset_to_pos (in
->buffer
, last
);
1097 first
= str_offset_to_pos (in
->buffer
, first
);
1098 len
= strlen (&in
->buffer
[last
]) + 1;
1099 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1105 update_input (WInput
* in
, int clear_first
)
1107 int has_history
= 0;
1109 int buf_len
= str_length (in
->buffer
);
1113 if (should_show_history_button (in
))
1114 has_history
= HISTORY_BUTTON_WIDTH
;
1116 if (in
->disable_update
)
1119 pw
= str_term_width2 (in
->buffer
, in
->point
);
1121 /* Make the point visible */
1122 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1125 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1126 if (in
->term_first_shown
< 0)
1127 in
->term_first_shown
= 0;
1130 /* Adjust the mark */
1131 if (in
->mark
> buf_len
)
1135 draw_history_button (in
);
1138 tty_setcolor (in
->unchanged_color
);
1140 tty_setcolor (in
->color
);
1142 widget_move (&in
->widget
, 0, 0);
1144 if (!in
->is_password
)
1148 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1149 in
->field_width
- has_history
));
1154 if (input_eval_marks (in
, &m1
, &m2
))
1156 tty_setcolor (in
->color
);
1157 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1158 tty_print_string (cp
);
1159 tty_setcolor (in
->mark_color
);
1160 if (m1
< in
->term_first_shown
)
1162 widget_move (&in
->widget
, 0, 0);
1163 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
, m2
- in
->term_first_shown
));
1168 widget_move (&in
->widget
, 0, m1
- in
->term_first_shown
);
1169 sel_width
= min (m2
- m1
, (in
->field_width
- has_history
) - (str_term_width2 (in
->buffer
, m1
) - in
->term_first_shown
));
1170 tty_print_string (str_term_substring (in
->buffer
, m1
, sel_width
));
1177 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1178 for (i
= 0; i
< in
->field_width
- has_history
; i
++)
1182 tty_setcolor (in
->color
);
1183 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1186 str_cnext_char (&cp
);
1195 winput_set_origin (WInput
* in
, int x
, int field_width
)
1198 in
->field_width
= in
->widget
.cols
= field_width
;
1199 update_input (in
, 0);
1202 /* {{{ history saving and loading */
1204 int num_history_items_recorded
= 60;
1207 This loads and saves the history of an input line to and from the
1208 widget. It is called with the widgets history name on creation of the
1209 widget, and returns the GList list. It stores histories in the file
1210 ~/.mc/history in using the profile code.
1212 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1213 function) then input_new assigns the default text to be the last text
1214 entered, or "" if not found.
1218 history_get (const char *input_name
)
1225 size_t keys_num
= 0;
1228 if (num_history_items_recorded
== 0) /* this is how to disable */
1230 if ((input_name
== NULL
) || (*input_name
== '\0'))
1233 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1234 cfg
= mc_config_init (profile
);
1236 /* get number of keys */
1237 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1240 for (i
= 0; i
< keys_num
; i
++)
1243 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1244 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1246 if (this_entry
!= NULL
)
1247 hist
= list_append_unique (hist
, this_entry
);
1250 mc_config_deinit (cfg
);
1253 /* return pointer to the last entry in the list */
1254 return g_list_last (hist
);
1258 history_put (const char *input_name
, GList
* h
)
1264 if (num_history_items_recorded
== 0) /* this is how to disable */
1266 if ((input_name
== NULL
) || (*input_name
== '\0'))
1271 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1273 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1277 /* Make sure the history is only readable by the user */
1278 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1284 /* go to end of list */
1285 h
= g_list_last (h
);
1287 /* go back 60 places */
1288 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1289 h
= g_list_previous (h
);
1291 cfg
= mc_config_init (profile
);
1293 if (input_name
!= NULL
)
1294 mc_config_del_group (cfg
, input_name
);
1296 /* dump history into profile */
1297 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1299 char *text
= (char *) h
->data
;
1301 /* We shouldn't have null entries, but let's be sure */
1305 g_snprintf (key
, sizeof (key
), "%d", i
++);
1306 mc_config_set_string (cfg
, input_name
, key
, text
);
1310 mc_config_save_file (cfg
, NULL
);
1311 mc_config_deinit (cfg
);
1315 /* }}} history saving and loading */
1318 /* {{{ history display */
1323 return _("History");
1334 dlg_hist_reposition (Dlg_head
* dlg_head
)
1336 dlg_hist_data
*data
;
1337 int x
= 0, y
, he
, wi
;
1340 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1341 return MSG_NOT_HANDLED
;
1343 data
= (dlg_hist_data
*) dlg_head
->data
;
1345 y
= data
->widget
->y
;
1346 he
= data
->count
+ 2;
1348 if (he
<= y
|| y
> (LINES
- 6))
1350 he
= min (he
, y
- 1);
1356 he
= min (he
, LINES
- y
);
1359 if (data
->widget
->x
> 2)
1360 x
= data
->widget
->x
- 2;
1362 wi
= data
->maxlen
+ 4;
1364 if ((wi
+ x
) > COLS
)
1366 wi
= min (wi
, COLS
);
1370 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1376 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1381 return dlg_hist_reposition (h
);
1384 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1389 show_hist (GList
** history
, Widget
* widget
)
1391 GList
*z
, *hlist
= NULL
, *hi
;
1392 size_t maxlen
, i
, count
= 0;
1394 Dlg_head
*query_dlg
;
1395 WListbox
*query_list
;
1396 dlg_hist_data hist_data
;
1398 if (*history
== NULL
)
1401 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1403 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1407 i
= str_term_width1 ((char *) z
->data
);
1408 maxlen
= max (maxlen
, i
);
1411 entry
= g_new0 (WLEntry
, 1);
1412 /* history is being reverted here */
1413 entry
->text
= g_strdup ((char *) z
->data
);
1414 hlist
= g_list_prepend (hlist
, entry
);
1417 hist_data
.widget
= widget
;
1418 hist_data
.count
= count
;
1419 hist_data
.maxlen
= maxlen
;
1422 create_dlg (TRUE
, 0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1423 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1424 query_dlg
->data
= &hist_data
;
1426 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1428 /* this call makes list stick to all sides of dialog, effectively make
1429 it be resized with dialog */
1430 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1432 /* to avoid diplicating of (calculating sizes in two places)
1433 code, call dlg_hist_callback function here, to set dialog and
1435 The main idea - create 4x4 dialog and add 2x2 list in
1436 center of it, and let dialog function resize it to needed
1438 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1440 if (query_dlg
->y
< widget
->y
)
1442 /* draw list entries from bottom upto top */
1443 listbox_set_list (query_list
, hlist
);
1444 listbox_select_last (query_list
);
1448 /* draw list entries from top downto bottom */
1449 /* revert history direction */
1450 hlist
= g_list_reverse (hlist
);
1451 listbox_set_list (query_list
, hlist
);
1454 if (run_dlg (query_dlg
) != B_CANCEL
)
1458 listbox_get_current (query_list
, &q
, NULL
);
1463 /* get modified history from dialog */
1465 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1469 entry
= (WLEntry
*) hi
->data
;
1470 /* history is being reverted here again */
1471 z
= g_list_prepend (z
, entry
->text
);
1475 destroy_dlg (query_dlg
);
1477 /* restore history direction */
1478 if (query_dlg
->y
< widget
->y
)
1479 z
= g_list_reverse (z
);
1481 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1482 g_list_free (*history
);
1483 *history
= g_list_last (z
);
1489 do_show_hist (WInput
* in
)
1493 r
= show_hist (&in
->history
, &in
->widget
);
1496 assign_text (in
, r
);
1501 /* }}} history display */
1504 input_destroy (WInput
* in
)
1508 fprintf (stderr
, "Internal error: null Input *\n");
1509 exit (EXIT_FAILURE
);
1514 if (in
->history
!= NULL
)
1516 if (!in
->is_password
&& (((Widget
*) in
)->owner
->ret_value
!= B_CANCEL
))
1517 history_put (in
->history_name
, in
->history
);
1519 in
->history
= g_list_first (in
->history
);
1520 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1521 g_list_free (in
->history
);
1524 g_free (in
->buffer
);
1525 free_completions (in
);
1526 g_free (in
->history_name
);
1528 g_free (kill_buffer
);
1533 input_disable_update (WInput
* in
)
1535 in
->disable_update
++;
1539 input_enable_update (WInput
* in
)
1541 in
->disable_update
--;
1542 update_input (in
, 0);
1547 push_history (WInput
* in
, const char *text
)
1549 /* input widget where urls with passwords are entered without any
1551 const char *password_input_fields
[] = {
1552 " Link to a remote machine ",
1554 " SMB link to machine "
1556 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1566 for (i
= 0; i
< ELEMENTS
; i
++)
1567 password_input_fields
[i
] = _(password_input_fields
[i
]);
1570 t
= g_strstrip (g_strdup (text
));
1573 t
= g_strdup (empty
? "" : text
);
1575 if (in
->history_name
!= NULL
)
1577 /* FIXME: It is the strange code. Rewrite is needed. */
1579 const char *p
= in
->history_name
+ 3;
1581 for (i
= 0; i
< ELEMENTS
; i
++)
1582 if (strcmp (p
, password_input_fields
[i
]) == 0)
1585 strip_password (t
, i
>= ELEMENTS
);
1588 in
->history
= list_append_unique (in
->history
, t
);
1592 /* Cleans the input line and adds the current text to the history */
1594 new_input (WInput
* in
)
1596 push_history (in
, in
->buffer
);
1598 in
->buffer
[0] = '\0';
1602 in
->highlight
= FALSE
;
1603 free_completions (in
);
1604 update_input (in
, 0);
1608 move_buffer_backward (WInput
* in
, int start
, int end
)
1611 int str_len
= str_length (in
->buffer
);
1612 if (start
>= str_len
|| end
> str_len
+ 1)
1615 pos
= str_offset_to_pos (in
->buffer
, start
);
1616 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1618 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1619 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1623 insert_char (WInput
* in
, int c_code
)
1631 if (input_eval_marks (in
, &m1
, &m2
))
1632 delete_region (in
, m1
, m2
);
1635 return MSG_NOT_HANDLED
;
1637 if (in
->charpoint
>= MB_LEN_MAX
)
1640 in
->charbuf
[in
->charpoint
] = c_code
;
1643 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1647 in
->charpoint
= 0; /* broken multibyte char, skip */
1652 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1654 /* Expand the buffer */
1655 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1656 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1660 in
->current_max_size
= new_length
;
1664 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1666 /* bytes from begin */
1667 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1669 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1671 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1672 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1674 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1683 beginning_of_line (WInput
* in
)
1690 end_of_line (WInput
* in
)
1692 in
->point
= str_length (in
->buffer
);
1697 backward_char (WInput
* in
)
1699 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1703 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1709 forward_char (WInput
* in
)
1711 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1714 in
->point
+= str_cnext_noncomb_char (&act
);
1720 forward_word (WInput
* in
)
1722 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1724 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1726 str_cnext_char (&p
);
1729 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1731 str_cnext_char (&p
);
1737 backward_word (WInput
* in
)
1742 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1743 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1745 while (p
!= in
->buffer
)
1748 str_cprev_char (&p
);
1749 if (!str_isspace (p
) && !str_ispunct (p
))
1756 while (p
!= in
->buffer
)
1758 str_cprev_char (&p
);
1759 if (str_isspace (p
) || str_ispunct (p
))
1767 key_left (WInput
* in
)
1773 key_ctrl_left (WInput
* in
)
1779 key_right (WInput
* in
)
1785 key_ctrl_right (WInput
* in
)
1790 backward_delete (WInput
* in
)
1792 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1798 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1799 move_buffer_backward (in
, start
, in
->point
);
1806 delete_char (WInput
* in
)
1808 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1809 int end
= in
->point
;
1811 end
+= str_cnext_noncomb_char (&act
);
1813 move_buffer_backward (in
, in
->point
, end
);
1819 copy_region (WInput
* in
, int x_first
, int x_last
)
1821 int first
= min (x_first
, x_last
);
1822 int last
= max (x_first
, x_last
);
1826 /* Copy selected files to clipboard */
1827 panel_save_curent_file_to_clip_file ();
1828 /* try use external clipboard utility */
1829 copy_file_to_ext_clip ();
1833 g_free (kill_buffer
);
1835 first
= str_offset_to_pos (in
->buffer
, first
);
1836 last
= str_offset_to_pos (in
->buffer
, last
);
1838 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1840 save_text_to_clip_file (kill_buffer
);
1841 /* try use external clipboard utility */
1842 copy_file_to_ext_clip ();
1846 kill_word (WInput
* in
)
1848 int old_point
= in
->point
;
1852 new_point
= in
->point
;
1853 in
->point
= old_point
;
1855 copy_region (in
, old_point
, new_point
);
1856 delete_region (in
, old_point
, new_point
);
1863 back_kill_word (WInput
* in
)
1865 int old_point
= in
->point
;
1869 new_point
= in
->point
;
1870 in
->point
= old_point
;
1872 copy_region (in
, old_point
, new_point
);
1873 delete_region (in
, old_point
, new_point
);
1878 set_mark (WInput
* in
)
1880 input_mark_cmd (in
, TRUE
);
1884 kill_save (WInput
* in
)
1886 copy_region (in
, in
->mark
, in
->point
);
1890 kill_region (WInput
* in
)
1893 delete_region (in
, in
->point
, in
->mark
);
1897 clear_region (WInput
* in
)
1899 delete_region (in
, in
->point
, in
->mark
);
1905 if (kill_buffer
!= NULL
)
1910 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1911 insert_char (in
, *p
);
1917 kill_line (WInput
* in
)
1919 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1920 g_free (kill_buffer
);
1921 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1922 in
->buffer
[chp
] = '\0';
1927 ins_from_clip (WInput
* in
)
1931 /* try use external clipboard utility */
1932 paste_to_file_from_ext_clip ();
1934 if (load_text_from_clip_file (&p
))
1938 for (pp
= p
; *pp
!= '\0'; pp
++)
1939 insert_char (in
, *pp
);
1946 assign_text (WInput
* in
, const char *text
)
1948 free_completions (in
);
1949 g_free (in
->buffer
);
1950 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1951 in
->current_max_size
= strlen (in
->buffer
) + 1;
1952 in
->point
= str_length (in
->buffer
);
1959 hist_prev (WInput
* in
)
1967 push_history (in
, in
->buffer
);
1969 prev
= g_list_previous (in
->history
);
1973 assign_text (in
, (char *) prev
->data
);
1979 hist_next (WInput
* in
)
1983 push_history (in
, in
->buffer
);
1984 assign_text (in
, "");
1991 if (!in
->history
->next
)
1993 assign_text (in
, "");
1997 in
->history
= g_list_next (in
->history
);
1998 assign_text (in
, (char *) in
->history
->data
);
2003 port_region_marked_for_delete (WInput
* in
)
2005 in
->buffer
[0] = '\0';
2012 input_execute_cmd (WInput
* in
, unsigned long command
)
2014 cb_ret_t res
= MSG_HANDLED
;
2016 /* a highlight command like shift-arrow */
2017 if (command
== CK_InputLeftHighlight
||
2018 command
== CK_InputRightHighlight
||
2019 command
== CK_InputWordLeftHighlight
||
2020 command
== CK_InputWordRightHighlight
||
2021 command
== CK_InputBolHighlight
||
2022 command
== CK_InputEolHighlight
)
2026 input_mark_cmd (in
, FALSE
); /* clear */
2027 input_mark_cmd (in
, TRUE
); /* marking on */
2033 case CK_InputForwardWord
:
2034 case CK_InputBackwardWord
:
2035 case CK_InputForwardChar
:
2036 case CK_InputBackwardChar
:
2038 input_mark_cmd (in
, FALSE
);
2044 case CK_InputBolHighlight
:
2045 beginning_of_line (in
);
2048 case CK_InputEolHighlight
:
2051 case CK_InputMoveLeft
:
2052 case CK_InputLeftHighlight
:
2055 case CK_InputWordLeft
:
2056 case CK_InputWordLeftHighlight
:
2059 case CK_InputMoveRight
:
2060 case CK_InputRightHighlight
:
2063 case CK_InputWordRight
:
2064 case CK_InputWordRightHighlight
:
2065 key_ctrl_right (in
);
2067 case CK_InputBackwardChar
:
2070 case CK_InputBackwardWord
:
2073 case CK_InputForwardChar
:
2076 case CK_InputForwardWord
:
2079 case CK_InputBackwardDelete
:
2083 if (input_eval_marks (in
, &m1
, &m2
))
2084 delete_region (in
, m1
, m2
);
2088 backward_delete (in
);
2091 case CK_InputDeleteChar
:
2093 port_region_marked_for_delete (in
);
2094 else if (in
->highlight
)
2097 if (input_eval_marks (in
, &m1
, &m2
))
2098 delete_region (in
, m1
, m2
);
2103 case CK_InputKillWord
:
2106 case CK_InputBackwardKillWord
:
2107 back_kill_word (in
);
2109 case CK_InputSetMark
:
2112 case CK_InputKillRegion
:
2115 case CK_InputClearLine
:
2118 case CK_InputKillSave
:
2127 case CK_InputKillLine
:
2130 case CK_InputHistoryPrev
:
2133 case CK_InputHistoryNext
:
2136 case CK_InputHistoryShow
:
2139 case CK_InputComplete
:
2143 res
= MSG_NOT_HANDLED
;
2146 if (command
!= CK_InputLeftHighlight
&&
2147 command
!= CK_InputRightHighlight
&&
2148 command
!= CK_InputWordLeftHighlight
&&
2149 command
!= CK_InputWordRightHighlight
&&
2150 command
!= CK_InputBolHighlight
&&
2151 command
!= CK_InputEolHighlight
)
2153 in
->highlight
= FALSE
;
2159 /* This function is a test for a special input key used in complete.c */
2160 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2161 and 2 if it is a complete key */
2163 is_in_input_map (WInput
* in
, int key
)
2166 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2167 if (key
== input_map
[i
].key
)
2169 input_execute_cmd (in
, input_map
[i
].command
);
2170 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2176 handle_char (WInput
* in
, int key
)
2181 v
= MSG_NOT_HANDLED
;
2185 free_completions (in
);
2186 v
= insert_char (in
, key
);
2187 update_input (in
, 1);
2191 for (i
= 0; input_map
[i
].key
; i
++)
2193 if (key
== input_map
[i
].key
)
2195 if (input_map
[i
].command
!= CK_InputComplete
)
2196 free_completions (in
);
2197 input_execute_cmd (in
, input_map
[i
].command
);
2198 update_input (in
, 1);
2203 if (input_map
[i
].command
== 0)
2206 return MSG_NOT_HANDLED
;
2208 port_region_marked_for_delete (in
);
2209 free_completions (in
);
2210 v
= insert_char (in
, key
);
2212 update_input (in
, 1);
2216 /* Inserts text in input line */
2218 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2220 input_disable_update (in
);
2221 while (*text
!= '\0')
2222 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2223 if (insert_extra_space
)
2224 handle_char (in
, ' ');
2225 input_enable_update (in
);
2226 update_input (in
, 1);
2230 input_set_point (WInput
* in
, int pos
)
2232 int max_pos
= str_length (in
->buffer
);
2236 if (pos
!= in
->point
)
2237 free_completions (in
);
2240 update_input (in
, 1);
2244 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2246 WInput
*in
= (WInput
*) w
;
2252 if (parm
== XCTRL ('q'))
2255 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2260 /* Keys we want others to handle */
2261 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2262 || parm
== KEY_F (10) || parm
== '\n')
2263 return MSG_NOT_HANDLED
;
2265 /* When pasting multiline text, insert literal Enter */
2266 if ((parm
& ~KEY_M_MASK
) == '\n')
2269 v
= handle_char (in
, '\n');
2274 return handle_char (in
, parm
);
2276 case WIDGET_COMMAND
:
2277 return input_execute_cmd (in
, parm
);
2280 case WIDGET_UNFOCUS
:
2282 update_input (in
, 0);
2286 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2287 - in
->term_first_shown
);
2290 case WIDGET_DESTROY
:
2295 return default_proc (msg
, parm
);
2300 input_event (Gpm_Event
* event
, void *data
)
2304 if (event
->type
& GPM_DOWN
)
2307 input_mark_cmd (in
, FALSE
);
2309 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2311 dlg_select_widget (in
);
2313 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2314 && should_show_history_button (in
))
2320 in
->point
= str_length (in
->buffer
);
2321 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2322 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2324 update_input (in
, 1);
2326 /* A lone up mustn't do anything */
2327 if (in
->highlight
&& event
->type
& (GPM_UP
| GPM_DRAG
))
2330 if (!(event
->type
& GPM_DRAG
))
2331 input_mark_cmd (in
, TRUE
);
2337 input_new (int y
, int x
, int *input_colors
, int width
, const char *def_text
,
2338 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2340 WInput
*in
= g_new (WInput
, 1);
2341 size_t initial_buffer_len
;
2343 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2346 in
->history_name
= NULL
;
2348 if ((histname
!= NULL
) && (*histname
!= '\0'))
2350 in
->history_name
= g_strdup (histname
);
2351 in
->history
= history_get (histname
);
2354 if (def_text
== NULL
)
2356 else if (def_text
== INPUT_LAST_TEXT
)
2358 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2359 def_text
= (char *) in
->history
->data
;
2364 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2365 in
->widget
.options
|= W_IS_INPUT
;
2366 in
->completions
= NULL
;
2367 in
->completion_flags
= completion_flags
;
2368 in
->current_max_size
= initial_buffer_len
;
2369 in
->buffer
= g_new (char, initial_buffer_len
);
2370 in
->color
= input_colors
[0];
2371 in
->unchanged_color
= input_colors
[1];
2372 in
->mark_color
= input_colors
[2];
2373 in
->field_width
= width
;
2375 in
->highlight
= FALSE
;
2376 in
->term_first_shown
= 0;
2377 in
->disable_update
= 0;
2380 in
->is_password
= 0;
2382 strcpy (in
->buffer
, def_text
);
2383 in
->point
= str_length (in
->buffer
);
2390 /* Listbox widget */
2392 /* Should draw the scrollbar, but currently draws only
2393 * indications that there is more information
2397 listbox_entry_free (void *data
)
2405 listbox_drawscroll (WListbox
* l
)
2407 const int max_line
= l
->widget
.lines
- 1;
2411 /* Are we at the top? */
2412 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2414 tty_print_one_vline (TRUE
);
2416 tty_print_char ('^');
2418 /* Are we at the bottom? */
2419 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2420 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2421 tty_print_one_vline (TRUE
);
2423 tty_print_char ('v');
2425 /* Now draw the nice relative pointer */
2427 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2429 for (i
= 1; i
< max_line
; i
++)
2431 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2433 tty_print_one_vline (TRUE
);
2435 tty_print_char ('*');
2440 listbox_draw (WListbox
* l
, gboolean focused
)
2442 const Dlg_head
*h
= l
->widget
.owner
;
2443 const int normalc
= DLG_NORMALC (h
);
2444 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2451 le
= g_list_nth (l
->list
, l
->top
);
2452 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2453 pos
= (le
== NULL
) ? 0 : l
->top
;
2455 for (i
= 0; i
< l
->widget
.lines
; i
++)
2459 /* Display the entry */
2460 if (pos
== l
->pos
&& sel_line
== -1)
2463 tty_setcolor (selc
);
2466 tty_setcolor (normalc
);
2468 widget_move (&l
->widget
, i
, 1);
2470 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2474 WLEntry
*e
= (WLEntry
*) le
->data
;
2476 le
= g_list_next (le
);
2480 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2483 l
->cursor_y
= sel_line
;
2485 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2487 tty_setcolor (normalc
);
2488 listbox_drawscroll (l
);
2493 listbox_check_hotkey (WListbox
* l
, int key
)
2498 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2500 WLEntry
*e
= (WLEntry
*) le
->data
;
2502 if (e
->hotkey
== key
)
2509 /* Selects the last entry and scrolls the list to the bottom */
2511 listbox_select_last (WListbox
* l
)
2513 l
->pos
= l
->count
- 1;
2514 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2517 /* Selects the first entry and scrolls the list to the top */
2519 listbox_select_first (WListbox
* l
)
2521 l
->pos
= l
->top
= 0;
2525 listbox_set_list (WListbox
* l
, GList
* list
)
2527 listbox_remove_list (l
);
2532 l
->top
= l
->pos
= 0;
2533 l
->count
= g_list_length (list
);
2538 listbox_remove_list (WListbox
* l
)
2540 if ((l
!= NULL
) && (l
->count
!= 0))
2542 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2543 g_list_free (l
->list
);
2545 l
->count
= l
->pos
= l
->top
= 0;
2550 listbox_remove_current (WListbox
* l
)
2552 if ((l
!= NULL
) && (l
->count
!= 0))
2556 current
= g_list_nth (l
->list
, l
->pos
);
2557 l
->list
= g_list_remove_link (l
->list
, current
);
2558 listbox_entry_free ((WLEntry
*) current
->data
);
2559 g_list_free_1 (current
);
2563 l
->top
= l
->pos
= 0;
2564 else if (l
->pos
>= l
->count
)
2565 l
->pos
= l
->count
- 1;
2570 listbox_select_entry (WListbox
* l
, int dest
)
2574 gboolean top_seen
= FALSE
;
2580 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2590 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2591 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2596 /* If we are unable to find it, set decent values */
2597 l
->pos
= l
->top
= 0;
2600 /* Selects from base the pos element */
2602 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2604 int last
= l
->count
- 1;
2614 listbox_fwd (WListbox
* l
)
2616 if (l
->pos
+ 1 >= l
->count
)
2617 listbox_select_first (l
);
2619 listbox_select_entry (l
, l
->pos
+ 1);
2623 listbox_back (WListbox
* l
)
2626 listbox_select_last (l
);
2628 listbox_select_entry (l
, l
->pos
- 1);
2631 /* Return MSG_HANDLED if we want a redraw */
2633 listbox_key (WListbox
* l
, int key
)
2637 cb_ret_t j
= MSG_NOT_HANDLED
;
2639 if (l
->list
== NULL
)
2640 return MSG_NOT_HANDLED
;
2642 /* focus on listbox item N by '0'..'9' keys */
2643 if (key
>= '0' && key
<= '9')
2645 int oldpos
= l
->pos
;
2646 listbox_select_entry (l
, key
- '0');
2648 /* need scroll to item? */
2649 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2660 listbox_select_first (l
);
2666 listbox_select_last (l
);
2681 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2690 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2701 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2702 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2704 listbox_remove_current (l
);
2705 if ((l
->top
> 0) && (is_last
|| is_more
))
2710 case (KEY_M_SHIFT
| KEY_DC
):
2712 if (l
->deletable
&& confirm_history_cleanup
2713 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2714 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2715 _("Do you want clean this history?"),
2716 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2718 listbox_remove_list (l
);
2731 listbox_destroy (WListbox
* l
)
2733 /* don't delete list in modifable listbox */
2735 listbox_remove_list (l
);
2739 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2741 WListbox
*l
= (WListbox
*) w
;
2742 Dlg_head
*h
= l
->widget
.owner
;
2754 pos
= listbox_check_hotkey (l
, parm
);
2756 return MSG_NOT_HANDLED
;
2758 listbox_select_entry (l
, pos
);
2759 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2761 if (l
->cback
!= NULL
)
2762 action
= l
->cback (l
);
2764 action
= LISTBOX_DONE
;
2766 if (action
== LISTBOX_DONE
)
2768 h
->ret_value
= B_ENTER
;
2776 ret_code
= listbox_key (l
, parm
);
2777 if (ret_code
!= MSG_NOT_HANDLED
)
2779 listbox_draw (l
, TRUE
);
2780 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2785 widget_move (&l
->widget
, l
->cursor_y
, 0);
2786 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2790 case WIDGET_UNFOCUS
:
2792 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2795 case WIDGET_DESTROY
:
2796 listbox_destroy (l
);
2799 case WIDGET_RESIZED
:
2803 return default_proc (msg
, parm
);
2808 listbox_event (Gpm_Event
* event
, void *data
)
2813 Dlg_head
*h
= l
->widget
.owner
;
2816 if (event
->type
& GPM_DOWN
)
2817 dlg_select_widget (l
);
2819 if (l
->list
== NULL
)
2822 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2824 int ret
= MOU_REPEAT
;
2826 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2830 for (i
= -event
->y
; i
>= 0; i
--)
2832 else if (event
->y
> l
->widget
.lines
)
2833 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2835 else if (event
->buttons
& GPM_B_UP
)
2840 else if (event
->buttons
& GPM_B_DOWN
)
2846 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2848 /* We need to refresh ourselves since the dialog manager doesn't */
2849 /* know about this event */
2850 listbox_draw (l
, TRUE
);
2855 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2859 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2860 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2863 dlg_select_widget (l
);
2864 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2866 if (l
->cback
!= NULL
)
2867 action
= l
->cback (l
);
2869 action
= LISTBOX_DONE
;
2871 if (action
== LISTBOX_DONE
)
2873 h
->ret_value
= B_ENTER
;
2882 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2884 WListbox
*l
= g_new (WListbox
, 1);
2889 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2892 l
->top
= l
->pos
= 0;
2894 l
->deletable
= deletable
;
2895 l
->cback
= callback
;
2896 l
->allow_duplicates
= TRUE
;
2897 l
->scrollbar
= !tty_is_slow ();
2898 widget_want_hotkey (l
->widget
, 1);
2899 widget_want_cursor (l
->widget
, 0);
2905 listbox_entry_cmp (const void *a
, const void *b
)
2907 const WLEntry
*ea
= (const WLEntry
*) a
;
2908 const WLEntry
*eb
= (const WLEntry
*) b
;
2910 return strcmp (ea
->text
, eb
->text
);
2913 /* Listbox item adding function */
2915 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2919 case LISTBOX_APPEND_AT_END
:
2920 l
->list
= g_list_append (l
->list
, e
);
2923 case LISTBOX_APPEND_BEFORE
:
2924 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2929 case LISTBOX_APPEND_AFTER
:
2930 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2933 case LISTBOX_APPEND_SORTED
:
2934 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2945 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2952 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2955 entry
= g_new (WLEntry
, 1);
2956 entry
->text
= g_strdup (text
);
2958 entry
->hotkey
= hotkey
;
2960 listbox_append_item (l
, entry
, pos
);
2966 listbox_search_text (WListbox
* l
, const char *text
)
2973 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2975 WLEntry
*e
= (WLEntry
*) le
->data
;
2977 if (strcmp (e
->text
, text
) == 0)
2985 /* Returns the current string text as well as the associated extra data */
2987 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2993 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2998 *string
= ok
? e
->text
: NULL
;
3001 *extra
= ok
? e
->data
: NULL
;
3005 /* ButtonBar widget */
3007 /* returns TRUE if a function has been called, FALSE otherwise. */
3009 buttonbar_call (WButtonBar
* bb
, int i
)
3011 cb_ret_t ret
= MSG_NOT_HANDLED
;
3013 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
3014 ret
= bb
->widget
.owner
->callback (bb
->widget
.owner
,
3015 (Widget
*) bb
, DLG_ACTION
,
3016 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
3020 /* calculate positions of buttons; width is never less than 7 */
3022 buttonbar_init_button_positions (WButtonBar
*bb
)
3027 if (COLS
< BUTTONBAR_LABELS_NUM
* 7)
3029 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3031 if (pos
+ 7 <= COLS
)
3034 bb
->labels
[i
].end_coord
= pos
;
3039 /* Distribute the extra width in a way that the middle vertical line
3040 (between F5 and F6) aligns with the two panels. The extra width
3041 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3044 lc_div
= COLS
/ BUTTONBAR_LABELS_NUM
;
3045 mod
= COLS
% BUTTONBAR_LABELS_NUM
;
3047 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
/ 2; i
++)
3050 if (BUTTONBAR_LABELS_NUM
/ 2 - 1 - i
< mod
/ 2)
3053 bb
->labels
[i
].end_coord
= pos
;
3056 for (; i
< BUTTONBAR_LABELS_NUM
; i
++)
3059 if (BUTTONBAR_LABELS_NUM
- 1 - i
< (mod
+ 1) / 2)
3062 bb
->labels
[i
].end_coord
= pos
;
3067 /* return width of one button */
3069 buttonbar_get_button_width (const WButtonBar
*bb
, int i
)
3072 return bb
->labels
[0].end_coord
;
3073 return bb
->labels
[i
].end_coord
- bb
->labels
[i
- 1].end_coord
;
3077 buttonbar_get_button_by_x_coord (const WButtonBar
*bb
, int x
)
3081 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3082 if (bb
->labels
[i
].end_coord
> x
)
3089 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3091 WButtonBar
*bb
= (WButtonBar
*) w
;
3098 return MSG_NOT_HANDLED
;
3101 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3102 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
3104 return MSG_NOT_HANDLED
;
3109 buttonbar_init_button_positions (bb
);
3110 widget_move (&bb
->widget
, 0, 0);
3111 tty_setcolor (DEFAULT_COLOR
);
3112 tty_printf ("%-*s", bb
->widget
.cols
, "");
3113 widget_move (&bb
->widget
, 0, 0);
3115 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3119 width
= buttonbar_get_button_width (bb
, i
);
3123 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
3124 tty_printf ("%2d", i
+ 1);
3125 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
3126 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
3127 tty_print_string (str_fit_to_term (text
, width
- 2, J_LEFT_FIT
));
3132 case WIDGET_DESTROY
:
3133 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3134 g_free (bb
->labels
[i
].text
);
3138 return default_proc (msg
, parm
);
3143 buttonbar_event (Gpm_Event
* event
, void *data
)
3145 WButtonBar
*bb
= data
;
3148 if (!(event
->type
& GPM_UP
))
3152 button
= buttonbar_get_button_by_x_coord (bb
, event
->x
- 1);
3154 buttonbar_call (bb
, button
);
3159 buttonbar_new (gboolean visible
)
3163 bb
= g_new0 (WButtonBar
, 1);
3165 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
3166 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
3167 bb
->visible
= visible
;
3168 widget_want_hotkey (bb
->widget
, 1);
3169 widget_want_cursor (bb
->widget
, 0);
3175 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
3177 g_free (bb
->labels
[lc_index
- 1].text
);
3178 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
3181 /* Find ButtonBar widget in the dialog */
3183 find_buttonbar (const Dlg_head
* h
)
3185 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
3189 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
3190 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
3192 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
3194 unsigned long command
= CK_Ignore_Key
;
3197 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
3199 if ((text
== NULL
) || (text
[0] == '\0'))
3200 set_label_text (bb
, idx
, "");
3202 set_label_text (bb
, idx
, text
);
3204 bb
->labels
[idx
- 1].command
= command
;
3205 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
3210 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
3212 bb
->visible
= visible
;
3216 buttonbar_redraw (WButtonBar
* bb
)
3219 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3223 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3225 WGroupbox
*g
= (WGroupbox
*) w
;
3233 return MSG_NOT_HANDLED
;
3236 tty_setcolor (COLOR_NORMAL
);
3237 draw_box (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3238 g
->widget
.x
- g
->widget
.owner
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3240 tty_setcolor (COLOR_HOT_NORMAL
);
3241 dlg_move (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3242 g
->widget
.x
- g
->widget
.owner
->x
+ 1);
3243 tty_print_string (g
->title
);
3246 case WIDGET_DESTROY
:
3251 return default_proc (msg
, parm
);
3256 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3258 WGroupbox
*g
= g_new (WGroupbox
, 1);
3260 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3262 g
->widget
.options
&= ~W_WANT_CURSOR
;
3263 widget_want_hotkey (g
->widget
, 0);
3265 /* Strip existing spaces, add one space before and after the title */
3269 t
= g_strstrip (g_strdup (title
));
3270 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);