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
;
72 if ((w
->options
& W_DISABLED
) != 0)
73 color
= DISABLED_COLOR
;
77 color
= DLG_HOT_FOCUSC (h
);
79 color
= DLG_HOT_NORMALC (h
);
84 color
= DLG_FOCUSC (h
);
86 color
= DLG_NORMALC (h
);
93 parse_hotkey (const char *text
)
95 struct hotkey_t result
;
98 /* search for '&', that is not on the of text */
99 cp
= strchr (text
, '&');
100 if (cp
!= NULL
&& cp
[1] != '\0')
102 result
.start
= g_strndup (text
, cp
- text
);
106 p
= str_cget_next_char (cp
);
107 result
.hotkey
= g_strndup (cp
, p
- cp
);
110 result
.end
= g_strdup (cp
);
114 result
.start
= g_strdup (text
);
115 result
.hotkey
= NULL
;
123 release_hotkey (const struct hotkey_t hotkey
)
125 g_free (hotkey
.start
);
126 g_free (hotkey
.hotkey
);
131 hotkey_width (const struct hotkey_t hotkey
)
135 result
= str_term_width1 (hotkey
.start
);
136 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
137 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
142 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
144 widget_selectcolor (w
, focused
, FALSE
);
145 tty_print_string (hotkey
.start
);
147 if (hotkey
.hotkey
!= NULL
)
149 widget_selectcolor (w
, focused
, TRUE
);
150 tty_print_string (hotkey
.hotkey
);
151 widget_selectcolor (w
, focused
, FALSE
);
154 if (hotkey
.end
!= NULL
)
155 tty_print_string (hotkey
.end
);
159 /* Default callback for widgets */
161 default_proc (widget_msg_t msg
, int parm
)
177 return MSG_NOT_HANDLED
;
181 static int button_event (Gpm_Event
* event
, void *);
186 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
188 WButton
*b
= (WButton
*) w
;
191 Dlg_head
*h
= b
->widget
.owner
;
197 * Don't let the default button steal Enter from the current
198 * button. This is a workaround for the flawed event model
199 * when hotkeys are sent to all widgets before the key is
200 * handled by the current widget.
202 if (parm
== '\n' && (Widget
*) h
->current
->data
== &b
->widget
)
204 button_callback (w
, WIDGET_KEY
, ' ');
208 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
210 button_callback (w
, WIDGET_KEY
, ' ');
214 if (b
->text
.hotkey
!= NULL
)
216 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
218 button_callback (w
, WIDGET_KEY
, ' ');
222 return MSG_NOT_HANDLED
;
225 if (parm
!= ' ' && parm
!= '\n')
226 return MSG_NOT_HANDLED
;
229 stop
= (*b
->callback
) (b
, b
->action
);
230 if (!b
->callback
|| stop
)
232 h
->ret_value
= b
->action
;
254 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
260 if (msg
== WIDGET_UNFOCUS
)
262 else if (msg
== WIDGET_FOCUS
)
265 widget_selectcolor (w
, b
->selected
, FALSE
);
266 widget_move (w
, 0, 0);
271 tty_print_string ("[< ");
274 tty_print_string ("[ ");
277 tty_print_string ("[");
284 draw_hotkey (w
, b
->text
, b
->selected
);
289 tty_print_string (" >]");
292 tty_print_string (" ]");
295 tty_print_string ("]");
301 release_hotkey (b
->text
);
305 return default_proc (msg
, parm
);
310 button_event (Gpm_Event
* event
, void *data
)
314 if (event
->type
& (GPM_DOWN
| GPM_UP
))
316 Dlg_head
*h
= b
->widget
.owner
;
317 dlg_select_widget (b
);
318 if (event
->type
& GPM_UP
)
320 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
321 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
329 button_get_len (const WButton
* b
)
331 int ret
= hotkey_width (b
->text
);
351 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
353 WButton
*b
= g_new (WButton
, 1);
357 b
->text
= parse_hotkey (text
);
359 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
362 b
->callback
= callback
;
363 widget_want_hotkey (b
->widget
, 1);
364 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
370 button_get_text (const WButton
* b
)
372 if (b
->text
.hotkey
!= NULL
)
373 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
375 return g_strdup (b
->text
.start
);
379 button_set_text (WButton
* b
, const char *text
)
381 release_hotkey (b
->text
);
382 b
->text
= parse_hotkey (text
);
383 b
->widget
.cols
= button_get_len (b
);
384 dlg_redraw (b
->widget
.owner
);
388 /* Radio button widget */
389 static int radio_event (Gpm_Event
* event
, void *);
392 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
394 WRadio
*r
= (WRadio
*) w
;
396 Dlg_head
*h
= r
->widget
.owner
;
402 int lp
= g_ascii_tolower ((gchar
) parm
);
404 for (i
= 0; i
< r
->count
; i
++)
406 if (r
->texts
[i
].hotkey
!= NULL
)
408 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
415 radio_callback (w
, WIDGET_KEY
, ' ');
420 return MSG_NOT_HANDLED
;
427 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
428 radio_callback (w
, WIDGET_FOCUS
, ' ');
438 return MSG_NOT_HANDLED
;
442 if (r
->count
- 1 > r
->pos
)
448 return MSG_NOT_HANDLED
;
451 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
452 radio_callback (w
, WIDGET_FOCUS
, ' ');
453 widget_move (&r
->widget
, r
->pos
, 1);
459 for (i
= 0; i
< r
->count
; i
++)
461 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
462 widget_selectcolor (w
, focused
, FALSE
);
463 widget_move (&r
->widget
, i
, 0);
464 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
465 draw_hotkey (w
, r
->texts
[i
], focused
);
470 for (i
= 0; i
< r
->count
; i
++)
472 release_hotkey (r
->texts
[i
]);
478 return default_proc (msg
, parm
);
483 radio_event (Gpm_Event
* event
, void *data
)
488 if (event
->type
& (GPM_DOWN
| GPM_UP
))
490 Dlg_head
*h
= r
->widget
.owner
;
492 r
->pos
= event
->y
- 1;
493 dlg_select_widget (r
);
494 if (event
->type
& GPM_UP
)
496 radio_callback (w
, WIDGET_KEY
, ' ');
497 radio_callback (w
, WIDGET_FOCUS
, 0);
498 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
506 radio_new (int y
, int x
, int count
, const char **texts
)
508 WRadio
*result
= g_new (WRadio
, 1);
511 /* Compute the longest string */
512 result
->texts
= g_new (struct hotkey_t
, count
);
515 for (i
= 0; i
< count
; i
++)
517 result
->texts
[i
] = parse_hotkey (texts
[i
]);
518 m
= hotkey_width (result
->texts
[i
]);
523 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
527 result
->count
= count
;
528 widget_want_hotkey (result
->widget
, 1);
534 /* Checkbutton widget */
536 static int check_event (Gpm_Event
* event
, void *);
539 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
541 WCheck
*c
= (WCheck
*) w
;
542 Dlg_head
*h
= c
->widget
.owner
;
547 if (c
->text
.hotkey
!= NULL
)
549 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
552 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
556 return MSG_NOT_HANDLED
;
560 return MSG_NOT_HANDLED
;
562 c
->state
^= C_CHANGE
;
563 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
564 check_callback (w
, WIDGET_FOCUS
, ' ');
568 widget_move (&c
->widget
, 0, 1);
574 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
575 widget_move (&c
->widget
, 0, 0);
576 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
577 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
581 release_hotkey (c
->text
);
585 return default_proc (msg
, parm
);
590 check_event (Gpm_Event
* event
, void *data
)
595 if (event
->type
& (GPM_DOWN
| GPM_UP
))
597 Dlg_head
*h
= c
->widget
.owner
;
599 dlg_select_widget (c
);
600 if (event
->type
& GPM_UP
)
602 check_callback (w
, WIDGET_KEY
, ' ');
603 check_callback (w
, WIDGET_FOCUS
, 0);
604 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
612 check_new (int y
, int x
, int state
, const char *text
)
614 WCheck
*c
= g_new (WCheck
, 1);
616 c
->text
= parse_hotkey (text
);
618 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
619 c
->state
= state
? C_BOOL
: 0;
620 widget_want_hotkey (c
->widget
, 1);
626 save_text_to_clip_file (const char *text
)
633 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
634 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
635 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
641 str_len
= strlen (text
);
642 ret
= mc_write (file
, (char *) text
, str_len
);
644 return ret
== (ssize_t
) str_len
;
648 load_text_from_clip_file (char **text
)
653 gboolean first
= TRUE
;
655 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
656 f
= fopen (fname
, "r");
664 while (fgets (buf
, sizeof (buf
), f
))
671 if (buf
[len
- 1] == '\n')
677 *text
= g_strdup (buf
);
681 /* remove \n on EOL */
684 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
693 return (*text
!= NULL
);
697 panel_save_curent_file_to_clip_file (void)
701 if (current_panel
->marked
== 0)
702 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
706 gboolean first
= TRUE
;
709 for (i
= 0; i
< current_panel
->count
; i
++)
710 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
711 { /* Skip the unmarked ones */
714 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
719 /* Add empty lines after the file */
723 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
731 res
= save_text_to_clip_file (flist
);
741 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
743 WLabel
*l
= (WLabel
*) w
;
744 Dlg_head
*h
= l
->widget
.owner
;
751 /* We don't want to get the focus */
753 return MSG_NOT_HANDLED
;
757 char *p
= l
->text
, *q
, c
= 0;
759 gboolean disabled
= (w
->options
& W_DISABLED
) != 0;
765 tty_setcolor (disabled
? DISABLED_COLOR
: DEFAULT_COLOR
);
767 tty_setcolor (disabled
? DISABLED_COLOR
: DLG_NORMALC (h
));
771 q
= strchr (p
, '\n');
778 widget_move (&l
->widget
, y
, 0);
779 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
795 return default_proc (msg
, parm
);
800 label_set_text (WLabel
* label
, const char *text
)
802 int newcols
= label
->widget
.cols
;
805 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
806 return; /* Flickering is not nice */
808 g_free (label
->text
);
812 label
->text
= g_strdup (text
);
813 if (label
->auto_adjust_cols
)
815 str_msg_term_size (text
, &newlines
, &newcols
);
816 if (newcols
> label
->widget
.cols
)
817 label
->widget
.cols
= newcols
;
818 if (newlines
> label
->widget
.lines
)
819 label
->widget
.lines
= newlines
;
825 if (label
->widget
.owner
)
826 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
828 if (newcols
< label
->widget
.cols
)
829 label
->widget
.cols
= newcols
;
833 label_new (int y
, int x
, const char *text
)
840 str_msg_term_size (text
, &lines
, &cols
);
842 l
= g_new (WLabel
, 1);
843 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
844 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
845 l
->auto_adjust_cols
= 1;
847 widget_want_cursor (l
->widget
, 0);
852 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
854 WHLine
*l
= (WHLine
*) w
;
855 Dlg_head
*h
= l
->widget
.owner
;
861 if (l
->auto_adjust_cols
)
863 if (((w
->owner
->flags
& DLG_COMPACT
) != 0))
866 w
->cols
= w
->owner
->cols
;
870 w
->x
= w
->owner
->x
+ 1;
871 w
->cols
= w
->owner
->cols
- 2;
876 /* We don't want to get the focus */
877 return MSG_NOT_HANDLED
;
881 tty_setcolor (DEFAULT_COLOR
);
883 tty_setcolor (DLG_NORMALC (h
));
885 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
887 if (l
->auto_adjust_cols
)
889 widget_move (w
, 0, 0);
890 tty_print_alt_char (ACS_LTEE
, FALSE
);
891 widget_move (w
, 0, w
->cols
- 1);
892 tty_print_alt_char (ACS_RTEE
, FALSE
);
897 return default_proc (msg
, parm
);
903 hline_new (int y
, int x
, int width
)
909 l
= g_new (WHLine
, 1);
910 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
911 l
->auto_adjust_cols
= (width
< 0);
912 l
->transparent
= FALSE
;
913 widget_want_cursor (l
->widget
, 0);
917 /* Gauge widget (progress indicator) */
918 /* Currently width is hardcoded here for text mode */
922 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
924 WGauge
*g
= (WGauge
*) w
;
925 Dlg_head
*h
= g
->widget
.owner
;
927 if (msg
== WIDGET_INIT
)
930 /* We don't want to get the focus */
931 if (msg
== WIDGET_FOCUS
)
932 return MSG_NOT_HANDLED
;
934 if (msg
== WIDGET_DRAW
)
936 widget_move (&g
->widget
, 0, 0);
937 tty_setcolor (DLG_NORMALC (h
));
939 tty_printf ("%*s", gauge_len
, "");
942 int percentage
, columns
;
943 long total
= g
->max
, done
= g
->current
;
945 if (total
<= 0 || done
< 0)
952 while (total
> 65535)
957 percentage
= (200 * done
/ total
+ 1) / 2;
958 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
959 tty_print_char ('[');
960 if (g
->from_left_to_right
)
962 tty_setcolor (GAUGE_COLOR
);
963 tty_printf ("%*s", (int) columns
, "");
964 tty_setcolor (DLG_NORMALC (h
));
965 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
969 tty_setcolor (DLG_NORMALC (h
));
970 tty_printf ("%*s", gauge_len
- columns
- 7, "");
971 tty_setcolor (GAUGE_COLOR
);
972 tty_printf ("%*s", columns
, "");
973 tty_setcolor (DLG_NORMALC (h
));
974 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
980 return default_proc (msg
, parm
);
984 gauge_set_value (WGauge
* g
, int max
, int current
)
986 if (g
->current
== current
&& g
->max
== max
)
987 return; /* Do not flicker */
989 max
= 1; /* I do not like division by zero :) */
991 g
->current
= current
;
993 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
997 gauge_show (WGauge
* g
, int shown
)
999 if (g
->shown
== shown
)
1002 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
1006 gauge_new (int y
, int x
, int shown
, int max
, int current
)
1008 WGauge
*g
= g_new (WGauge
, 1);
1010 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
1013 max
= 1; /* I do not like division by zero :) */
1015 g
->current
= current
;
1016 g
->from_left_to_right
= TRUE
;
1017 widget_want_cursor (g
->widget
, 0);
1024 /* {{{ history button */
1026 #define LARGE_HISTORY_BUTTON 1
1028 #ifdef LARGE_HISTORY_BUTTON
1029 # define HISTORY_BUTTON_WIDTH 3
1031 # define HISTORY_BUTTON_WIDTH 1
1034 #define should_show_history_button(in) \
1035 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1038 draw_history_button (WInput
* in
)
1041 gboolean disabled
= (((Widget
*) in
)->options
& W_DISABLED
) != 0;
1043 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1044 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1045 #ifdef LARGE_HISTORY_BUTTON
1048 h
= in
->widget
.owner
;
1049 tty_setcolor (disabled
? DISABLED_COLOR
: NORMAL_COLOR
);
1050 tty_print_string ("[ ]");
1051 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1052 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1056 tty_setcolor (disabled
? DISABLED_COLOR
: MARKED_COLOR
);
1061 /* }}} history button */
1064 /* Input widgets now have a global kill ring */
1065 /* Pointer to killed data */
1066 static char *kill_buffer
= NULL
;
1069 input_set_markers (WInput
* in
, long m1
)
1075 input_mark_cmd (WInput
* in
, gboolean mark
)
1079 in
->highlight
= FALSE
;
1080 input_set_markers (in
, 0);
1084 in
->highlight
= TRUE
;
1085 input_set_markers (in
, in
->point
);
1090 input_eval_marks (WInput
* in
, long *start_mark
, long *end_mark
)
1094 *start_mark
= min (in
->mark
, in
->point
);
1095 *end_mark
= max (in
->mark
, in
->point
);
1100 *start_mark
= *end_mark
= 0;
1106 delete_region (WInput
* in
, int x_first
, int x_last
)
1108 int first
= min (x_first
, x_last
);
1109 int last
= max (x_first
, x_last
);
1112 input_mark_cmd (in
, FALSE
);
1114 last
= str_offset_to_pos (in
->buffer
, last
);
1115 first
= str_offset_to_pos (in
->buffer
, first
);
1116 len
= strlen (&in
->buffer
[last
]) + 1;
1117 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1123 update_input (WInput
* in
, int clear_first
)
1125 int has_history
= 0;
1127 int buf_len
= str_length (in
->buffer
);
1131 if (should_show_history_button (in
))
1132 has_history
= HISTORY_BUTTON_WIDTH
;
1134 if (in
->disable_update
)
1137 pw
= str_term_width2 (in
->buffer
, in
->point
);
1139 /* Make the point visible */
1140 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1143 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1144 if (in
->term_first_shown
< 0)
1145 in
->term_first_shown
= 0;
1148 /* Adjust the mark */
1149 if (in
->mark
> buf_len
)
1153 draw_history_button (in
);
1155 if ((((Widget
*) in
)->options
& W_DISABLED
) != 0)
1156 tty_setcolor (DISABLED_COLOR
);
1158 tty_setcolor (in
->unchanged_color
);
1160 tty_setcolor (in
->color
);
1162 widget_move (&in
->widget
, 0, 0);
1164 if (!in
->is_password
)
1168 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1169 in
->field_width
- has_history
));
1174 if (input_eval_marks (in
, &m1
, &m2
))
1176 tty_setcolor (in
->color
);
1177 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1178 tty_print_string (cp
);
1179 tty_setcolor (in
->mark_color
);
1180 if (m1
< in
->term_first_shown
)
1182 widget_move (&in
->widget
, 0, 0);
1183 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
, m2
- in
->term_first_shown
));
1188 widget_move (&in
->widget
, 0, m1
- in
->term_first_shown
);
1189 sel_width
= min (m2
- m1
, (in
->field_width
- has_history
) - (str_term_width2 (in
->buffer
, m1
) - in
->term_first_shown
));
1190 tty_print_string (str_term_substring (in
->buffer
, m1
, sel_width
));
1197 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1198 for (i
= 0; i
< in
->field_width
- has_history
; i
++)
1202 tty_setcolor (in
->color
);
1203 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1206 str_cnext_char (&cp
);
1215 winput_set_origin (WInput
* in
, int x
, int field_width
)
1218 in
->field_width
= in
->widget
.cols
= field_width
;
1219 update_input (in
, 0);
1222 /* {{{ history saving and loading */
1224 int num_history_items_recorded
= 60;
1227 This loads and saves the history of an input line to and from the
1228 widget. It is called with the widgets history name on creation of the
1229 widget, and returns the GList list. It stores histories in the file
1230 ~/.mc/history in using the profile code.
1232 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1233 function) then input_new assigns the default text to be the last text
1234 entered, or "" if not found.
1238 history_get (const char *input_name
)
1245 size_t keys_num
= 0;
1248 if (num_history_items_recorded
== 0) /* this is how to disable */
1250 if ((input_name
== NULL
) || (*input_name
== '\0'))
1253 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1254 cfg
= mc_config_init (profile
);
1256 /* get number of keys */
1257 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1260 for (i
= 0; i
< keys_num
; i
++)
1263 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1264 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1266 if (this_entry
!= NULL
)
1267 hist
= list_append_unique (hist
, this_entry
);
1270 mc_config_deinit (cfg
);
1273 /* return pointer to the last entry in the list */
1274 return g_list_last (hist
);
1278 history_put (const char *input_name
, GList
* h
)
1284 if (num_history_items_recorded
== 0) /* this is how to disable */
1286 if ((input_name
== NULL
) || (*input_name
== '\0'))
1291 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1293 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1297 /* Make sure the history is only readable by the user */
1298 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1304 /* go to end of list */
1305 h
= g_list_last (h
);
1307 /* go back 60 places */
1308 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1309 h
= g_list_previous (h
);
1311 cfg
= mc_config_init (profile
);
1313 if (input_name
!= NULL
)
1314 mc_config_del_group (cfg
, input_name
);
1316 /* dump history into profile */
1317 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1319 char *text
= (char *) h
->data
;
1321 /* We shouldn't have null entries, but let's be sure */
1325 g_snprintf (key
, sizeof (key
), "%d", i
++);
1326 mc_config_set_string (cfg
, input_name
, key
, text
);
1330 mc_config_save_file (cfg
, NULL
);
1331 mc_config_deinit (cfg
);
1335 /* }}} history saving and loading */
1338 /* {{{ history display */
1343 return _("History");
1354 dlg_hist_reposition (Dlg_head
* dlg_head
)
1356 dlg_hist_data
*data
;
1357 int x
= 0, y
, he
, wi
;
1360 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1361 return MSG_NOT_HANDLED
;
1363 data
= (dlg_hist_data
*) dlg_head
->data
;
1365 y
= data
->widget
->y
;
1366 he
= data
->count
+ 2;
1368 if (he
<= y
|| y
> (LINES
- 6))
1370 he
= min (he
, y
- 1);
1376 he
= min (he
, LINES
- y
);
1379 if (data
->widget
->x
> 2)
1380 x
= data
->widget
->x
- 2;
1382 wi
= data
->maxlen
+ 4;
1384 if ((wi
+ x
) > COLS
)
1386 wi
= min (wi
, COLS
);
1390 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1396 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1401 return dlg_hist_reposition (h
);
1404 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1409 show_hist (GList
** history
, Widget
* widget
)
1411 GList
*z
, *hlist
= NULL
, *hi
;
1412 size_t maxlen
, i
, count
= 0;
1414 Dlg_head
*query_dlg
;
1415 WListbox
*query_list
;
1416 dlg_hist_data hist_data
;
1418 if (*history
== NULL
)
1421 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1423 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1427 i
= str_term_width1 ((char *) z
->data
);
1428 maxlen
= max (maxlen
, i
);
1431 entry
= g_new0 (WLEntry
, 1);
1432 /* history is being reverted here */
1433 entry
->text
= g_strdup ((char *) z
->data
);
1434 hlist
= g_list_prepend (hlist
, entry
);
1437 hist_data
.widget
= widget
;
1438 hist_data
.count
= count
;
1439 hist_data
.maxlen
= maxlen
;
1442 create_dlg (TRUE
, 0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1443 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1444 query_dlg
->data
= &hist_data
;
1446 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1448 /* this call makes list stick to all sides of dialog, effectively make
1449 it be resized with dialog */
1450 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1452 /* to avoid diplicating of (calculating sizes in two places)
1453 code, call dlg_hist_callback function here, to set dialog and
1455 The main idea - create 4x4 dialog and add 2x2 list in
1456 center of it, and let dialog function resize it to needed
1458 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1460 if (query_dlg
->y
< widget
->y
)
1462 /* draw list entries from bottom upto top */
1463 listbox_set_list (query_list
, hlist
);
1464 listbox_select_last (query_list
);
1468 /* draw list entries from top downto bottom */
1469 /* revert history direction */
1470 hlist
= g_list_reverse (hlist
);
1471 listbox_set_list (query_list
, hlist
);
1474 if (run_dlg (query_dlg
) != B_CANCEL
)
1478 listbox_get_current (query_list
, &q
, NULL
);
1483 /* get modified history from dialog */
1485 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1489 entry
= (WLEntry
*) hi
->data
;
1490 /* history is being reverted here again */
1491 z
= g_list_prepend (z
, entry
->text
);
1495 /* restore history direction */
1496 if (query_dlg
->y
< widget
->y
)
1497 z
= g_list_reverse (z
);
1499 destroy_dlg (query_dlg
);
1501 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1502 g_list_free (*history
);
1503 *history
= g_list_last (z
);
1509 do_show_hist (WInput
* in
)
1513 r
= show_hist (&in
->history
, &in
->widget
);
1516 assign_text (in
, r
);
1521 /* }}} history display */
1524 input_destroy (WInput
* in
)
1528 fprintf (stderr
, "Internal error: null Input *\n");
1529 exit (EXIT_FAILURE
);
1534 if (in
->history
!= NULL
)
1536 if (!in
->is_password
&& (((Widget
*) in
)->owner
->ret_value
!= B_CANCEL
))
1537 history_put (in
->history_name
, in
->history
);
1539 in
->history
= g_list_first (in
->history
);
1540 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1541 g_list_free (in
->history
);
1544 g_free (in
->buffer
);
1545 free_completions (in
);
1546 g_free (in
->history_name
);
1548 g_free (kill_buffer
);
1553 input_disable_update (WInput
* in
)
1555 in
->disable_update
++;
1559 input_enable_update (WInput
* in
)
1561 in
->disable_update
--;
1562 update_input (in
, 0);
1567 push_history (WInput
* in
, const char *text
)
1569 /* input widget where urls with passwords are entered without any
1571 const char *password_input_fields
[] = {
1572 " Link to a remote machine ",
1574 " SMB link to machine "
1576 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1586 for (i
= 0; i
< ELEMENTS
; i
++)
1587 password_input_fields
[i
] = _(password_input_fields
[i
]);
1590 t
= g_strstrip (g_strdup (text
));
1593 t
= g_strdup (empty
? "" : text
);
1595 if (in
->history_name
!= NULL
)
1597 /* FIXME: It is the strange code. Rewrite is needed. */
1599 const char *p
= in
->history_name
+ 3;
1601 for (i
= 0; i
< ELEMENTS
; i
++)
1602 if (strcmp (p
, password_input_fields
[i
]) == 0)
1605 strip_password (t
, i
>= ELEMENTS
);
1608 in
->history
= list_append_unique (in
->history
, t
);
1612 /* Cleans the input line and adds the current text to the history */
1614 new_input (WInput
* in
)
1616 push_history (in
, in
->buffer
);
1618 in
->buffer
[0] = '\0';
1622 in
->highlight
= FALSE
;
1623 free_completions (in
);
1624 update_input (in
, 0);
1628 move_buffer_backward (WInput
* in
, int start
, int end
)
1631 int str_len
= str_length (in
->buffer
);
1632 if (start
>= str_len
|| end
> str_len
+ 1)
1635 pos
= str_offset_to_pos (in
->buffer
, start
);
1636 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1638 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1639 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1643 insert_char (WInput
* in
, int c_code
)
1651 if (input_eval_marks (in
, &m1
, &m2
))
1652 delete_region (in
, m1
, m2
);
1655 return MSG_NOT_HANDLED
;
1657 if (in
->charpoint
>= MB_LEN_MAX
)
1660 in
->charbuf
[in
->charpoint
] = c_code
;
1663 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1667 in
->charpoint
= 0; /* broken multibyte char, skip */
1672 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1674 /* Expand the buffer */
1675 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1676 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1680 in
->current_max_size
= new_length
;
1684 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1686 /* bytes from begin */
1687 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1689 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1691 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1692 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1694 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1703 beginning_of_line (WInput
* in
)
1710 end_of_line (WInput
* in
)
1712 in
->point
= str_length (in
->buffer
);
1717 backward_char (WInput
* in
)
1719 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1723 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1729 forward_char (WInput
* in
)
1731 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1734 in
->point
+= str_cnext_noncomb_char (&act
);
1740 forward_word (WInput
* in
)
1742 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1744 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1746 str_cnext_char (&p
);
1749 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1751 str_cnext_char (&p
);
1757 backward_word (WInput
* in
)
1762 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1763 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1765 while (p
!= in
->buffer
)
1768 str_cprev_char (&p
);
1769 if (!str_isspace (p
) && !str_ispunct (p
))
1776 while (p
!= in
->buffer
)
1778 str_cprev_char (&p
);
1779 if (str_isspace (p
) || str_ispunct (p
))
1787 key_left (WInput
* in
)
1793 key_ctrl_left (WInput
* in
)
1799 key_right (WInput
* in
)
1805 key_ctrl_right (WInput
* in
)
1810 backward_delete (WInput
* in
)
1812 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1818 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1819 move_buffer_backward (in
, start
, in
->point
);
1826 delete_char (WInput
* in
)
1828 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1829 int end
= in
->point
;
1831 end
+= str_cnext_noncomb_char (&act
);
1833 move_buffer_backward (in
, in
->point
, end
);
1839 copy_region (WInput
* in
, int x_first
, int x_last
)
1841 int first
= min (x_first
, x_last
);
1842 int last
= max (x_first
, x_last
);
1846 /* Copy selected files to clipboard */
1847 panel_save_curent_file_to_clip_file ();
1848 /* try use external clipboard utility */
1849 copy_file_to_ext_clip ();
1853 g_free (kill_buffer
);
1855 first
= str_offset_to_pos (in
->buffer
, first
);
1856 last
= str_offset_to_pos (in
->buffer
, last
);
1858 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1860 save_text_to_clip_file (kill_buffer
);
1861 /* try use external clipboard utility */
1862 copy_file_to_ext_clip ();
1866 kill_word (WInput
* in
)
1868 int old_point
= in
->point
;
1872 new_point
= in
->point
;
1873 in
->point
= old_point
;
1875 copy_region (in
, old_point
, new_point
);
1876 delete_region (in
, old_point
, new_point
);
1883 back_kill_word (WInput
* in
)
1885 int old_point
= in
->point
;
1889 new_point
= in
->point
;
1890 in
->point
= old_point
;
1892 copy_region (in
, old_point
, new_point
);
1893 delete_region (in
, old_point
, new_point
);
1898 set_mark (WInput
* in
)
1900 input_mark_cmd (in
, TRUE
);
1904 kill_save (WInput
* in
)
1906 copy_region (in
, in
->mark
, in
->point
);
1910 kill_region (WInput
* in
)
1913 delete_region (in
, in
->point
, in
->mark
);
1917 clear_region (WInput
* in
)
1919 delete_region (in
, in
->point
, in
->mark
);
1925 if (kill_buffer
!= NULL
)
1930 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1931 insert_char (in
, *p
);
1937 kill_line (WInput
* in
)
1939 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1940 g_free (kill_buffer
);
1941 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1942 in
->buffer
[chp
] = '\0';
1947 ins_from_clip (WInput
* in
)
1951 /* try use external clipboard utility */
1952 paste_to_file_from_ext_clip ();
1954 if (load_text_from_clip_file (&p
))
1958 for (pp
= p
; *pp
!= '\0'; pp
++)
1959 insert_char (in
, *pp
);
1966 assign_text (WInput
* in
, const char *text
)
1968 free_completions (in
);
1969 g_free (in
->buffer
);
1970 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1971 in
->current_max_size
= strlen (in
->buffer
) + 1;
1972 in
->point
= str_length (in
->buffer
);
1979 hist_prev (WInput
* in
)
1987 push_history (in
, in
->buffer
);
1989 prev
= g_list_previous (in
->history
);
1993 assign_text (in
, (char *) prev
->data
);
1999 hist_next (WInput
* in
)
2003 push_history (in
, in
->buffer
);
2004 assign_text (in
, "");
2011 if (!in
->history
->next
)
2013 assign_text (in
, "");
2017 in
->history
= g_list_next (in
->history
);
2018 assign_text (in
, (char *) in
->history
->data
);
2023 port_region_marked_for_delete (WInput
* in
)
2025 in
->buffer
[0] = '\0';
2032 input_execute_cmd (WInput
* in
, unsigned long command
)
2034 cb_ret_t res
= MSG_HANDLED
;
2036 /* a highlight command like shift-arrow */
2037 if (command
== CK_InputLeftHighlight
||
2038 command
== CK_InputRightHighlight
||
2039 command
== CK_InputWordLeftHighlight
||
2040 command
== CK_InputWordRightHighlight
||
2041 command
== CK_InputBolHighlight
||
2042 command
== CK_InputEolHighlight
)
2046 input_mark_cmd (in
, FALSE
); /* clear */
2047 input_mark_cmd (in
, TRUE
); /* marking on */
2053 case CK_InputForwardWord
:
2054 case CK_InputBackwardWord
:
2055 case CK_InputForwardChar
:
2056 case CK_InputBackwardChar
:
2058 input_mark_cmd (in
, FALSE
);
2064 case CK_InputBolHighlight
:
2065 beginning_of_line (in
);
2068 case CK_InputEolHighlight
:
2071 case CK_InputMoveLeft
:
2072 case CK_InputLeftHighlight
:
2075 case CK_InputWordLeft
:
2076 case CK_InputWordLeftHighlight
:
2079 case CK_InputMoveRight
:
2080 case CK_InputRightHighlight
:
2083 case CK_InputWordRight
:
2084 case CK_InputWordRightHighlight
:
2085 key_ctrl_right (in
);
2087 case CK_InputBackwardChar
:
2090 case CK_InputBackwardWord
:
2093 case CK_InputForwardChar
:
2096 case CK_InputForwardWord
:
2099 case CK_InputBackwardDelete
:
2103 if (input_eval_marks (in
, &m1
, &m2
))
2104 delete_region (in
, m1
, m2
);
2108 backward_delete (in
);
2111 case CK_InputDeleteChar
:
2113 port_region_marked_for_delete (in
);
2114 else if (in
->highlight
)
2117 if (input_eval_marks (in
, &m1
, &m2
))
2118 delete_region (in
, m1
, m2
);
2123 case CK_InputKillWord
:
2126 case CK_InputBackwardKillWord
:
2127 back_kill_word (in
);
2129 case CK_InputSetMark
:
2132 case CK_InputKillRegion
:
2135 case CK_InputClearLine
:
2138 case CK_InputKillSave
:
2147 case CK_InputKillLine
:
2150 case CK_InputHistoryPrev
:
2153 case CK_InputHistoryNext
:
2156 case CK_InputHistoryShow
:
2159 case CK_InputComplete
:
2163 res
= MSG_NOT_HANDLED
;
2166 if (command
!= CK_InputLeftHighlight
&&
2167 command
!= CK_InputRightHighlight
&&
2168 command
!= CK_InputWordLeftHighlight
&&
2169 command
!= CK_InputWordRightHighlight
&&
2170 command
!= CK_InputBolHighlight
&&
2171 command
!= CK_InputEolHighlight
)
2173 in
->highlight
= FALSE
;
2179 /* This function is a test for a special input key used in complete.c */
2180 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2181 and 2 if it is a complete key */
2183 is_in_input_map (WInput
* in
, int key
)
2186 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2187 if (key
== input_map
[i
].key
)
2189 input_execute_cmd (in
, input_map
[i
].command
);
2190 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2196 handle_char (WInput
* in
, int key
)
2201 v
= MSG_NOT_HANDLED
;
2205 free_completions (in
);
2206 v
= insert_char (in
, key
);
2207 update_input (in
, 1);
2211 for (i
= 0; input_map
[i
].key
; i
++)
2213 if (key
== input_map
[i
].key
)
2215 if (input_map
[i
].command
!= CK_InputComplete
)
2216 free_completions (in
);
2217 input_execute_cmd (in
, input_map
[i
].command
);
2218 update_input (in
, 1);
2223 if (input_map
[i
].command
== CK_Ignore_Key
)
2226 return MSG_NOT_HANDLED
;
2228 port_region_marked_for_delete (in
);
2229 free_completions (in
);
2230 v
= insert_char (in
, key
);
2232 update_input (in
, 1);
2236 /* Inserts text in input line */
2238 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2240 input_disable_update (in
);
2241 while (*text
!= '\0')
2242 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2243 if (insert_extra_space
)
2244 handle_char (in
, ' ');
2245 input_enable_update (in
);
2246 update_input (in
, 1);
2250 input_set_point (WInput
* in
, int pos
)
2252 int max_pos
= str_length (in
->buffer
);
2256 if (pos
!= in
->point
)
2257 free_completions (in
);
2260 update_input (in
, 1);
2264 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2266 WInput
*in
= (WInput
*) w
;
2272 if (parm
== XCTRL ('q'))
2275 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2280 /* Keys we want others to handle */
2281 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2282 || parm
== KEY_F (10) || parm
== '\n')
2283 return MSG_NOT_HANDLED
;
2285 /* When pasting multiline text, insert literal Enter */
2286 if ((parm
& ~KEY_M_MASK
) == '\n')
2289 v
= handle_char (in
, '\n');
2294 return handle_char (in
, parm
);
2296 case WIDGET_COMMAND
:
2297 return input_execute_cmd (in
, parm
);
2300 case WIDGET_UNFOCUS
:
2302 update_input (in
, 0);
2306 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2307 - in
->term_first_shown
);
2310 case WIDGET_DESTROY
:
2315 return default_proc (msg
, parm
);
2320 input_event (Gpm_Event
* event
, void *data
)
2324 if (event
->type
& GPM_DOWN
)
2327 input_mark_cmd (in
, FALSE
);
2329 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2331 dlg_select_widget (in
);
2333 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2334 && should_show_history_button (in
))
2340 in
->point
= str_length (in
->buffer
);
2341 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2342 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2344 update_input (in
, 1);
2346 /* A lone up mustn't do anything */
2347 if (in
->highlight
&& event
->type
& (GPM_UP
| GPM_DRAG
))
2350 if (!(event
->type
& GPM_DRAG
))
2351 input_mark_cmd (in
, TRUE
);
2357 input_new (int y
, int x
, int *input_colors
, int width
, const char *def_text
,
2358 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2360 WInput
*in
= g_new (WInput
, 1);
2361 size_t initial_buffer_len
;
2363 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2366 in
->history_name
= NULL
;
2368 if ((histname
!= NULL
) && (*histname
!= '\0'))
2370 in
->history_name
= g_strdup (histname
);
2371 in
->history
= history_get (histname
);
2374 if (def_text
== NULL
)
2376 else if (def_text
== INPUT_LAST_TEXT
)
2378 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2379 def_text
= (char *) in
->history
->data
;
2384 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2385 in
->widget
.options
|= W_IS_INPUT
;
2386 in
->completions
= NULL
;
2387 in
->completion_flags
= completion_flags
;
2388 in
->current_max_size
= initial_buffer_len
;
2389 in
->buffer
= g_new (char, initial_buffer_len
);
2390 in
->color
= input_colors
[0];
2391 in
->unchanged_color
= input_colors
[1];
2392 in
->mark_color
= input_colors
[2];
2393 in
->field_width
= width
;
2395 in
->highlight
= FALSE
;
2396 in
->term_first_shown
= 0;
2397 in
->disable_update
= 0;
2400 in
->is_password
= 0;
2402 strcpy (in
->buffer
, def_text
);
2403 in
->point
= str_length (in
->buffer
);
2410 /* Listbox widget */
2412 /* Should draw the scrollbar, but currently draws only
2413 * indications that there is more information
2417 listbox_entry_free (void *data
)
2425 listbox_drawscroll (WListbox
* l
)
2427 const int max_line
= l
->widget
.lines
- 1;
2431 /* Are we at the top? */
2432 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2434 tty_print_one_vline (TRUE
);
2436 tty_print_char ('^');
2438 /* Are we at the bottom? */
2439 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2440 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2441 tty_print_one_vline (TRUE
);
2443 tty_print_char ('v');
2445 /* Now draw the nice relative pointer */
2447 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2449 for (i
= 1; i
< max_line
; i
++)
2451 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2453 tty_print_one_vline (TRUE
);
2455 tty_print_char ('*');
2460 listbox_draw (WListbox
* l
, gboolean focused
)
2462 const Dlg_head
*h
= l
->widget
.owner
;
2463 const gboolean disabled
= (((Widget
*) l
)->options
& W_DISABLED
) != 0;
2464 const int normalc
= disabled
? DISABLED_COLOR
: DLG_NORMALC (h
);
2465 int selc
= disabled
? DISABLED_COLOR
: focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2472 le
= g_list_nth (l
->list
, l
->top
);
2473 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2474 pos
= (le
== NULL
) ? 0 : l
->top
;
2476 for (i
= 0; i
< l
->widget
.lines
; i
++)
2480 /* Display the entry */
2481 if (pos
== l
->pos
&& sel_line
== -1)
2484 tty_setcolor (selc
);
2487 tty_setcolor (normalc
);
2489 widget_move (&l
->widget
, i
, 1);
2491 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2495 WLEntry
*e
= (WLEntry
*) le
->data
;
2497 le
= g_list_next (le
);
2501 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2504 l
->cursor_y
= sel_line
;
2506 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2508 tty_setcolor (normalc
);
2509 listbox_drawscroll (l
);
2514 listbox_check_hotkey (WListbox
* l
, int key
)
2519 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2521 WLEntry
*e
= (WLEntry
*) le
->data
;
2523 if (e
->hotkey
== key
)
2530 /* Selects the last entry and scrolls the list to the bottom */
2532 listbox_select_last (WListbox
* l
)
2534 l
->pos
= l
->count
- 1;
2535 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2538 /* Selects the first entry and scrolls the list to the top */
2540 listbox_select_first (WListbox
* l
)
2542 l
->pos
= l
->top
= 0;
2546 listbox_set_list (WListbox
* l
, GList
* list
)
2548 listbox_remove_list (l
);
2553 l
->top
= l
->pos
= 0;
2554 l
->count
= g_list_length (list
);
2559 listbox_remove_list (WListbox
* l
)
2561 if ((l
!= NULL
) && (l
->count
!= 0))
2563 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2564 g_list_free (l
->list
);
2566 l
->count
= l
->pos
= l
->top
= 0;
2571 listbox_remove_current (WListbox
* l
)
2573 if ((l
!= NULL
) && (l
->count
!= 0))
2577 current
= g_list_nth (l
->list
, l
->pos
);
2578 l
->list
= g_list_remove_link (l
->list
, current
);
2579 listbox_entry_free ((WLEntry
*) current
->data
);
2580 g_list_free_1 (current
);
2584 l
->top
= l
->pos
= 0;
2585 else if (l
->pos
>= l
->count
)
2586 l
->pos
= l
->count
- 1;
2591 listbox_select_entry (WListbox
* l
, int dest
)
2595 gboolean top_seen
= FALSE
;
2601 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2611 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2612 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2617 /* If we are unable to find it, set decent values */
2618 l
->pos
= l
->top
= 0;
2621 /* Selects from base the pos element */
2623 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2625 int last
= l
->count
- 1;
2635 listbox_fwd (WListbox
* l
)
2637 if (l
->pos
+ 1 >= l
->count
)
2638 listbox_select_first (l
);
2640 listbox_select_entry (l
, l
->pos
+ 1);
2644 listbox_back (WListbox
* l
)
2647 listbox_select_last (l
);
2649 listbox_select_entry (l
, l
->pos
- 1);
2653 listbox_execute_cmd (WListbox
* l
, unsigned long command
)
2655 cb_ret_t ret
= MSG_HANDLED
;
2660 case CK_ListboxMoveUp
:
2663 case CK_ListboxMoveDown
:
2666 case CK_ListboxMoveHome
:
2667 listbox_select_first (l
);
2669 case CK_ListboxMoveEnd
:
2670 listbox_select_last (l
);
2672 case CK_ListboxMovePgUp
:
2673 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2676 case CK_ListboxMovePgDn
:
2677 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2680 case CK_ListboxDeleteItem
:
2683 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2684 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2686 listbox_remove_current (l
);
2687 if ((l
->top
> 0) && (is_last
|| is_more
))
2691 case CK_ListboxDeleteAll
:
2692 if (l
->deletable
&& confirm_history_cleanup
2693 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2694 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2695 _("Do you want clean this history?"),
2696 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2697 listbox_remove_list (l
);
2700 ret
= MSG_NOT_HANDLED
;
2706 /* Return MSG_HANDLED if we want a redraw */
2708 listbox_key (WListbox
* l
, int key
)
2710 unsigned long command
;
2712 if (l
->list
== NULL
)
2713 return MSG_NOT_HANDLED
;
2715 /* focus on listbox item N by '0'..'9' keys */
2716 if (key
>= '0' && key
<= '9')
2718 int oldpos
= l
->pos
;
2719 listbox_select_entry (l
, key
- '0');
2721 /* need scroll to item? */
2722 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2728 command
= lookup_keymap_command (listbox_map
, key
);
2729 if (command
== CK_Ignore_Key
)
2730 return MSG_NOT_HANDLED
;
2731 return listbox_execute_cmd (l
, command
);
2735 listbox_destroy (WListbox
* l
)
2737 listbox_remove_list (l
);
2741 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2743 WListbox
*l
= (WListbox
*) w
;
2744 Dlg_head
*h
= l
->widget
.owner
;
2756 pos
= listbox_check_hotkey (l
, parm
);
2758 return MSG_NOT_HANDLED
;
2760 listbox_select_entry (l
, pos
);
2761 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2763 if (l
->cback
!= NULL
)
2764 action
= l
->cback (l
);
2766 action
= LISTBOX_DONE
;
2768 if (action
== LISTBOX_DONE
)
2770 h
->ret_value
= B_ENTER
;
2778 ret_code
= listbox_key (l
, parm
);
2779 if (ret_code
!= MSG_NOT_HANDLED
)
2781 listbox_draw (l
, TRUE
);
2782 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2786 case WIDGET_COMMAND
:
2787 return listbox_execute_cmd (l
, parm
);
2790 widget_move (&l
->widget
, l
->cursor_y
, 0);
2791 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2795 case WIDGET_UNFOCUS
:
2797 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2800 case WIDGET_DESTROY
:
2801 listbox_destroy (l
);
2804 case WIDGET_RESIZED
:
2808 return default_proc (msg
, parm
);
2813 listbox_event (Gpm_Event
* event
, void *data
)
2818 Dlg_head
*h
= l
->widget
.owner
;
2821 if (event
->type
& GPM_DOWN
)
2822 dlg_select_widget (l
);
2824 if (l
->list
== NULL
)
2827 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2829 int ret
= MOU_REPEAT
;
2831 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2835 for (i
= -event
->y
; i
>= 0; i
--)
2837 else if (event
->y
> l
->widget
.lines
)
2838 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2840 else if (event
->buttons
& GPM_B_UP
)
2845 else if (event
->buttons
& GPM_B_DOWN
)
2851 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2853 /* We need to refresh ourselves since the dialog manager doesn't */
2854 /* know about this event */
2855 listbox_draw (l
, TRUE
);
2860 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2864 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2865 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2868 dlg_select_widget (l
);
2869 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2871 if (l
->cback
!= NULL
)
2872 action
= l
->cback (l
);
2874 action
= LISTBOX_DONE
;
2876 if (action
== LISTBOX_DONE
)
2878 h
->ret_value
= B_ENTER
;
2887 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2889 WListbox
*l
= g_new (WListbox
, 1);
2894 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2897 l
->top
= l
->pos
= 0;
2899 l
->deletable
= deletable
;
2900 l
->cback
= callback
;
2901 l
->allow_duplicates
= TRUE
;
2902 l
->scrollbar
= !tty_is_slow ();
2903 widget_want_hotkey (l
->widget
, 1);
2904 widget_want_cursor (l
->widget
, 0);
2910 listbox_entry_cmp (const void *a
, const void *b
)
2912 const WLEntry
*ea
= (const WLEntry
*) a
;
2913 const WLEntry
*eb
= (const WLEntry
*) b
;
2915 return strcmp (ea
->text
, eb
->text
);
2918 /* Listbox item adding function */
2920 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2924 case LISTBOX_APPEND_AT_END
:
2925 l
->list
= g_list_append (l
->list
, e
);
2928 case LISTBOX_APPEND_BEFORE
:
2929 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2934 case LISTBOX_APPEND_AFTER
:
2935 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2938 case LISTBOX_APPEND_SORTED
:
2939 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2950 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2957 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2960 entry
= g_new (WLEntry
, 1);
2961 entry
->text
= g_strdup (text
);
2963 entry
->hotkey
= hotkey
;
2965 listbox_append_item (l
, entry
, pos
);
2971 listbox_search_text (WListbox
* l
, const char *text
)
2978 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2980 WLEntry
*e
= (WLEntry
*) le
->data
;
2982 if (strcmp (e
->text
, text
) == 0)
2990 /* Returns the current string text as well as the associated extra data */
2992 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2998 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
3003 *string
= ok
? e
->text
: NULL
;
3006 *extra
= ok
? e
->data
: NULL
;
3010 /* ButtonBar widget */
3012 /* returns TRUE if a function has been called, FALSE otherwise. */
3014 buttonbar_call (WButtonBar
* bb
, int i
)
3016 cb_ret_t ret
= MSG_NOT_HANDLED
;
3018 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
3019 ret
= bb
->widget
.owner
->callback (bb
->widget
.owner
,
3020 (Widget
*) bb
, DLG_ACTION
,
3021 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
3025 /* calculate positions of buttons; width is never less than 7 */
3027 buttonbar_init_button_positions (WButtonBar
*bb
)
3032 if (COLS
< BUTTONBAR_LABELS_NUM
* 7)
3034 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3036 if (pos
+ 7 <= COLS
)
3039 bb
->labels
[i
].end_coord
= pos
;
3044 /* Distribute the extra width in a way that the middle vertical line
3045 (between F5 and F6) aligns with the two panels. The extra width
3046 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3049 lc_div
= COLS
/ BUTTONBAR_LABELS_NUM
;
3050 mod
= COLS
% BUTTONBAR_LABELS_NUM
;
3052 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
/ 2; i
++)
3055 if (BUTTONBAR_LABELS_NUM
/ 2 - 1 - i
< mod
/ 2)
3058 bb
->labels
[i
].end_coord
= pos
;
3061 for (; i
< BUTTONBAR_LABELS_NUM
; i
++)
3064 if (BUTTONBAR_LABELS_NUM
- 1 - i
< (mod
+ 1) / 2)
3067 bb
->labels
[i
].end_coord
= pos
;
3072 /* return width of one button */
3074 buttonbar_get_button_width (const WButtonBar
*bb
, int i
)
3077 return bb
->labels
[0].end_coord
;
3078 return bb
->labels
[i
].end_coord
- bb
->labels
[i
- 1].end_coord
;
3082 buttonbar_get_button_by_x_coord (const WButtonBar
*bb
, int x
)
3086 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3087 if (bb
->labels
[i
].end_coord
> x
)
3094 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3096 WButtonBar
*bb
= (WButtonBar
*) w
;
3103 return MSG_NOT_HANDLED
;
3106 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3107 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
3109 return MSG_NOT_HANDLED
;
3114 buttonbar_init_button_positions (bb
);
3115 widget_move (&bb
->widget
, 0, 0);
3116 tty_setcolor (DEFAULT_COLOR
);
3117 tty_printf ("%-*s", bb
->widget
.cols
, "");
3118 widget_move (&bb
->widget
, 0, 0);
3120 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3124 width
= buttonbar_get_button_width (bb
, i
);
3128 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
3129 /* don't show num of undefined button */
3130 if ((bb
->labels
[i
].text
== NULL
) || (bb
->labels
[i
].text
[0] == '\0'))
3131 tty_print_string (" ");
3133 tty_printf ("%2d", i
+ 1);
3135 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
3136 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
3137 tty_print_string (str_fit_to_term (text
, width
- 2, J_LEFT_FIT
));
3142 case WIDGET_DESTROY
:
3143 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3144 g_free (bb
->labels
[i
].text
);
3148 return default_proc (msg
, parm
);
3153 buttonbar_event (Gpm_Event
* event
, void *data
)
3155 WButtonBar
*bb
= data
;
3158 if (!(event
->type
& GPM_UP
))
3162 button
= buttonbar_get_button_by_x_coord (bb
, event
->x
- 1);
3164 buttonbar_call (bb
, button
);
3169 buttonbar_new (gboolean visible
)
3173 bb
= g_new0 (WButtonBar
, 1);
3175 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
3176 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
3177 bb
->visible
= visible
;
3178 widget_want_hotkey (bb
->widget
, 1);
3179 widget_want_cursor (bb
->widget
, 0);
3185 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
3187 g_free (bb
->labels
[lc_index
- 1].text
);
3188 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
3191 /* Find ButtonBar widget in the dialog */
3193 find_buttonbar (const Dlg_head
* h
)
3195 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
3199 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
3200 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
3202 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
3204 unsigned long command
= CK_Ignore_Key
;
3207 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
3209 if ((text
== NULL
) || (text
[0] == '\0'))
3210 set_label_text (bb
, idx
, "");
3212 set_label_text (bb
, idx
, text
);
3214 bb
->labels
[idx
- 1].command
= command
;
3215 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
3220 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
3222 bb
->visible
= visible
;
3226 buttonbar_redraw (WButtonBar
* bb
)
3229 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3233 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3235 WGroupbox
*g
= (WGroupbox
*) w
;
3243 return MSG_NOT_HANDLED
;
3247 gboolean disabled
= (w
->options
& W_DISABLED
) != 0;
3248 tty_setcolor (disabled
? DISABLED_COLOR
: COLOR_NORMAL
);
3249 draw_box (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3250 g
->widget
.x
- g
->widget
.owner
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3252 tty_setcolor (disabled
? DISABLED_COLOR
: COLOR_HOT_NORMAL
);
3253 dlg_move (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3254 g
->widget
.x
- g
->widget
.owner
->x
+ 1);
3255 tty_print_string (g
->title
);
3259 case WIDGET_DESTROY
:
3264 return default_proc (msg
, parm
);
3269 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3271 WGroupbox
*g
= g_new (WGroupbox
, 1);
3273 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3275 g
->widget
.options
&= ~W_WANT_CURSOR
;
3276 widget_want_hotkey (g
->widget
, 0);
3278 /* Strip existing spaces, add one space before and after the title */
3282 t
= g_strstrip (g_strdup (title
));
3283 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);