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
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 * \brief Source: widgets
41 #include <sys/types.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/mouse.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
49 #include "lib/mcconfig.h" /* for history loading and saving */
50 #include "lib/vfs/mc-vfs/vfs.h"
51 #include "lib/fileloc.h"
52 #include "lib/strutil.h"
58 #include "cmddef.h" /* CK_ cmd name const */
59 #include "keybind.h" /* global_keymap_t */
60 #include "panel.h" /* current_panel */
61 #include "main.h" /* confirm_history_cleanup */
63 const global_keymap_t
*input_map
;
66 widget_selectcolor (Widget
*w
, gboolean focused
, gboolean hotkey
)
68 Dlg_head
*h
= w
->parent
;
73 : DLG_HOT_NORMALC (h
))
80 parse_hotkey (const char *text
)
82 struct hotkey_t result
;
85 /* search for '&', that is not on the of text */
86 cp
= strchr (text
, '&');
87 if (cp
!= NULL
&& cp
[1] != '\0') {
88 result
.start
= g_strndup (text
, cp
- text
);
92 p
= str_cget_next_char (cp
);
93 result
.hotkey
= g_strndup (cp
, p
- cp
);
96 result
.end
= g_strdup (cp
);
98 result
.start
= g_strdup (text
);
106 release_hotkey (const struct hotkey_t hotkey
)
108 g_free (hotkey
.start
);
109 g_free (hotkey
.hotkey
);
114 hotkey_width (const struct hotkey_t hotkey
)
118 result
= str_term_width1 (hotkey
.start
);
119 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
120 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
125 draw_hotkey (Widget
*w
, const struct hotkey_t hotkey
, gboolean focused
)
127 widget_selectcolor (w
, focused
, FALSE
);
128 tty_print_string (hotkey
.start
);
130 if (hotkey
.hotkey
!= NULL
) {
131 widget_selectcolor (w
, focused
, TRUE
);
132 tty_print_string (hotkey
.hotkey
);
133 widget_selectcolor (w
, focused
, FALSE
);
136 if (hotkey
.end
!= NULL
)
137 tty_print_string (hotkey
.end
);
141 /* Default callback for widgets */
143 default_proc (widget_msg_t msg
, int parm
)
158 return MSG_NOT_HANDLED
;
162 static int button_event (Gpm_Event
*event
, void *);
167 button_callback (Widget
*w
, widget_msg_t msg
, int parm
)
169 WButton
*b
= (WButton
*) w
;
172 Dlg_head
*h
= b
->widget
.parent
;
177 * Don't let the default button steal Enter from the current
178 * button. This is a workaround for the flawed event model
179 * when hotkeys are sent to all widgets before the key is
180 * handled by the current widget.
182 if (parm
== '\n' && h
->current
== &b
->widget
) {
183 button_callback (w
, WIDGET_KEY
, ' ');
187 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
) {
188 button_callback (w
, WIDGET_KEY
, ' ');
192 if (b
->text
.hotkey
!= NULL
) {
193 if (g_ascii_tolower ((gchar
)b
->text
.hotkey
[0]) ==
194 g_ascii_tolower ((gchar
)parm
)) {
195 button_callback (w
, WIDGET_KEY
, ' ');
199 return MSG_NOT_HANDLED
;
202 if (parm
!= ' ' && parm
!= '\n')
203 return MSG_NOT_HANDLED
;
206 stop
= (*b
->callback
) (b
->action
);
207 if (!b
->callback
|| stop
) {
208 h
->ret_value
= b
->action
;
229 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
235 if (msg
== WIDGET_UNFOCUS
)
237 else if (msg
== WIDGET_FOCUS
)
240 widget_selectcolor (w
, b
->selected
, FALSE
);
241 widget_move (w
, 0, 0);
245 tty_print_string ("[< ");
248 tty_print_string ("[ ");
251 tty_print_string ("[");
258 draw_hotkey (w
, b
->text
, b
->selected
);
262 tty_print_string (" >]");
265 tty_print_string (" ]");
268 tty_print_string ("]");
274 release_hotkey (b
->text
);
278 return default_proc (msg
, parm
);
283 button_event (Gpm_Event
*event
, void *data
)
287 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
288 Dlg_head
*h
= b
->widget
.parent
;
289 dlg_select_widget (b
);
290 if (event
->type
& GPM_UP
){
291 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
292 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
300 button_get_len (const WButton
*b
)
302 int ret
= hotkey_width (b
->text
);
321 button_new (int y
, int x
, int action
, int flags
, const char *text
,
324 WButton
*b
= g_new (WButton
, 1);
328 b
->text
= parse_hotkey (text
);
330 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
),
331 button_callback
, button_event
);
334 b
->callback
= callback
;
335 widget_want_hotkey (b
->widget
, 1);
336 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
342 button_get_text (const WButton
*b
)
344 if (b
->text
.hotkey
!= NULL
)
345 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
,
346 b
->text
.end
, (char *) NULL
);
348 return g_strdup (b
->text
.start
);
352 button_set_text (WButton
*b
, const char *text
)
354 release_hotkey (b
->text
);
355 b
->text
= parse_hotkey (text
);
356 b
->widget
.cols
= button_get_len (b
);
357 dlg_redraw (b
->widget
.parent
);
361 /* Radio button widget */
362 static int radio_event (Gpm_Event
*event
, void *);
365 radio_callback (Widget
*w
, widget_msg_t msg
, int parm
)
367 WRadio
*r
= (WRadio
*) w
;
369 Dlg_head
*h
= r
->widget
.parent
;
374 int lp
= g_ascii_tolower ((gchar
)parm
);
376 for (i
= 0; i
< r
->count
; i
++) {
377 if (r
->texts
[i
].hotkey
!= NULL
) {
378 int c
= g_ascii_tolower ((gchar
)r
->texts
[i
].hotkey
[0]);
385 radio_callback (w
, WIDGET_KEY
, ' ');
390 return MSG_NOT_HANDLED
;
396 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
397 radio_callback (w
, WIDGET_FOCUS
, ' ');
406 return MSG_NOT_HANDLED
;
410 if (r
->count
- 1 > r
->pos
) {
415 return MSG_NOT_HANDLED
;
418 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
419 radio_callback (w
, WIDGET_FOCUS
, ' ');
420 widget_move (&r
->widget
, r
->pos
, 1);
426 for (i
= 0; i
< r
->count
; i
++) {
427 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
428 widget_selectcolor (w
, focused
, FALSE
);
429 widget_move (&r
->widget
, i
, 0);
430 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
431 draw_hotkey (w
, r
->texts
[i
], focused
);
436 for (i
= 0; i
< r
->count
; i
++) {
437 release_hotkey (r
->texts
[i
]);
443 return default_proc (msg
, parm
);
448 radio_event (Gpm_Event
*event
, void *data
)
453 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
454 Dlg_head
*h
= r
->widget
.parent
;
456 r
->pos
= event
->y
- 1;
457 dlg_select_widget (r
);
458 if (event
->type
& GPM_UP
){
459 radio_callback (w
, WIDGET_KEY
, ' ');
460 radio_callback (w
, WIDGET_FOCUS
, 0);
461 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
469 radio_new (int y
, int x
, int count
, const char **texts
)
471 WRadio
*result
= g_new (WRadio
, 1);
474 /* Compute the longest string */
475 result
->texts
= g_new (struct hotkey_t
, count
);
478 for (i
= 0; i
< count
; i
++){
479 result
->texts
[i
] = parse_hotkey (texts
[i
]);
480 m
= hotkey_width (result
->texts
[i
]);
485 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
489 result
->count
= count
;
490 widget_want_hotkey (result
->widget
, 1);
496 /* Checkbutton widget */
498 static int check_event (Gpm_Event
*event
, void *);
501 check_callback (Widget
*w
, widget_msg_t msg
, int parm
)
503 WCheck
*c
= (WCheck
*) w
;
504 Dlg_head
*h
= c
->widget
.parent
;
508 if (c
->text
.hotkey
!= NULL
) {
509 if (g_ascii_tolower ((gchar
)c
->text
.hotkey
[0]) ==
510 g_ascii_tolower ((gchar
)parm
)) {
512 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
516 return MSG_NOT_HANDLED
;
520 return MSG_NOT_HANDLED
;
522 c
->state
^= C_CHANGE
;
523 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
524 check_callback (w
, WIDGET_FOCUS
, ' ');
528 widget_move (&c
->widget
, 0, 1);
534 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
535 widget_move (&c
->widget
, 0, 0);
536 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
537 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
541 release_hotkey (c
->text
);
545 return default_proc (msg
, parm
);
550 check_event (Gpm_Event
*event
, void *data
)
555 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
556 Dlg_head
*h
= c
->widget
.parent
;
558 dlg_select_widget (c
);
559 if (event
->type
& GPM_UP
){
560 check_callback (w
, WIDGET_KEY
, ' ');
561 check_callback (w
, WIDGET_FOCUS
, 0);
562 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
570 check_new (int y
, int x
, int state
, const char *text
)
572 WCheck
*c
= g_new (WCheck
, 1);
574 c
->text
= parse_hotkey (text
);
576 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
),
577 check_callback
, check_event
);
578 c
->state
= state
? C_BOOL
: 0;
579 widget_want_hotkey (c
->widget
, 1);
585 save_text_to_clip_file (const char *text
)
590 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
591 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
592 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
598 mc_write (file
, (char *) text
, strlen (text
));
604 load_text_from_clip_file (char **text
)
609 gboolean first
= TRUE
;
611 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
612 f
= fopen (fname
, "r");
620 while (fgets (buf
, sizeof (buf
), f
)) {
625 if (buf
[len
- 1] == '\n')
630 *text
= g_strdup (buf
);
632 /* remove \n on EOL */
635 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
644 return (*text
!= NULL
);
648 panel_save_curent_file_to_clip_file (void)
652 if (current_panel
->marked
== 0)
653 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
656 gboolean first
= TRUE
;
659 for (i
= 0; i
< current_panel
->count
; i
++)
660 if (current_panel
->dir
.list
[i
].f
.marked
!= 0) { /* Skip the unmarked ones */
662 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
665 /* Add empty lines after the file */
668 tmp
= g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
675 res
= save_text_to_clip_file (flist
);
686 label_callback (Widget
*w
, widget_msg_t msg
, int parm
)
688 WLabel
*l
= (WLabel
*) w
;
689 Dlg_head
*h
= l
->widget
.parent
;
695 /* We don't want to get the focus */
697 return MSG_NOT_HANDLED
;
701 char *p
= l
->text
, *q
, c
= 0;
708 tty_setcolor (DEFAULT_COLOR
);
710 tty_setcolor (DLG_NORMALC (h
));
713 q
= strchr (p
, '\n');
719 widget_move (&l
->widget
, y
, 0);
720 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
736 return default_proc (msg
, parm
);
741 label_set_text (WLabel
*label
, const char *text
)
743 int newcols
= label
->widget
.cols
;
746 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
747 return; /* Flickering is not nice */
749 g_free (label
->text
);
752 label
->text
= g_strdup (text
);
753 if (label
->auto_adjust_cols
) {
754 str_msg_term_size (text
, &newlines
, &newcols
);
755 if (newcols
> label
->widget
.cols
)
756 label
->widget
.cols
= newcols
;
757 if (newlines
> label
->widget
.lines
)
758 label
->widget
.lines
= newlines
;
760 } else label
->text
= NULL
;
762 if (label
->widget
.parent
)
763 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
765 if (newcols
< label
->widget
.cols
)
766 label
->widget
.cols
= newcols
;
770 label_new (int y
, int x
, const char *text
)
777 str_msg_term_size (text
, &lines
, &cols
);
779 l
= g_new (WLabel
, 1);
780 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
781 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
782 l
->auto_adjust_cols
= 1;
784 widget_want_cursor (l
->widget
, 0);
789 hline_callback (Widget
*w
, widget_msg_t msg
, int parm
)
791 WHLine
*l
= (WHLine
*) w
;
792 Dlg_head
*h
= l
->widget
.parent
;
797 if (l
->auto_adjust_cols
) {
798 if (((w
->parent
->flags
& DLG_COMPACT
) != 0)) {
800 w
->cols
= w
->parent
->cols
;
802 w
->x
= w
->parent
->x
+ 1;
803 w
->cols
= w
->parent
->cols
- 2;
808 /* We don't want to get the focus */
809 return MSG_NOT_HANDLED
;
813 tty_setcolor (DEFAULT_COLOR
);
815 tty_setcolor (DLG_NORMALC (h
));
817 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
819 if (l
->auto_adjust_cols
) {
820 widget_move (w
, 0, 0);
821 tty_print_alt_char (ACS_LTEE
);
822 widget_move (w
, 0, w
->cols
- 1);
823 tty_print_alt_char (ACS_RTEE
);
828 return default_proc (msg
, parm
);
834 hline_new (int y
, int x
, int width
)
840 l
= g_new (WHLine
, 1);
841 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
842 l
->auto_adjust_cols
= (width
< 0);
843 l
->transparent
= FALSE
;
844 widget_want_cursor (l
->widget
, 0);
849 /* Gauge widget (progress indicator) */
850 /* Currently width is hardcoded here for text mode */
854 gauge_callback (Widget
*w
, widget_msg_t msg
, int parm
)
856 WGauge
*g
= (WGauge
*) w
;
857 Dlg_head
*h
= g
->widget
.parent
;
859 if (msg
== WIDGET_INIT
)
862 /* We don't want to get the focus */
863 if (msg
== WIDGET_FOCUS
)
864 return MSG_NOT_HANDLED
;
866 if (msg
== WIDGET_DRAW
){
867 widget_move (&g
->widget
, 0, 0);
868 tty_setcolor (DLG_NORMALC (h
));
870 tty_printf ("%*s", gauge_len
, "");
872 int percentage
, columns
;
873 long total
= g
->max
, done
= g
->current
;
875 if (total
<= 0 || done
< 0) {
881 while (total
> 65535) {
885 percentage
= (200 * done
/ total
+ 1) / 2;
886 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
887 tty_print_char ('[');
888 if (g
->from_left_to_right
) {
889 tty_setcolor (GAUGE_COLOR
);
890 tty_printf ("%*s", (int) columns
, "");
891 tty_setcolor (DLG_NORMALC (h
));
892 tty_printf ("%*s] %3d%%", (int)(gauge_len
- 7 - columns
), "", (int) percentage
);
894 tty_setcolor (DLG_NORMALC (h
));
895 tty_printf ("%*s", gauge_len
- columns
- 7, "");
896 tty_setcolor (GAUGE_COLOR
);
897 tty_printf ("%*s", columns
, "");
898 tty_setcolor (DLG_NORMALC (h
));
899 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
905 return default_proc (msg
, parm
);
909 gauge_set_value (WGauge
*g
, int max
, int current
)
911 if (g
->current
== current
&& g
->max
== max
)
912 return; /* Do not flicker */
914 max
= 1; /* I do not like division by zero :) */
916 g
->current
= current
;
918 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
922 gauge_show (WGauge
*g
, int shown
)
924 if (g
->shown
== shown
)
927 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
931 gauge_new (int y
, int x
, int shown
, int max
, int current
)
933 WGauge
*g
= g_new (WGauge
, 1);
935 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
938 max
= 1; /* I do not like division by zero :) */
940 g
->current
= current
;
941 g
->from_left_to_right
= TRUE
;
942 widget_want_cursor (g
->widget
, 0);
949 /* {{{ history button */
951 #define LARGE_HISTORY_BUTTON 1
953 #ifdef LARGE_HISTORY_BUTTON
954 # define HISTORY_BUTTON_WIDTH 3
956 # define HISTORY_BUTTON_WIDTH 1
959 #define should_show_history_button(in) \
960 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
962 static void draw_history_button (WInput
* in
)
965 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
966 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
967 #ifdef LARGE_HISTORY_BUTTON
970 h
= in
->widget
.parent
;
971 tty_setcolor (NORMAL_COLOR
);
972 tty_print_string ("[ ]");
973 /* Too distracting: tty_setcolor (MARKED_COLOR); */
974 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
978 tty_setcolor (MARKED_COLOR
);
983 /* }}} history button */
986 /* Input widgets now have a global kill ring */
987 /* Pointer to killed data */
988 static char *kill_buffer
= 0;
991 update_input (WInput
*in
, int clear_first
)
995 int buf_len
= str_length (in
->buffer
);
999 if (should_show_history_button (in
))
1000 has_history
= HISTORY_BUTTON_WIDTH
;
1002 if (in
->disable_update
)
1005 pw
= str_term_width2 (in
->buffer
, in
->point
);
1007 /* Make the point visible */
1008 if ((pw
< in
->term_first_shown
) ||
1009 (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
)) {
1011 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1012 if (in
->term_first_shown
< 0)
1013 in
->term_first_shown
= 0;
1016 /* Adjust the mark */
1017 if (in
->mark
> buf_len
)
1021 draw_history_button (in
);
1023 tty_setcolor (in
->color
);
1025 widget_move (&in
->widget
, 0, 0);
1027 if (!in
->is_password
) {
1028 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1029 in
->field_width
- has_history
));
1032 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++){
1034 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1036 if (cp
[0] != '\0') str_cnext_char (&cp
);
1045 winput_set_origin (WInput
*in
, int x
, int field_width
)
1048 in
->field_width
= in
->widget
.cols
= field_width
;
1049 update_input (in
, 0);
1052 /* {{{ history saving and loading */
1054 int num_history_items_recorded
= 60;
1057 This loads and saves the history of an input line to and from the
1058 widget. It is called with the widgets history name on creation of the
1059 widget, and returns the GList list. It stores histories in the file
1060 ~/.mc/history in using the profile code.
1062 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1063 function) then input_new assigns the default text to be the last text
1064 entered, or "" if not found.
1068 history_get (const char *input_name
)
1075 size_t keys_num
= 0;
1078 if (num_history_items_recorded
== 0) /* this is how to disable */
1080 if ((input_name
== NULL
) || (*input_name
== '\0'))
1083 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1084 cfg
= mc_config_init (profile
);
1086 /* get number of keys */
1087 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1090 for (i
= 0; i
< keys_num
; i
++) {
1092 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long)i
);
1093 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1095 if (this_entry
!= NULL
)
1096 hist
= list_append_unique (hist
, this_entry
);
1099 mc_config_deinit (cfg
);
1102 /* return pointer to the last entry in the list */
1103 return g_list_last (hist
);
1107 history_put (const char *input_name
, GList
*h
)
1113 if (num_history_items_recorded
== 0) /* this is how to disable */
1115 if ((input_name
== NULL
) || (*input_name
== '\0'))
1120 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1122 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1126 /* Make sure the history is only readable by the user */
1127 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
) {
1132 /* go to end of list */
1133 h
= g_list_last (h
);
1135 /* go back 60 places */
1136 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1137 h
= g_list_previous (h
);
1139 cfg
= mc_config_init (profile
);
1141 if (input_name
!= NULL
)
1142 mc_config_del_group (cfg
, input_name
);
1144 /* dump history into profile */
1145 for (i
= 0; h
!= NULL
; h
= g_list_next (h
)) {
1146 char *text
= (char *) h
->data
;
1148 /* We shouldn't have null entries, but let's be sure */
1151 g_snprintf (key
, sizeof (key
), "%d", i
++);
1152 mc_config_set_string (cfg
, input_name
, key
, text
);
1156 mc_config_save_file (cfg
, NULL
);
1157 mc_config_deinit(cfg
);
1161 /* }}} history saving and loading */
1164 /* {{{ history display */
1169 return _(" History ");
1179 dlg_hist_reposition (Dlg_head
*dlg_head
)
1181 dlg_hist_data
*data
;
1182 int x
= 0, y
, he
, wi
;
1185 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1186 return MSG_NOT_HANDLED
;
1188 data
= (dlg_hist_data
*) dlg_head
->data
;
1190 y
= data
->widget
->y
;
1191 he
= data
->count
+ 2;
1193 if (he
<= y
|| y
> (LINES
- 6)) {
1194 he
= min (he
, y
- 1);
1198 he
= min (he
, LINES
- y
);
1201 if (data
->widget
->x
> 2)
1202 x
= data
->widget
->x
- 2;
1204 wi
= data
->maxlen
+ 4;
1206 if ((wi
+ x
) > COLS
) {
1207 wi
= min (wi
, COLS
);
1211 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1217 dlg_hist_callback (Dlg_head
*h
, Widget
*sender
,
1218 dlg_msg_t msg
, int parm
, void *data
)
1222 return dlg_hist_reposition (h
);
1225 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1230 show_hist (GList
**history
, Widget
*widget
)
1232 GList
*z
, *hlist
= NULL
, *hi
;
1233 size_t maxlen
, i
, count
= 0;
1235 Dlg_head
*query_dlg
;
1236 WListbox
*query_list
;
1237 dlg_hist_data hist_data
;
1239 if (*history
== NULL
)
1242 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1244 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
)) {
1247 i
= str_term_width1 ((char *) z
->data
);
1248 maxlen
= max (maxlen
, i
);
1251 entry
= g_new0 (WLEntry
, 1);
1252 /* history is being reverted here */
1253 entry
->text
= g_strdup ((char *) z
->data
);
1254 hlist
= g_list_prepend (hlist
, entry
);
1257 hist_data
.widget
= widget
;
1258 hist_data
.count
= count
;
1259 hist_data
.maxlen
= maxlen
;
1262 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1263 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1264 query_dlg
->data
= &hist_data
;
1266 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1268 /* this call makes list stick to all sides of dialog, effectively make
1269 it be resized with dialog */
1270 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1272 /* to avoid diplicating of (calculating sizes in two places)
1273 code, call dlg_hist_callback function here, to set dialog and
1275 The main idea - create 4x4 dialog and add 2x2 list in
1276 center of it, and let dialog function resize it to needed
1278 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1280 if (query_dlg
->y
< widget
->y
) {
1281 /* draw list entries from bottom upto top */
1282 listbox_set_list (query_list
, hlist
);
1283 listbox_select_last (query_list
);
1285 /* draw list entries from top downto bottom */
1286 /* revert history direction */
1287 hlist
= g_list_reverse (hlist
);
1288 listbox_set_list (query_list
, hlist
);
1291 if (run_dlg (query_dlg
) != B_CANCEL
) {
1294 listbox_get_current (query_list
, &q
, NULL
);
1299 /* get modified history from dialog */
1301 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
)) {
1304 entry
= (WLEntry
*) hi
->data
;
1305 /* history is being reverted here again */
1306 z
= g_list_prepend (z
, entry
->text
);
1310 destroy_dlg (query_dlg
);
1312 /* restore history direction */
1313 if (query_dlg
->y
< widget
->y
)
1314 z
= g_list_reverse (z
);
1316 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1317 g_list_free (*history
);
1318 *history
= g_list_last (z
);
1324 do_show_hist (WInput
*in
)
1328 r
= show_hist (&in
->history
, &in
->widget
);
1330 assign_text (in
, r
);
1335 /* }}} history display */
1338 input_destroy (WInput
*in
)
1341 fprintf (stderr
, "Internal error: null Input *\n");
1348 if (!in
->is_password
) /* don't save passwords ;-) */
1349 history_put (in
->history_name
, in
->history
);
1351 in
->history
= g_list_first (in
->history
);
1352 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1353 g_list_free (in
->history
);
1356 g_free (in
->buffer
);
1357 free_completions (in
);
1358 g_free (in
->history_name
);
1362 input_disable_update (WInput
*in
)
1364 in
->disable_update
++;
1368 input_enable_update (WInput
*in
)
1370 in
->disable_update
--;
1371 update_input (in
, 0);
1376 push_history (WInput
*in
, const char *text
)
1378 /* input widget where urls with passwords are entered without any
1380 const char *password_input_fields
[] = {
1381 N_(" Link to a remote machine "),
1382 N_(" FTP to machine "),
1383 N_(" SMB link to machine ")
1385 const size_t ELEMENTS
= (sizeof (password_input_fields
) /
1386 sizeof (password_input_fields
[0]));
1396 for (i
= 0; i
< ELEMENTS
; i
++)
1397 password_input_fields
[i
] = _(password_input_fields
[i
]);
1400 t
= g_strstrip (g_strdup (text
));
1403 t
= g_strdup (empty
? "" : text
);
1405 if (in
->history_name
!= NULL
) {
1406 const char *p
= in
->history_name
+ 3;
1408 for (i
= 0; i
< ELEMENTS
; i
++)
1409 if (strcmp (p
, password_input_fields
[i
]) == 0)
1412 strip_password (t
, i
>= ELEMENTS
);
1415 in
->history
= list_append_unique (in
->history
, t
);
1419 /* Cleans the input line and adds the current text to the history */
1421 new_input (WInput
*in
)
1423 push_history (in
, in
->buffer
);
1425 in
->buffer
[0] = '\0';
1429 free_completions (in
);
1430 update_input (in
, 0);
1434 move_buffer_backward (WInput
*in
, int start
, int end
)
1437 int str_len
= str_length (in
->buffer
);
1438 if (start
>= str_len
|| end
> str_len
+ 1) return;
1440 pos
= str_offset_to_pos (in
->buffer
, start
);
1441 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1443 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1444 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1448 insert_char (WInput
*in
, int c_code
)
1454 return MSG_NOT_HANDLED
;
1456 if (in
->charpoint
>= MB_LEN_MAX
)
1459 in
->charbuf
[in
->charpoint
] = c_code
;
1462 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1465 in
->charpoint
= 0; /* broken multibyte char, skip */
1470 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
){
1471 /* Expand the buffer */
1472 size_t new_length
= in
->current_max_size
+
1473 in
->field_width
+ in
->charpoint
;
1474 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1477 in
->current_max_size
= new_length
;
1481 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
) {
1482 /* bytes from begin */
1483 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1485 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1487 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1488 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] =
1489 in
->buffer
[ins_point
+ i
- 1];
1491 memcpy(in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1500 beginning_of_line (WInput
*in
)
1507 end_of_line (WInput
*in
)
1509 in
->point
= str_length (in
->buffer
);
1514 backward_char (WInput
*in
)
1516 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1518 if (in
->point
> 0) {
1519 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1525 forward_char (WInput
*in
)
1527 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1528 if (act
[0] != '\0') {
1529 in
->point
+= str_cnext_noncomb_char (&act
);
1535 forward_word (WInput
* in
)
1537 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1539 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
))) {
1540 str_cnext_char (&p
);
1543 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
)) {
1544 str_cnext_char (&p
);
1550 backward_word (WInput
*in
)
1556 p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1557 (p
!= in
->buffer
) && (p
[0] == '\0');
1558 str_cprev_char (&p
), in
->point
--
1561 while (p
!= in
->buffer
) {
1563 str_cprev_char (&p
);
1564 if (!str_isspace (p
) && !str_ispunct (p
)) {
1570 while (p
!= in
->buffer
) {
1571 str_cprev_char (&p
);
1572 if (str_isspace (p
) || str_ispunct (p
))
1580 key_left (WInput
*in
)
1586 key_ctrl_left (WInput
*in
)
1592 key_right (WInput
*in
)
1598 key_ctrl_right (WInput
*in
)
1603 backward_delete (WInput
*in
)
1605 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1611 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1612 move_buffer_backward(in
, start
, in
->point
);
1619 delete_char (WInput
*in
)
1621 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1622 int end
= in
->point
;
1624 end
+= str_cnext_noncomb_char (&act
);
1626 move_buffer_backward(in
, in
->point
, end
);
1632 copy_region (WInput
*in
, int x_first
, int x_last
)
1634 int first
= min (x_first
, x_last
);
1635 int last
= max (x_first
, x_last
);
1637 if (last
== first
) {
1638 /* Copy selected files to clipboard */
1639 panel_save_curent_file_to_clip_file ();
1643 g_free (kill_buffer
);
1645 first
= str_offset_to_pos (in
->buffer
, first
);
1646 last
= str_offset_to_pos (in
->buffer
, last
);
1648 kill_buffer
= g_strndup(in
->buffer
+ first
, last
- first
);
1649 save_text_to_clip_file (kill_buffer
);
1653 delete_region (WInput
*in
, int x_first
, int x_last
)
1655 int first
= min (x_first
, x_last
);
1656 int last
= max (x_first
, x_last
);
1660 if (in
->mark
> first
)
1662 last
= str_offset_to_pos (in
->buffer
, last
);
1663 first
= str_offset_to_pos (in
->buffer
, first
);
1664 len
= strlen (&in
->buffer
[last
]) + 1;
1665 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1671 kill_word (WInput
*in
)
1673 int old_point
= in
->point
;
1677 new_point
= in
->point
;
1678 in
->point
= old_point
;
1680 copy_region (in
, old_point
, new_point
);
1681 delete_region (in
, old_point
, new_point
);
1688 back_kill_word (WInput
*in
)
1690 int old_point
= in
->point
;
1694 new_point
= in
->point
;
1695 in
->point
= old_point
;
1697 copy_region (in
, old_point
, new_point
);
1698 delete_region (in
, old_point
, new_point
);
1703 set_mark (WInput
*in
)
1705 in
->mark
= in
->point
;
1709 kill_save (WInput
*in
)
1711 copy_region (in
, in
->mark
, in
->point
);
1715 kill_region (WInput
*in
)
1718 delete_region (in
, in
->point
, in
->mark
);
1722 clear_region (WInput
*in
)
1724 delete_region (in
, in
->point
, in
->mark
);
1735 for (p
= kill_buffer
; *p
; p
++)
1736 insert_char (in
, *p
);
1741 kill_line (WInput
*in
)
1743 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1744 g_free (kill_buffer
);
1745 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1746 in
->buffer
[chp
] = '\0';
1751 ins_from_clip (WInput
*in
)
1755 if (load_text_from_clip_file (&p
)) {
1758 for (pp
= p
; *pp
!= '\0'; pp
++)
1759 insert_char (in
, *pp
);
1766 assign_text (WInput
*in
, const char *text
)
1768 free_completions (in
);
1769 g_free (in
->buffer
);
1770 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1771 in
->current_max_size
= strlen (in
->buffer
) + 1;
1772 in
->point
= str_length (in
->buffer
);
1779 hist_prev (WInput
*in
)
1787 push_history (in
, in
->buffer
);
1789 prev
= g_list_previous (in
->history
);
1792 assign_text (in
, (char *) prev
->data
);
1798 hist_next (WInput
*in
)
1800 if (in
->need_push
) {
1801 push_history (in
, in
->buffer
);
1802 assign_text (in
, "");
1809 if (!in
->history
->next
) {
1810 assign_text (in
, "");
1814 in
->history
= g_list_next (in
->history
);
1815 assign_text (in
, (char *) in
->history
->data
);
1820 port_region_marked_for_delete (WInput
*in
)
1822 in
->buffer
[0] = '\0';
1829 input_execute_cmd (WInput
*in
, unsigned long command
)
1831 cb_ret_t res
= MSG_HANDLED
;
1835 beginning_of_line (in
);
1840 case CK_InputMoveLeft
:
1843 case CK_InputWordLeft
:
1846 case CK_InputMoveRight
:
1849 case CK_InputWordRight
:
1850 key_ctrl_right (in
);
1852 case CK_InputBackwardChar
:
1855 case CK_InputBackwardWord
:
1858 case CK_InputForwardChar
:
1861 case CK_InputForwardWord
:
1864 case CK_InputBackwardDelete
:
1865 backward_delete (in
);
1867 case CK_InputDeleteChar
:
1870 case CK_InputKillWord
:
1873 case CK_InputBackwardKillWord
:
1874 back_kill_word (in
);
1876 case CK_InputSetMark
:
1879 case CK_InputKillRegion
:
1882 case CK_InputClearLine
:
1885 case CK_InputKillSave
:
1894 case CK_InputKillLine
:
1897 case CK_InputHistoryPrev
:
1900 case CK_InputHistoryNext
:
1903 case CK_InputHistoryShow
:
1906 case CK_InputComplete
:
1910 res
= MSG_NOT_HANDLED
;
1916 /* This function is a test for a special input key used in complete.c */
1917 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1918 and 2 if it is a complete key */
1920 is_in_input_map (WInput
*in
, int key
)
1923 for (i
= 0; input_map
[i
].key
!= 0; i
++)
1924 if (key
== input_map
[i
].key
) {
1925 input_execute_cmd (in
, input_map
[i
].command
);
1926 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
1932 handle_char (WInput
*in
, int key
)
1937 v
= MSG_NOT_HANDLED
;
1940 free_completions (in
);
1941 v
= insert_char (in
, key
);
1942 update_input (in
, 1);
1946 for (i
= 0; input_map
[i
].key
; i
++) {
1947 if (key
== input_map
[i
].key
) {
1948 if (input_map
[i
].command
!= CK_InputComplete
)
1949 free_completions (in
);
1950 input_execute_cmd (in
, input_map
[i
].command
);
1951 update_input (in
, 1);
1956 if (input_map
[i
].command
== 0) {
1958 return MSG_NOT_HANDLED
;
1960 port_region_marked_for_delete (in
);
1961 free_completions (in
);
1962 v
= insert_char (in
, key
);
1964 update_input (in
, 1);
1968 /* Inserts text in input line */
1970 stuff (WInput
*in
, const char *text
, int insert_extra_space
)
1972 input_disable_update (in
);
1973 while (*text
!= '\0')
1974 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
1975 if (insert_extra_space
)
1976 handle_char (in
, ' ');
1977 input_enable_update (in
);
1978 update_input (in
, 1);
1982 input_set_point (WInput
*in
, int pos
)
1984 int max_pos
= str_length (in
->buffer
);
1988 if (pos
!= in
->point
)
1989 free_completions (in
);
1992 update_input (in
, 1);
1996 input_callback (Widget
*w
, widget_msg_t msg
, int parm
)
1998 WInput
*in
= (WInput
*) w
;
2003 if (parm
== XCTRL ('q')) {
2005 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2010 /* Keys we want others to handle */
2011 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2012 || parm
== KEY_F (10) || parm
== '\n')
2013 return MSG_NOT_HANDLED
;
2015 /* When pasting multiline text, insert literal Enter */
2016 if ((parm
& ~KEY_M_MASK
) == '\n') {
2018 v
= handle_char (in
, '\n');
2023 return handle_char (in
, parm
);
2025 case WIDGET_COMMAND
:
2026 return input_execute_cmd (in
, parm
);
2029 case WIDGET_UNFOCUS
:
2031 update_input (in
, 0);
2035 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2036 - in
->term_first_shown
);
2039 case WIDGET_DESTROY
:
2044 return default_proc (msg
, parm
);
2049 input_event (Gpm_Event
* event
, void *data
)
2053 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2054 dlg_select_widget (in
);
2056 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2057 && should_show_history_button (in
)) {
2060 in
->point
= str_length (in
->buffer
);
2061 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2062 in
->point
= str_column_to_pos (in
->buffer
, event
->x
2063 + in
->term_first_shown
- 1);
2065 update_input (in
, 1);
2071 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
2072 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2074 WInput
*in
= g_new (WInput
, 1);
2075 size_t initial_buffer_len
;
2077 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2080 in
->history_name
= NULL
;
2082 if ((histname
!= NULL
) && (*histname
!= '\0')) {
2083 in
->history_name
= g_strdup (histname
);
2084 in
->history
= history_get (histname
);
2087 if (def_text
== NULL
)
2089 else if (def_text
== INPUT_LAST_TEXT
) {
2090 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2091 def_text
= (char *) in
->history
->data
;
2096 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2097 in
->widget
.options
|= W_IS_INPUT
;
2098 in
->completions
= NULL
;
2099 in
->completion_flags
= completion_flags
;
2100 in
->current_max_size
= initial_buffer_len
;
2101 in
->buffer
= g_new (char, initial_buffer_len
);
2103 in
->field_width
= width
;
2105 in
->term_first_shown
= 0;
2106 in
->disable_update
= 0;
2109 in
->is_password
= 0;
2111 strcpy (in
->buffer
, def_text
);
2112 in
->point
= str_length (in
->buffer
);
2119 /* Listbox widget */
2121 /* Should draw the scrollbar, but currently draws only
2122 * indications that there is more information
2126 listbox_entry_free (void *data
)
2134 listbox_drawscroll (WListbox
*l
)
2136 const int max_line
= l
->widget
.lines
- 1;
2140 /* Are we at the top? */
2141 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2143 tty_print_one_vline ();
2145 tty_print_char ('^');
2147 /* Are we at the bottom? */
2148 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2149 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2150 tty_print_one_vline ();
2152 tty_print_char ('v');
2154 /* Now draw the nice relative pointer */
2156 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2158 for (i
= 1; i
< max_line
; i
++) {
2159 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2161 tty_print_one_vline ();
2163 tty_print_char ('*');
2168 listbox_draw (WListbox
*l
, gboolean focused
)
2170 const Dlg_head
*h
= l
->widget
.parent
;
2171 const int normalc
= DLG_NORMALC (h
);
2172 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2179 le
= g_list_nth (l
->list
, l
->top
);
2180 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2181 pos
= (le
== NULL
) ? 0 : l
->top
;
2183 for (i
= 0; i
< l
->widget
.lines
; i
++) {
2186 /* Display the entry */
2187 if (pos
== l
->pos
&& sel_line
== -1) {
2189 tty_setcolor (selc
);
2191 tty_setcolor (normalc
);
2193 widget_move (&l
->widget
, i
, 1);
2195 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2198 WLEntry
*e
= (WLEntry
*) le
->data
;
2200 le
= g_list_next (le
);
2204 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2207 l
->cursor_y
= sel_line
;
2209 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
)) {
2210 tty_setcolor (normalc
);
2211 listbox_drawscroll (l
);
2216 listbox_check_hotkey (WListbox
*l
, int key
)
2221 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
)) {
2222 WLEntry
*e
= (WLEntry
*) le
->data
;
2224 if (e
->hotkey
== key
)
2231 /* Selects the last entry and scrolls the list to the bottom */
2233 listbox_select_last (WListbox
*l
)
2235 l
->pos
= l
->count
- 1;
2236 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2239 /* Selects the first entry and scrolls the list to the top */
2241 listbox_select_first (WListbox
*l
)
2243 l
->pos
= l
->top
= 0;
2247 listbox_set_list (WListbox
*l
, GList
*list
)
2249 listbox_remove_list (l
);
2253 l
->top
= l
->pos
= 0;
2254 l
->count
= g_list_length (list
);
2259 listbox_remove_list (WListbox
*l
)
2261 if ((l
!= NULL
) && (l
->count
!= 0)) {
2262 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2263 g_list_free (l
->list
);
2265 l
->count
= l
->pos
= l
->top
= 0;
2270 listbox_remove_current (WListbox
*l
)
2272 if ((l
!= NULL
) && (l
->count
!= 0)) {
2275 current
= g_list_nth (l
->list
, l
->pos
);
2276 l
->list
= g_list_remove_link (l
->list
, current
);
2277 listbox_entry_free ((WLEntry
*) current
->data
);
2278 g_list_free_1 (current
);
2282 l
->top
= l
->pos
= 0;
2283 else if (l
->pos
>= l
->count
)
2284 l
->pos
= l
->count
- 1;
2289 listbox_select_entry (WListbox
*l
, int dest
)
2293 gboolean top_seen
= FALSE
;
2299 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
)) {
2308 if (l
->pos
- l
->top
>= l
->widget
.lines
)
2309 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2314 /* If we are unable to find it, set decent values */
2315 l
->pos
= l
->top
= 0;
2318 /* Selects from base the pos element */
2320 listbox_select_pos (WListbox
*l
, int base
, int pos
)
2322 int last
= l
->count
- 1;
2332 listbox_fwd (WListbox
*l
)
2334 if (l
->pos
+ 1 >= l
->count
)
2335 listbox_select_first (l
);
2337 listbox_select_entry (l
, l
->pos
+ 1);
2341 listbox_back (WListbox
*l
)
2344 listbox_select_last (l
);
2346 listbox_select_entry (l
, l
->pos
- 1);
2349 /* Return MSG_HANDLED if we want a redraw */
2351 listbox_key (WListbox
*l
, int key
)
2355 cb_ret_t j
= MSG_NOT_HANDLED
;
2357 if (l
->list
== NULL
)
2358 return MSG_NOT_HANDLED
;
2360 /* focus on listbox item N by '0'..'9' keys */
2361 if (key
>= '0' && key
<= '9') {
2362 int oldpos
= l
->pos
;
2363 listbox_select_entry (l
, key
- '0');
2365 /* need scroll to item? */
2366 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2376 listbox_select_first (l
);
2382 listbox_select_last (l
);
2397 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++) {
2405 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++) {
2414 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2415 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2417 listbox_remove_current (l
);
2418 if ((l
->top
> 0) && (is_last
|| is_more
))
2423 case (KEY_M_SHIFT
| KEY_DC
):
2425 if (l
->deletable
&& confirm_history_cleanup
2426 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2427 && (query_dialog (Q_("DialogTitle|History cleanup"),
2428 _("Do you want clean this history?"),
2429 D_ERROR
, 2, _("&Yes"), _("&No")) == 0)) {
2430 listbox_remove_list (l
);
2443 listbox_destroy (WListbox
*l
)
2445 /* don't delete list in modifable listbox */
2447 listbox_remove_list (l
);
2451 listbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2453 WListbox
*l
= (WListbox
*) w
;
2454 Dlg_head
*h
= l
->widget
.parent
;
2465 pos
= listbox_check_hotkey (l
, parm
);
2467 return MSG_NOT_HANDLED
;
2469 listbox_select_entry (l
, pos
);
2470 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2472 if (l
->cback
!= NULL
)
2473 action
= l
->cback (l
);
2475 action
= LISTBOX_DONE
;
2477 if (action
== LISTBOX_DONE
) {
2478 h
->ret_value
= B_ENTER
;
2486 ret_code
= listbox_key (l
, parm
);
2487 if (ret_code
!= MSG_NOT_HANDLED
) {
2488 listbox_draw (l
, TRUE
);
2489 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2494 widget_move (&l
->widget
, l
->cursor_y
, 0);
2495 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2499 case WIDGET_UNFOCUS
:
2501 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2504 case WIDGET_DESTROY
:
2505 listbox_destroy (l
);
2508 case WIDGET_RESIZED
:
2512 return default_proc (msg
, parm
);
2517 listbox_event (Gpm_Event
*event
, void *data
)
2522 Dlg_head
*h
= l
->widget
.parent
;
2525 if (event
->type
& GPM_DOWN
)
2526 dlg_select_widget (l
);
2528 if (l
->list
== NULL
)
2531 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2532 int ret
= MOU_REPEAT
;
2534 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2538 for (i
= -event
->y
; i
>= 0; i
--)
2540 else if (event
->y
> l
->widget
.lines
)
2541 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2543 else if (event
->buttons
& GPM_B_UP
) {
2546 } else if (event
->buttons
& GPM_B_DOWN
) {
2550 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2552 /* We need to refresh ourselves since the dialog manager doesn't */
2553 /* know about this event */
2554 listbox_draw (l
, TRUE
);
2559 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
)) {
2562 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2563 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2566 dlg_select_widget (l
);
2567 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2569 if (l
->cback
!= NULL
)
2570 action
= l
->cback (l
);
2572 action
= LISTBOX_DONE
;
2574 if (action
== LISTBOX_DONE
) {
2575 h
->ret_value
= B_ENTER
;
2584 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2586 WListbox
*l
= g_new (WListbox
, 1);
2591 init_widget (&l
->widget
, y
, x
, height
, width
,
2592 listbox_callback
, listbox_event
);
2595 l
->top
= l
->pos
= 0;
2597 l
->deletable
= deletable
;
2598 l
->cback
= callback
;
2599 l
->allow_duplicates
= TRUE
;
2600 l
->scrollbar
= !tty_is_slow ();
2601 widget_want_hotkey (l
->widget
, 1);
2607 listbox_entry_cmp (const void *a
, const void *b
)
2609 const WLEntry
*ea
= (const WLEntry
*) a
;
2610 const WLEntry
*eb
= (const WLEntry
*) b
;
2612 return strcmp (ea
->text
, eb
->text
);
2615 /* Listbox item adding function */
2617 listbox_append_item (WListbox
*l
, WLEntry
*e
, listbox_append_t pos
)
2620 case LISTBOX_APPEND_AT_END
:
2621 l
->list
= g_list_append (l
->list
, e
);
2624 case LISTBOX_APPEND_BEFORE
:
2625 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2630 case LISTBOX_APPEND_AFTER
:
2631 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2634 case LISTBOX_APPEND_SORTED
:
2635 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2646 listbox_add_item (WListbox
*l
, listbox_append_t pos
, int hotkey
,
2647 const char *text
, void *data
)
2654 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2657 entry
= g_new (WLEntry
, 1);
2658 entry
->text
= g_strdup (text
);
2660 entry
->hotkey
= hotkey
;
2662 listbox_append_item (l
, entry
, pos
);
2668 listbox_search_text (WListbox
*l
, const char *text
)
2674 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
)) {
2675 WLEntry
*e
= (WLEntry
*) le
->data
;
2677 if (strcmp (e
->text
, text
) == 0)
2685 /* Returns the current string text as well as the associated extra data */
2687 listbox_get_current (WListbox
*l
, char **string
, void **extra
)
2693 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2698 *string
= ok
? e
->text
: NULL
;
2701 *extra
= ok
? e
->data
: NULL
;
2705 /* ButtonBar widget */
2707 /* returns TRUE if a function has been called, FALSE otherwise. */
2709 buttonbar_call (WButtonBar
*bb
, int i
)
2711 cb_ret_t ret
= MSG_NOT_HANDLED
;
2714 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2715 (Widget
*) bb
, DLG_ACTION
,
2716 bb
->labels
[i
].command
,
2717 bb
->labels
[i
].receiver
);
2721 /* calculate width of one button, width is never lesser than 7 */
2723 buttonbat_get_button_width (void)
2725 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2726 return (result
>= 7) ? result
: 7;
2730 buttonbar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2732 WButtonBar
*bb
= (WButtonBar
*) w
;
2738 return MSG_NOT_HANDLED
;
2741 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2742 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2744 return MSG_NOT_HANDLED
;
2749 int count_free_positions
;
2751 widget_move (&bb
->widget
, 0, 0);
2752 tty_setcolor (DEFAULT_COLOR
);
2753 bb
->btn_width
= buttonbat_get_button_width ();
2754 tty_printf ("%-*s", bb
->widget
.cols
, "");
2755 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2757 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++) {
2758 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2759 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2760 tty_printf ("%2d", i
+ 1);
2761 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2762 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2763 tty_print_string (str_fit_to_term (
2765 bb
->btn_width
- 2 + (int)(offset
< count_free_positions
),
2768 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2774 case WIDGET_DESTROY
:
2775 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2776 g_free (bb
->labels
[i
].text
);
2780 return default_proc (msg
, parm
);
2785 buttonbar_event (Gpm_Event
*event
, void *data
)
2787 WButtonBar
*bb
= data
;
2790 if (!(event
->type
& GPM_UP
))
2794 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2795 if (button
< BUTTONBAR_LABELS_NUM
)
2796 buttonbar_call (bb
, button
);
2801 buttonbar_new (gboolean visible
)
2805 bb
= g_new0 (WButtonBar
, 1);
2807 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
,
2808 buttonbar_callback
, buttonbar_event
);
2809 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2810 bb
->visible
= visible
;
2811 widget_want_hotkey (bb
->widget
, 1);
2812 widget_want_cursor (bb
->widget
, 0);
2813 bb
->btn_width
= buttonbat_get_button_width ();
2819 set_label_text (WButtonBar
*bb
, int lc_index
, const char *text
)
2821 g_free (bb
->labels
[lc_index
- 1].text
);
2822 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2825 /* Find ButtonBar widget in the dialog */
2827 find_buttonbar (const Dlg_head
*h
)
2829 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2833 buttonbar_set_label (WButtonBar
*bb
, int idx
, const char *text
,
2834 const struct global_keymap_t
*keymap
, const Widget
*receiver
)
2836 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
)) {
2837 unsigned long command
= CK_Ignore_Key
;
2840 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2842 if ((text
== NULL
) || (text
[0] == '\0'))
2843 set_label_text (bb
, idx
, "");
2845 set_label_text (bb
, idx
, text
);
2847 bb
->labels
[idx
- 1].command
= command
;
2848 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
2853 buttonbar_set_visible (WButtonBar
*bb
, gboolean visible
)
2855 bb
->visible
= visible
;
2859 buttonbar_redraw (WButtonBar
*bb
)
2862 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
2866 groupbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2868 WGroupbox
*g
= (WGroupbox
*) w
;
2875 return MSG_NOT_HANDLED
;
2878 tty_setcolor (COLOR_NORMAL
);
2879 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2880 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
,
2883 tty_setcolor (COLOR_HOT_NORMAL
);
2884 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2885 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
2886 tty_print_string (g
->title
);
2889 case WIDGET_DESTROY
:
2894 return default_proc (msg
, parm
);
2899 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
2901 WGroupbox
*g
= g_new (WGroupbox
, 1);
2903 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
2905 g
->widget
.options
&= ~W_WANT_CURSOR
;
2906 widget_want_hotkey (g
->widget
, 0);
2908 /* Strip existing spaces, add one space before and after the title */
2911 t
= g_strstrip (g_strdup (title
));
2912 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);