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 */
64 const global_keymap_t
*input_map
;
67 widget_selectcolor (Widget
* w
, gboolean focused
, gboolean hotkey
)
69 Dlg_head
*h
= w
->parent
;
74 : DLG_HOT_NORMALC (h
))
81 parse_hotkey (const char *text
)
83 struct hotkey_t result
;
86 /* search for '&', that is not on the of text */
87 cp
= strchr (text
, '&');
88 if (cp
!= NULL
&& cp
[1] != '\0')
90 result
.start
= g_strndup (text
, cp
- text
);
94 p
= str_cget_next_char (cp
);
95 result
.hotkey
= g_strndup (cp
, p
- cp
);
98 result
.end
= g_strdup (cp
);
102 result
.start
= g_strdup (text
);
103 result
.hotkey
= NULL
;
111 release_hotkey (const struct hotkey_t hotkey
)
113 g_free (hotkey
.start
);
114 g_free (hotkey
.hotkey
);
119 hotkey_width (const struct hotkey_t hotkey
)
123 result
= str_term_width1 (hotkey
.start
);
124 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
125 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
130 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
132 widget_selectcolor (w
, focused
, FALSE
);
133 tty_print_string (hotkey
.start
);
135 if (hotkey
.hotkey
!= NULL
)
137 widget_selectcolor (w
, focused
, TRUE
);
138 tty_print_string (hotkey
.hotkey
);
139 widget_selectcolor (w
, focused
, FALSE
);
142 if (hotkey
.end
!= NULL
)
143 tty_print_string (hotkey
.end
);
147 /* Default callback for widgets */
149 default_proc (widget_msg_t msg
, int parm
)
165 return MSG_NOT_HANDLED
;
169 static int button_event (Gpm_Event
* event
, void *);
174 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
176 WButton
*b
= (WButton
*) w
;
179 Dlg_head
*h
= b
->widget
.parent
;
185 * Don't let the default button steal Enter from the current
186 * button. This is a workaround for the flawed event model
187 * when hotkeys are sent to all widgets before the key is
188 * handled by the current widget.
190 if (parm
== '\n' && h
->current
== &b
->widget
)
192 button_callback (w
, WIDGET_KEY
, ' ');
196 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
198 button_callback (w
, WIDGET_KEY
, ' ');
202 if (b
->text
.hotkey
!= NULL
)
204 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
206 button_callback (w
, WIDGET_KEY
, ' ');
210 return MSG_NOT_HANDLED
;
213 if (parm
!= ' ' && parm
!= '\n')
214 return MSG_NOT_HANDLED
;
217 stop
= (*b
->callback
) (b
->action
);
218 if (!b
->callback
|| stop
)
220 h
->ret_value
= b
->action
;
242 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
248 if (msg
== WIDGET_UNFOCUS
)
250 else if (msg
== WIDGET_FOCUS
)
253 widget_selectcolor (w
, b
->selected
, FALSE
);
254 widget_move (w
, 0, 0);
259 tty_print_string ("[< ");
262 tty_print_string ("[ ");
265 tty_print_string ("[");
272 draw_hotkey (w
, b
->text
, b
->selected
);
277 tty_print_string (" >]");
280 tty_print_string (" ]");
283 tty_print_string ("]");
289 release_hotkey (b
->text
);
293 return default_proc (msg
, parm
);
298 button_event (Gpm_Event
* event
, void *data
)
302 if (event
->type
& (GPM_DOWN
| GPM_UP
))
304 Dlg_head
*h
= b
->widget
.parent
;
305 dlg_select_widget (b
);
306 if (event
->type
& GPM_UP
)
308 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
309 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
317 button_get_len (const WButton
* b
)
319 int ret
= hotkey_width (b
->text
);
339 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
341 WButton
*b
= g_new (WButton
, 1);
345 b
->text
= parse_hotkey (text
);
347 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
350 b
->callback
= callback
;
351 widget_want_hotkey (b
->widget
, 1);
352 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
358 button_get_text (const WButton
* b
)
360 if (b
->text
.hotkey
!= NULL
)
361 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
363 return g_strdup (b
->text
.start
);
367 button_set_text (WButton
* b
, const char *text
)
369 release_hotkey (b
->text
);
370 b
->text
= parse_hotkey (text
);
371 b
->widget
.cols
= button_get_len (b
);
372 dlg_redraw (b
->widget
.parent
);
376 /* Radio button widget */
377 static int radio_event (Gpm_Event
* event
, void *);
380 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
382 WRadio
*r
= (WRadio
*) w
;
384 Dlg_head
*h
= r
->widget
.parent
;
390 int lp
= g_ascii_tolower ((gchar
) parm
);
392 for (i
= 0; i
< r
->count
; i
++)
394 if (r
->texts
[i
].hotkey
!= NULL
)
396 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
403 radio_callback (w
, WIDGET_KEY
, ' ');
408 return MSG_NOT_HANDLED
;
415 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
416 radio_callback (w
, WIDGET_FOCUS
, ' ');
426 return MSG_NOT_HANDLED
;
430 if (r
->count
- 1 > r
->pos
)
436 return MSG_NOT_HANDLED
;
439 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
440 radio_callback (w
, WIDGET_FOCUS
, ' ');
441 widget_move (&r
->widget
, r
->pos
, 1);
447 for (i
= 0; i
< r
->count
; i
++)
449 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
450 widget_selectcolor (w
, focused
, FALSE
);
451 widget_move (&r
->widget
, i
, 0);
452 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
453 draw_hotkey (w
, r
->texts
[i
], focused
);
458 for (i
= 0; i
< r
->count
; i
++)
460 release_hotkey (r
->texts
[i
]);
466 return default_proc (msg
, parm
);
471 radio_event (Gpm_Event
* event
, void *data
)
476 if (event
->type
& (GPM_DOWN
| GPM_UP
))
478 Dlg_head
*h
= r
->widget
.parent
;
480 r
->pos
= event
->y
- 1;
481 dlg_select_widget (r
);
482 if (event
->type
& GPM_UP
)
484 radio_callback (w
, WIDGET_KEY
, ' ');
485 radio_callback (w
, WIDGET_FOCUS
, 0);
486 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
494 radio_new (int y
, int x
, int count
, const char **texts
)
496 WRadio
*result
= g_new (WRadio
, 1);
499 /* Compute the longest string */
500 result
->texts
= g_new (struct hotkey_t
, count
);
503 for (i
= 0; i
< count
; i
++)
505 result
->texts
[i
] = parse_hotkey (texts
[i
]);
506 m
= hotkey_width (result
->texts
[i
]);
511 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
515 result
->count
= count
;
516 widget_want_hotkey (result
->widget
, 1);
522 /* Checkbutton widget */
524 static int check_event (Gpm_Event
* event
, void *);
527 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
529 WCheck
*c
= (WCheck
*) w
;
530 Dlg_head
*h
= c
->widget
.parent
;
535 if (c
->text
.hotkey
!= NULL
)
537 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
540 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
544 return MSG_NOT_HANDLED
;
548 return MSG_NOT_HANDLED
;
550 c
->state
^= C_CHANGE
;
551 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
552 check_callback (w
, WIDGET_FOCUS
, ' ');
556 widget_move (&c
->widget
, 0, 1);
562 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
563 widget_move (&c
->widget
, 0, 0);
564 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
565 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
569 release_hotkey (c
->text
);
573 return default_proc (msg
, parm
);
578 check_event (Gpm_Event
* event
, void *data
)
583 if (event
->type
& (GPM_DOWN
| GPM_UP
))
585 Dlg_head
*h
= c
->widget
.parent
;
587 dlg_select_widget (c
);
588 if (event
->type
& GPM_UP
)
590 check_callback (w
, WIDGET_KEY
, ' ');
591 check_callback (w
, WIDGET_FOCUS
, 0);
592 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
600 check_new (int y
, int x
, int state
, const char *text
)
602 WCheck
*c
= g_new (WCheck
, 1);
604 c
->text
= parse_hotkey (text
);
606 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
607 c
->state
= state
? C_BOOL
: 0;
608 widget_want_hotkey (c
->widget
, 1);
614 save_text_to_clip_file (const char *text
)
621 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
622 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
623 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
629 str_len
= strlen (text
);
630 ret
= mc_write (file
, (char *) text
, str_len
);
632 return ret
== (ssize_t
) str_len
;
636 load_text_from_clip_file (char **text
)
641 gboolean first
= TRUE
;
643 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
644 f
= fopen (fname
, "r");
652 while (fgets (buf
, sizeof (buf
), f
))
659 if (buf
[len
- 1] == '\n')
665 *text
= g_strdup (buf
);
669 /* remove \n on EOL */
672 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
681 return (*text
!= NULL
);
685 panel_save_curent_file_to_clip_file (void)
689 if (current_panel
->marked
== 0)
690 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
694 gboolean first
= TRUE
;
697 for (i
= 0; i
< current_panel
->count
; i
++)
698 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
699 { /* Skip the unmarked ones */
702 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
707 /* Add empty lines after the file */
711 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
719 res
= save_text_to_clip_file (flist
);
729 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
731 WLabel
*l
= (WLabel
*) w
;
732 Dlg_head
*h
= l
->widget
.parent
;
739 /* We don't want to get the focus */
741 return MSG_NOT_HANDLED
;
745 char *p
= l
->text
, *q
, c
= 0;
752 tty_setcolor (DEFAULT_COLOR
);
754 tty_setcolor (DLG_NORMALC (h
));
758 q
= strchr (p
, '\n');
765 widget_move (&l
->widget
, y
, 0);
766 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
782 return default_proc (msg
, parm
);
787 label_set_text (WLabel
* label
, const char *text
)
789 int newcols
= label
->widget
.cols
;
792 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
793 return; /* Flickering is not nice */
795 g_free (label
->text
);
799 label
->text
= g_strdup (text
);
800 if (label
->auto_adjust_cols
)
802 str_msg_term_size (text
, &newlines
, &newcols
);
803 if (newcols
> label
->widget
.cols
)
804 label
->widget
.cols
= newcols
;
805 if (newlines
> label
->widget
.lines
)
806 label
->widget
.lines
= newlines
;
812 if (label
->widget
.parent
)
813 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
815 if (newcols
< label
->widget
.cols
)
816 label
->widget
.cols
= newcols
;
820 label_new (int y
, int x
, const char *text
)
827 str_msg_term_size (text
, &lines
, &cols
);
829 l
= g_new (WLabel
, 1);
830 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
831 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
832 l
->auto_adjust_cols
= 1;
834 widget_want_cursor (l
->widget
, 0);
839 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
841 WHLine
*l
= (WHLine
*) w
;
842 Dlg_head
*h
= l
->widget
.parent
;
848 if (l
->auto_adjust_cols
)
850 if (((w
->parent
->flags
& DLG_COMPACT
) != 0))
853 w
->cols
= w
->parent
->cols
;
857 w
->x
= w
->parent
->x
+ 1;
858 w
->cols
= w
->parent
->cols
- 2;
863 /* We don't want to get the focus */
864 return MSG_NOT_HANDLED
;
868 tty_setcolor (DEFAULT_COLOR
);
870 tty_setcolor (DLG_NORMALC (h
));
872 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
874 if (l
->auto_adjust_cols
)
876 widget_move (w
, 0, 0);
877 tty_print_alt_char (ACS_LTEE
, FALSE
);
878 widget_move (w
, 0, w
->cols
- 1);
879 tty_print_alt_char (ACS_RTEE
, FALSE
);
884 return default_proc (msg
, parm
);
890 hline_new (int y
, int x
, int width
)
896 l
= g_new (WHLine
, 1);
897 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
898 l
->auto_adjust_cols
= (width
< 0);
899 l
->transparent
= FALSE
;
900 widget_want_cursor (l
->widget
, 0);
904 /* Gauge widget (progress indicator) */
905 /* Currently width is hardcoded here for text mode */
909 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
911 WGauge
*g
= (WGauge
*) w
;
912 Dlg_head
*h
= g
->widget
.parent
;
914 if (msg
== WIDGET_INIT
)
917 /* We don't want to get the focus */
918 if (msg
== WIDGET_FOCUS
)
919 return MSG_NOT_HANDLED
;
921 if (msg
== WIDGET_DRAW
)
923 widget_move (&g
->widget
, 0, 0);
924 tty_setcolor (DLG_NORMALC (h
));
926 tty_printf ("%*s", gauge_len
, "");
929 int percentage
, columns
;
930 long total
= g
->max
, done
= g
->current
;
932 if (total
<= 0 || done
< 0)
939 while (total
> 65535)
944 percentage
= (200 * done
/ total
+ 1) / 2;
945 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
946 tty_print_char ('[');
947 if (g
->from_left_to_right
)
949 tty_setcolor (GAUGE_COLOR
);
950 tty_printf ("%*s", (int) columns
, "");
951 tty_setcolor (DLG_NORMALC (h
));
952 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
956 tty_setcolor (DLG_NORMALC (h
));
957 tty_printf ("%*s", gauge_len
- columns
- 7, "");
958 tty_setcolor (GAUGE_COLOR
);
959 tty_printf ("%*s", columns
, "");
960 tty_setcolor (DLG_NORMALC (h
));
961 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
967 return default_proc (msg
, parm
);
971 gauge_set_value (WGauge
* g
, int max
, int current
)
973 if (g
->current
== current
&& g
->max
== max
)
974 return; /* Do not flicker */
976 max
= 1; /* I do not like division by zero :) */
978 g
->current
= current
;
980 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
984 gauge_show (WGauge
* g
, int shown
)
986 if (g
->shown
== shown
)
989 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
993 gauge_new (int y
, int x
, int shown
, int max
, int current
)
995 WGauge
*g
= g_new (WGauge
, 1);
997 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
1000 max
= 1; /* I do not like division by zero :) */
1002 g
->current
= current
;
1003 g
->from_left_to_right
= TRUE
;
1004 widget_want_cursor (g
->widget
, 0);
1011 /* {{{ history button */
1013 #define LARGE_HISTORY_BUTTON 1
1015 #ifdef LARGE_HISTORY_BUTTON
1016 # define HISTORY_BUTTON_WIDTH 3
1018 # define HISTORY_BUTTON_WIDTH 1
1021 #define should_show_history_button(in) \
1022 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1025 draw_history_button (WInput
* in
)
1028 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1029 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1030 #ifdef LARGE_HISTORY_BUTTON
1033 h
= in
->widget
.parent
;
1034 tty_setcolor (NORMAL_COLOR
);
1035 tty_print_string ("[ ]");
1036 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1037 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1041 tty_setcolor (MARKED_COLOR
);
1046 /* }}} history button */
1049 /* Input widgets now have a global kill ring */
1050 /* Pointer to killed data */
1051 static char *kill_buffer
= NULL
;
1054 update_input (WInput
* in
, int clear_first
)
1056 int has_history
= 0;
1058 int buf_len
= str_length (in
->buffer
);
1062 if (should_show_history_button (in
))
1063 has_history
= HISTORY_BUTTON_WIDTH
;
1065 if (in
->disable_update
)
1068 pw
= str_term_width2 (in
->buffer
, in
->point
);
1070 /* Make the point visible */
1071 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1074 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1075 if (in
->term_first_shown
< 0)
1076 in
->term_first_shown
= 0;
1079 /* Adjust the mark */
1080 if (in
->mark
> buf_len
)
1084 draw_history_button (in
);
1086 tty_setcolor (in
->color
);
1088 widget_move (&in
->widget
, 0, 0);
1090 if (!in
->is_password
)
1092 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1093 in
->field_width
- has_history
));
1098 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++)
1102 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1105 str_cnext_char (&cp
);
1114 winput_set_origin (WInput
* in
, int x
, int field_width
)
1117 in
->field_width
= in
->widget
.cols
= field_width
;
1118 update_input (in
, 0);
1121 /* {{{ history saving and loading */
1123 int num_history_items_recorded
= 60;
1126 This loads and saves the history of an input line to and from the
1127 widget. It is called with the widgets history name on creation of the
1128 widget, and returns the GList list. It stores histories in the file
1129 ~/.mc/history in using the profile code.
1131 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1132 function) then input_new assigns the default text to be the last text
1133 entered, or "" if not found.
1137 history_get (const char *input_name
)
1144 size_t keys_num
= 0;
1147 if (num_history_items_recorded
== 0) /* this is how to disable */
1149 if ((input_name
== NULL
) || (*input_name
== '\0'))
1152 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1153 cfg
= mc_config_init (profile
);
1155 /* get number of keys */
1156 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1159 for (i
= 0; i
< keys_num
; i
++)
1162 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1163 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1165 if (this_entry
!= NULL
)
1166 hist
= list_append_unique (hist
, this_entry
);
1169 mc_config_deinit (cfg
);
1172 /* return pointer to the last entry in the list */
1173 return g_list_last (hist
);
1177 history_put (const char *input_name
, GList
* h
)
1183 if (num_history_items_recorded
== 0) /* this is how to disable */
1185 if ((input_name
== NULL
) || (*input_name
== '\0'))
1190 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1192 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1196 /* Make sure the history is only readable by the user */
1197 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1203 /* go to end of list */
1204 h
= g_list_last (h
);
1206 /* go back 60 places */
1207 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1208 h
= g_list_previous (h
);
1210 cfg
= mc_config_init (profile
);
1212 if (input_name
!= NULL
)
1213 mc_config_del_group (cfg
, input_name
);
1215 /* dump history into profile */
1216 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1218 char *text
= (char *) h
->data
;
1220 /* We shouldn't have null entries, but let's be sure */
1224 g_snprintf (key
, sizeof (key
), "%d", i
++);
1225 mc_config_set_string (cfg
, input_name
, key
, text
);
1229 mc_config_save_file (cfg
, NULL
);
1230 mc_config_deinit (cfg
);
1234 /* }}} history saving and loading */
1237 /* {{{ history display */
1242 return _(" History ");
1253 dlg_hist_reposition (Dlg_head
* dlg_head
)
1255 dlg_hist_data
*data
;
1256 int x
= 0, y
, he
, wi
;
1259 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1260 return MSG_NOT_HANDLED
;
1262 data
= (dlg_hist_data
*) dlg_head
->data
;
1264 y
= data
->widget
->y
;
1265 he
= data
->count
+ 2;
1267 if (he
<= y
|| y
> (LINES
- 6))
1269 he
= min (he
, y
- 1);
1275 he
= min (he
, LINES
- y
);
1278 if (data
->widget
->x
> 2)
1279 x
= data
->widget
->x
- 2;
1281 wi
= data
->maxlen
+ 4;
1283 if ((wi
+ x
) > COLS
)
1285 wi
= min (wi
, COLS
);
1289 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1295 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1300 return dlg_hist_reposition (h
);
1303 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1308 show_hist (GList
** history
, Widget
* widget
)
1310 GList
*z
, *hlist
= NULL
, *hi
;
1311 size_t maxlen
, i
, count
= 0;
1313 Dlg_head
*query_dlg
;
1314 WListbox
*query_list
;
1315 dlg_hist_data hist_data
;
1317 if (*history
== NULL
)
1320 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1322 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1326 i
= str_term_width1 ((char *) z
->data
);
1327 maxlen
= max (maxlen
, i
);
1330 entry
= g_new0 (WLEntry
, 1);
1331 /* history is being reverted here */
1332 entry
->text
= g_strdup ((char *) z
->data
);
1333 hlist
= g_list_prepend (hlist
, entry
);
1336 hist_data
.widget
= widget
;
1337 hist_data
.count
= count
;
1338 hist_data
.maxlen
= maxlen
;
1341 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1342 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1343 query_dlg
->data
= &hist_data
;
1345 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1347 /* this call makes list stick to all sides of dialog, effectively make
1348 it be resized with dialog */
1349 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1351 /* to avoid diplicating of (calculating sizes in two places)
1352 code, call dlg_hist_callback function here, to set dialog and
1354 The main idea - create 4x4 dialog and add 2x2 list in
1355 center of it, and let dialog function resize it to needed
1357 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1359 if (query_dlg
->y
< widget
->y
)
1361 /* draw list entries from bottom upto top */
1362 listbox_set_list (query_list
, hlist
);
1363 listbox_select_last (query_list
);
1367 /* draw list entries from top downto bottom */
1368 /* revert history direction */
1369 hlist
= g_list_reverse (hlist
);
1370 listbox_set_list (query_list
, hlist
);
1373 if (run_dlg (query_dlg
) != B_CANCEL
)
1377 listbox_get_current (query_list
, &q
, NULL
);
1382 /* get modified history from dialog */
1384 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1388 entry
= (WLEntry
*) hi
->data
;
1389 /* history is being reverted here again */
1390 z
= g_list_prepend (z
, entry
->text
);
1394 destroy_dlg (query_dlg
);
1396 /* restore history direction */
1397 if (query_dlg
->y
< widget
->y
)
1398 z
= g_list_reverse (z
);
1400 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1401 g_list_free (*history
);
1402 *history
= g_list_last (z
);
1408 do_show_hist (WInput
* in
)
1412 r
= show_hist (&in
->history
, &in
->widget
);
1415 assign_text (in
, r
);
1420 /* }}} history display */
1423 input_destroy (WInput
* in
)
1427 fprintf (stderr
, "Internal error: null Input *\n");
1433 if (in
->history
!= NULL
)
1435 if (!in
->is_password
&& (((Widget
*) in
)->parent
->ret_value
!= B_CANCEL
))
1436 history_put (in
->history_name
, in
->history
);
1438 in
->history
= g_list_first (in
->history
);
1439 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1440 g_list_free (in
->history
);
1443 g_free (in
->buffer
);
1444 free_completions (in
);
1445 g_free (in
->history_name
);
1447 g_free (kill_buffer
);
1452 input_disable_update (WInput
* in
)
1454 in
->disable_update
++;
1458 input_enable_update (WInput
* in
)
1460 in
->disable_update
--;
1461 update_input (in
, 0);
1466 push_history (WInput
* in
, const char *text
)
1468 /* input widget where urls with passwords are entered without any
1470 const char *password_input_fields
[] = {
1471 N_(" Link to a remote machine "),
1472 N_(" FTP to machine "),
1473 N_(" SMB link to machine ")
1475 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1485 for (i
= 0; i
< ELEMENTS
; i
++)
1486 password_input_fields
[i
] = _(password_input_fields
[i
]);
1489 t
= g_strstrip (g_strdup (text
));
1492 t
= g_strdup (empty
? "" : text
);
1494 if (in
->history_name
!= NULL
)
1496 const char *p
= in
->history_name
+ 3;
1498 for (i
= 0; i
< ELEMENTS
; i
++)
1499 if (strcmp (p
, password_input_fields
[i
]) == 0)
1502 strip_password (t
, i
>= ELEMENTS
);
1505 in
->history
= list_append_unique (in
->history
, t
);
1509 /* Cleans the input line and adds the current text to the history */
1511 new_input (WInput
* in
)
1513 push_history (in
, in
->buffer
);
1515 in
->buffer
[0] = '\0';
1519 free_completions (in
);
1520 update_input (in
, 0);
1524 move_buffer_backward (WInput
* in
, int start
, int end
)
1527 int str_len
= str_length (in
->buffer
);
1528 if (start
>= str_len
|| end
> str_len
+ 1)
1531 pos
= str_offset_to_pos (in
->buffer
, start
);
1532 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1534 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1535 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1539 insert_char (WInput
* in
, int c_code
)
1545 return MSG_NOT_HANDLED
;
1547 if (in
->charpoint
>= MB_LEN_MAX
)
1550 in
->charbuf
[in
->charpoint
] = c_code
;
1553 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1557 in
->charpoint
= 0; /* broken multibyte char, skip */
1562 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1564 /* Expand the buffer */
1565 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1566 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1570 in
->current_max_size
= new_length
;
1574 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1576 /* bytes from begin */
1577 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1579 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1581 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1582 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1584 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1593 beginning_of_line (WInput
* in
)
1600 end_of_line (WInput
* in
)
1602 in
->point
= str_length (in
->buffer
);
1607 backward_char (WInput
* in
)
1609 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1613 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1619 forward_char (WInput
* in
)
1621 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1624 in
->point
+= str_cnext_noncomb_char (&act
);
1630 forward_word (WInput
* in
)
1632 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1634 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1636 str_cnext_char (&p
);
1639 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1641 str_cnext_char (&p
);
1647 backward_word (WInput
* in
)
1652 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1653 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1655 while (p
!= in
->buffer
)
1658 str_cprev_char (&p
);
1659 if (!str_isspace (p
) && !str_ispunct (p
))
1666 while (p
!= in
->buffer
)
1668 str_cprev_char (&p
);
1669 if (str_isspace (p
) || str_ispunct (p
))
1677 key_left (WInput
* in
)
1683 key_ctrl_left (WInput
* in
)
1689 key_right (WInput
* in
)
1695 key_ctrl_right (WInput
* in
)
1700 backward_delete (WInput
* in
)
1702 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1708 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1709 move_buffer_backward (in
, start
, in
->point
);
1716 delete_char (WInput
* in
)
1718 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1719 int end
= in
->point
;
1721 end
+= str_cnext_noncomb_char (&act
);
1723 move_buffer_backward (in
, in
->point
, end
);
1729 copy_region (WInput
* in
, int x_first
, int x_last
)
1731 int first
= min (x_first
, x_last
);
1732 int last
= max (x_first
, x_last
);
1736 /* Copy selected files to clipboard */
1737 panel_save_curent_file_to_clip_file ();
1741 g_free (kill_buffer
);
1743 first
= str_offset_to_pos (in
->buffer
, first
);
1744 last
= str_offset_to_pos (in
->buffer
, last
);
1746 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1747 save_text_to_clip_file (kill_buffer
);
1751 delete_region (WInput
* in
, int x_first
, int x_last
)
1753 int first
= min (x_first
, x_last
);
1754 int last
= max (x_first
, x_last
);
1758 if (in
->mark
> first
)
1760 last
= str_offset_to_pos (in
->buffer
, last
);
1761 first
= str_offset_to_pos (in
->buffer
, first
);
1762 len
= strlen (&in
->buffer
[last
]) + 1;
1763 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1769 kill_word (WInput
* in
)
1771 int old_point
= in
->point
;
1775 new_point
= in
->point
;
1776 in
->point
= old_point
;
1778 copy_region (in
, old_point
, new_point
);
1779 delete_region (in
, old_point
, new_point
);
1786 back_kill_word (WInput
* in
)
1788 int old_point
= in
->point
;
1792 new_point
= in
->point
;
1793 in
->point
= old_point
;
1795 copy_region (in
, old_point
, new_point
);
1796 delete_region (in
, old_point
, new_point
);
1801 set_mark (WInput
* in
)
1803 in
->mark
= in
->point
;
1807 kill_save (WInput
* in
)
1809 copy_region (in
, in
->mark
, in
->point
);
1813 kill_region (WInput
* in
)
1816 delete_region (in
, in
->point
, in
->mark
);
1820 clear_region (WInput
* in
)
1822 delete_region (in
, in
->point
, in
->mark
);
1828 if (kill_buffer
!= NULL
)
1833 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1834 insert_char (in
, *p
);
1840 kill_line (WInput
* in
)
1842 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1843 g_free (kill_buffer
);
1844 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1845 in
->buffer
[chp
] = '\0';
1850 ins_from_clip (WInput
* in
)
1854 if (load_text_from_clip_file (&p
))
1858 for (pp
= p
; *pp
!= '\0'; pp
++)
1859 insert_char (in
, *pp
);
1866 assign_text (WInput
* in
, const char *text
)
1868 free_completions (in
);
1869 g_free (in
->buffer
);
1870 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1871 in
->current_max_size
= strlen (in
->buffer
) + 1;
1872 in
->point
= str_length (in
->buffer
);
1879 hist_prev (WInput
* in
)
1887 push_history (in
, in
->buffer
);
1889 prev
= g_list_previous (in
->history
);
1893 assign_text (in
, (char *) prev
->data
);
1899 hist_next (WInput
* in
)
1903 push_history (in
, in
->buffer
);
1904 assign_text (in
, "");
1911 if (!in
->history
->next
)
1913 assign_text (in
, "");
1917 in
->history
= g_list_next (in
->history
);
1918 assign_text (in
, (char *) in
->history
->data
);
1923 port_region_marked_for_delete (WInput
* in
)
1925 in
->buffer
[0] = '\0';
1932 input_execute_cmd (WInput
* in
, unsigned long command
)
1934 cb_ret_t res
= MSG_HANDLED
;
1939 beginning_of_line (in
);
1944 case CK_InputMoveLeft
:
1947 case CK_InputWordLeft
:
1950 case CK_InputMoveRight
:
1953 case CK_InputWordRight
:
1954 key_ctrl_right (in
);
1956 case CK_InputBackwardChar
:
1959 case CK_InputBackwardWord
:
1962 case CK_InputForwardChar
:
1965 case CK_InputForwardWord
:
1968 case CK_InputBackwardDelete
:
1969 backward_delete (in
);
1971 case CK_InputDeleteChar
:
1974 case CK_InputKillWord
:
1977 case CK_InputBackwardKillWord
:
1978 back_kill_word (in
);
1980 case CK_InputSetMark
:
1983 case CK_InputKillRegion
:
1986 case CK_InputClearLine
:
1989 case CK_InputKillSave
:
1998 case CK_InputKillLine
:
2001 case CK_InputHistoryPrev
:
2004 case CK_InputHistoryNext
:
2007 case CK_InputHistoryShow
:
2010 case CK_InputComplete
:
2014 res
= MSG_NOT_HANDLED
;
2020 /* This function is a test for a special input key used in complete.c */
2021 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2022 and 2 if it is a complete key */
2024 is_in_input_map (WInput
* in
, int key
)
2027 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2028 if (key
== input_map
[i
].key
)
2030 input_execute_cmd (in
, input_map
[i
].command
);
2031 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2037 handle_char (WInput
* in
, int key
)
2042 v
= MSG_NOT_HANDLED
;
2046 free_completions (in
);
2047 v
= insert_char (in
, key
);
2048 update_input (in
, 1);
2052 for (i
= 0; input_map
[i
].key
; i
++)
2054 if (key
== input_map
[i
].key
)
2056 if (input_map
[i
].command
!= CK_InputComplete
)
2057 free_completions (in
);
2058 input_execute_cmd (in
, input_map
[i
].command
);
2059 update_input (in
, 1);
2064 if (input_map
[i
].command
== 0)
2067 return MSG_NOT_HANDLED
;
2069 port_region_marked_for_delete (in
);
2070 free_completions (in
);
2071 v
= insert_char (in
, key
);
2073 update_input (in
, 1);
2077 /* Inserts text in input line */
2079 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2081 input_disable_update (in
);
2082 while (*text
!= '\0')
2083 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2084 if (insert_extra_space
)
2085 handle_char (in
, ' ');
2086 input_enable_update (in
);
2087 update_input (in
, 1);
2091 input_set_point (WInput
* in
, int pos
)
2093 int max_pos
= str_length (in
->buffer
);
2097 if (pos
!= in
->point
)
2098 free_completions (in
);
2101 update_input (in
, 1);
2105 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2107 WInput
*in
= (WInput
*) w
;
2113 if (parm
== XCTRL ('q'))
2116 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2121 /* Keys we want others to handle */
2122 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2123 || parm
== KEY_F (10) || parm
== '\n')
2124 return MSG_NOT_HANDLED
;
2126 /* When pasting multiline text, insert literal Enter */
2127 if ((parm
& ~KEY_M_MASK
) == '\n')
2130 v
= handle_char (in
, '\n');
2135 return handle_char (in
, parm
);
2137 case WIDGET_COMMAND
:
2138 return input_execute_cmd (in
, parm
);
2141 case WIDGET_UNFOCUS
:
2143 update_input (in
, 0);
2147 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2148 - in
->term_first_shown
);
2151 case WIDGET_DESTROY
:
2156 return default_proc (msg
, parm
);
2161 input_event (Gpm_Event
* event
, void *data
)
2165 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2167 dlg_select_widget (in
);
2169 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2170 && should_show_history_button (in
))
2176 in
->point
= str_length (in
->buffer
);
2177 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2178 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2180 update_input (in
, 1);
2186 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
2187 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2189 WInput
*in
= g_new (WInput
, 1);
2190 size_t initial_buffer_len
;
2192 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2195 in
->history_name
= NULL
;
2197 if ((histname
!= NULL
) && (*histname
!= '\0'))
2199 in
->history_name
= g_strdup (histname
);
2200 in
->history
= history_get (histname
);
2203 if (def_text
== NULL
)
2205 else if (def_text
== INPUT_LAST_TEXT
)
2207 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2208 def_text
= (char *) in
->history
->data
;
2213 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2214 in
->widget
.options
|= W_IS_INPUT
;
2215 in
->completions
= NULL
;
2216 in
->completion_flags
= completion_flags
;
2217 in
->current_max_size
= initial_buffer_len
;
2218 in
->buffer
= g_new (char, initial_buffer_len
);
2220 in
->field_width
= width
;
2222 in
->term_first_shown
= 0;
2223 in
->disable_update
= 0;
2226 in
->is_password
= 0;
2228 strcpy (in
->buffer
, def_text
);
2229 in
->point
= str_length (in
->buffer
);
2236 /* Listbox widget */
2238 /* Should draw the scrollbar, but currently draws only
2239 * indications that there is more information
2243 listbox_entry_free (void *data
)
2251 listbox_drawscroll (WListbox
* l
)
2253 const int max_line
= l
->widget
.lines
- 1;
2257 /* Are we at the top? */
2258 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2260 tty_print_one_vline (TRUE
);
2262 tty_print_char ('^');
2264 /* Are we at the bottom? */
2265 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2266 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2267 tty_print_one_vline (TRUE
);
2269 tty_print_char ('v');
2271 /* Now draw the nice relative pointer */
2273 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2275 for (i
= 1; i
< max_line
; i
++)
2277 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2279 tty_print_one_vline (TRUE
);
2281 tty_print_char ('*');
2286 listbox_draw (WListbox
* l
, gboolean focused
)
2288 const Dlg_head
*h
= l
->widget
.parent
;
2289 const int normalc
= DLG_NORMALC (h
);
2290 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2297 le
= g_list_nth (l
->list
, l
->top
);
2298 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2299 pos
= (le
== NULL
) ? 0 : l
->top
;
2301 for (i
= 0; i
< l
->widget
.lines
; i
++)
2305 /* Display the entry */
2306 if (pos
== l
->pos
&& sel_line
== -1)
2309 tty_setcolor (selc
);
2312 tty_setcolor (normalc
);
2314 widget_move (&l
->widget
, i
, 1);
2316 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2320 WLEntry
*e
= (WLEntry
*) le
->data
;
2322 le
= g_list_next (le
);
2326 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2329 l
->cursor_y
= sel_line
;
2331 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2333 tty_setcolor (normalc
);
2334 listbox_drawscroll (l
);
2339 listbox_check_hotkey (WListbox
* l
, int key
)
2344 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2346 WLEntry
*e
= (WLEntry
*) le
->data
;
2348 if (e
->hotkey
== key
)
2355 /* Selects the last entry and scrolls the list to the bottom */
2357 listbox_select_last (WListbox
* l
)
2359 l
->pos
= l
->count
- 1;
2360 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2363 /* Selects the first entry and scrolls the list to the top */
2365 listbox_select_first (WListbox
* l
)
2367 l
->pos
= l
->top
= 0;
2371 listbox_set_list (WListbox
* l
, GList
* list
)
2373 listbox_remove_list (l
);
2378 l
->top
= l
->pos
= 0;
2379 l
->count
= g_list_length (list
);
2384 listbox_remove_list (WListbox
* l
)
2386 if ((l
!= NULL
) && (l
->count
!= 0))
2388 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2389 g_list_free (l
->list
);
2391 l
->count
= l
->pos
= l
->top
= 0;
2396 listbox_remove_current (WListbox
* l
)
2398 if ((l
!= NULL
) && (l
->count
!= 0))
2402 current
= g_list_nth (l
->list
, l
->pos
);
2403 l
->list
= g_list_remove_link (l
->list
, current
);
2404 listbox_entry_free ((WLEntry
*) current
->data
);
2405 g_list_free_1 (current
);
2409 l
->top
= l
->pos
= 0;
2410 else if (l
->pos
>= l
->count
)
2411 l
->pos
= l
->count
- 1;
2416 listbox_select_entry (WListbox
* l
, int dest
)
2420 gboolean top_seen
= FALSE
;
2426 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2436 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2437 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2442 /* If we are unable to find it, set decent values */
2443 l
->pos
= l
->top
= 0;
2446 /* Selects from base the pos element */
2448 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2450 int last
= l
->count
- 1;
2460 listbox_fwd (WListbox
* l
)
2462 if (l
->pos
+ 1 >= l
->count
)
2463 listbox_select_first (l
);
2465 listbox_select_entry (l
, l
->pos
+ 1);
2469 listbox_back (WListbox
* l
)
2472 listbox_select_last (l
);
2474 listbox_select_entry (l
, l
->pos
- 1);
2477 /* Return MSG_HANDLED if we want a redraw */
2479 listbox_key (WListbox
* l
, int key
)
2483 cb_ret_t j
= MSG_NOT_HANDLED
;
2485 if (l
->list
== NULL
)
2486 return MSG_NOT_HANDLED
;
2488 /* focus on listbox item N by '0'..'9' keys */
2489 if (key
>= '0' && key
<= '9')
2491 int oldpos
= l
->pos
;
2492 listbox_select_entry (l
, key
- '0');
2494 /* need scroll to item? */
2495 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2506 listbox_select_first (l
);
2512 listbox_select_last (l
);
2527 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2536 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2547 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2548 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2550 listbox_remove_current (l
);
2551 if ((l
->top
> 0) && (is_last
|| is_more
))
2556 case (KEY_M_SHIFT
| KEY_DC
):
2558 if (l
->deletable
&& confirm_history_cleanup
2559 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2560 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2561 _("Do you want clean this history?"),
2562 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2564 listbox_remove_list (l
);
2577 listbox_destroy (WListbox
* l
)
2579 /* don't delete list in modifable listbox */
2581 listbox_remove_list (l
);
2585 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2587 WListbox
*l
= (WListbox
*) w
;
2588 Dlg_head
*h
= l
->widget
.parent
;
2600 pos
= listbox_check_hotkey (l
, parm
);
2602 return MSG_NOT_HANDLED
;
2604 listbox_select_entry (l
, pos
);
2605 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2607 if (l
->cback
!= NULL
)
2608 action
= l
->cback (l
);
2610 action
= LISTBOX_DONE
;
2612 if (action
== LISTBOX_DONE
)
2614 h
->ret_value
= B_ENTER
;
2622 ret_code
= listbox_key (l
, parm
);
2623 if (ret_code
!= MSG_NOT_HANDLED
)
2625 listbox_draw (l
, TRUE
);
2626 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2631 widget_move (&l
->widget
, l
->cursor_y
, 0);
2632 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2636 case WIDGET_UNFOCUS
:
2638 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2641 case WIDGET_DESTROY
:
2642 listbox_destroy (l
);
2645 case WIDGET_RESIZED
:
2649 return default_proc (msg
, parm
);
2654 listbox_event (Gpm_Event
* event
, void *data
)
2659 Dlg_head
*h
= l
->widget
.parent
;
2662 if (event
->type
& GPM_DOWN
)
2663 dlg_select_widget (l
);
2665 if (l
->list
== NULL
)
2668 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2670 int ret
= MOU_REPEAT
;
2672 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2676 for (i
= -event
->y
; i
>= 0; i
--)
2678 else if (event
->y
> l
->widget
.lines
)
2679 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2681 else if (event
->buttons
& GPM_B_UP
)
2686 else if (event
->buttons
& GPM_B_DOWN
)
2692 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2694 /* We need to refresh ourselves since the dialog manager doesn't */
2695 /* know about this event */
2696 listbox_draw (l
, TRUE
);
2701 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2705 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2706 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2709 dlg_select_widget (l
);
2710 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2712 if (l
->cback
!= NULL
)
2713 action
= l
->cback (l
);
2715 action
= LISTBOX_DONE
;
2717 if (action
== LISTBOX_DONE
)
2719 h
->ret_value
= B_ENTER
;
2728 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2730 WListbox
*l
= g_new (WListbox
, 1);
2735 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2738 l
->top
= l
->pos
= 0;
2740 l
->deletable
= deletable
;
2741 l
->cback
= callback
;
2742 l
->allow_duplicates
= TRUE
;
2743 l
->scrollbar
= !tty_is_slow ();
2744 widget_want_hotkey (l
->widget
, 1);
2750 listbox_entry_cmp (const void *a
, const void *b
)
2752 const WLEntry
*ea
= (const WLEntry
*) a
;
2753 const WLEntry
*eb
= (const WLEntry
*) b
;
2755 return strcmp (ea
->text
, eb
->text
);
2758 /* Listbox item adding function */
2760 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2764 case LISTBOX_APPEND_AT_END
:
2765 l
->list
= g_list_append (l
->list
, e
);
2768 case LISTBOX_APPEND_BEFORE
:
2769 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2774 case LISTBOX_APPEND_AFTER
:
2775 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2778 case LISTBOX_APPEND_SORTED
:
2779 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2790 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2797 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2800 entry
= g_new (WLEntry
, 1);
2801 entry
->text
= g_strdup (text
);
2803 entry
->hotkey
= hotkey
;
2805 listbox_append_item (l
, entry
, pos
);
2811 listbox_search_text (WListbox
* l
, const char *text
)
2818 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2820 WLEntry
*e
= (WLEntry
*) le
->data
;
2822 if (strcmp (e
->text
, text
) == 0)
2830 /* Returns the current string text as well as the associated extra data */
2832 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2838 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2843 *string
= ok
? e
->text
: NULL
;
2846 *extra
= ok
? e
->data
: NULL
;
2850 /* ButtonBar widget */
2852 /* returns TRUE if a function has been called, FALSE otherwise. */
2854 buttonbar_call (WButtonBar
* bb
, int i
)
2856 cb_ret_t ret
= MSG_NOT_HANDLED
;
2858 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
2859 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2860 (Widget
*) bb
, DLG_ACTION
,
2861 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
2865 /* calculate width of one button, width is never lesser than 7 */
2867 buttonbat_get_button_width (void)
2869 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2870 return (result
>= 7) ? result
: 7;
2874 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2876 WButtonBar
*bb
= (WButtonBar
*) w
;
2883 return MSG_NOT_HANDLED
;
2886 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2887 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2889 return MSG_NOT_HANDLED
;
2895 int count_free_positions
;
2897 widget_move (&bb
->widget
, 0, 0);
2898 tty_setcolor (DEFAULT_COLOR
);
2899 bb
->btn_width
= buttonbat_get_button_width ();
2900 tty_printf ("%-*s", bb
->widget
.cols
, "");
2901 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2903 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++)
2905 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2906 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2907 tty_printf ("%2d", i
+ 1);
2908 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2909 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2910 tty_print_string (str_fit_to_term (text
,
2911 bb
->btn_width
- 2 + (int) (offset
<
2912 count_free_positions
),
2915 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2921 case WIDGET_DESTROY
:
2922 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2923 g_free (bb
->labels
[i
].text
);
2927 return default_proc (msg
, parm
);
2932 buttonbar_event (Gpm_Event
* event
, void *data
)
2934 WButtonBar
*bb
= data
;
2937 if (!(event
->type
& GPM_UP
))
2941 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2942 if (button
< BUTTONBAR_LABELS_NUM
)
2943 buttonbar_call (bb
, button
);
2948 buttonbar_new (gboolean visible
)
2952 bb
= g_new0 (WButtonBar
, 1);
2954 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
2955 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2956 bb
->visible
= visible
;
2957 widget_want_hotkey (bb
->widget
, 1);
2958 widget_want_cursor (bb
->widget
, 0);
2959 bb
->btn_width
= buttonbat_get_button_width ();
2965 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
2967 g_free (bb
->labels
[lc_index
- 1].text
);
2968 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2971 /* Find ButtonBar widget in the dialog */
2973 find_buttonbar (const Dlg_head
* h
)
2975 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2979 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
2980 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
2982 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
2984 unsigned long command
= CK_Ignore_Key
;
2987 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2989 if ((text
== NULL
) || (text
[0] == '\0'))
2990 set_label_text (bb
, idx
, "");
2992 set_label_text (bb
, idx
, text
);
2994 bb
->labels
[idx
- 1].command
= command
;
2995 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
3000 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
3002 bb
->visible
= visible
;
3006 buttonbar_redraw (WButtonBar
* bb
)
3009 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3013 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3015 WGroupbox
*g
= (WGroupbox
*) w
;
3023 return MSG_NOT_HANDLED
;
3026 tty_setcolor (COLOR_NORMAL
);
3027 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3028 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3030 tty_setcolor (COLOR_HOT_NORMAL
);
3031 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3032 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
3033 tty_print_string (g
->title
);
3036 case WIDGET_DESTROY
:
3041 return default_proc (msg
, parm
);
3046 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3048 WGroupbox
*g
= g_new (WGroupbox
, 1);
3050 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3052 g
->widget
.options
&= ~W_WANT_CURSOR
;
3053 widget_want_hotkey (g
->widget
, 0);
3055 /* Strip existing spaces, add one space before and after the title */
3059 t
= g_strstrip (g_strdup (title
));
3060 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);