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 */
65 widget_selectcolor (Widget
* w
, gboolean focused
, gboolean hotkey
)
67 Dlg_head
*h
= w
->parent
;
72 : DLG_HOT_NORMALC (h
)) : (focused
? DLG_FOCUSC (h
) : DLG_NORMALC (h
)));
76 parse_hotkey (const char *text
)
78 struct hotkey_t result
;
81 /* search for '&', that is not on the of text */
82 cp
= strchr (text
, '&');
83 if (cp
!= NULL
&& cp
[1] != '\0')
85 result
.start
= g_strndup (text
, cp
- text
);
89 p
= str_cget_next_char (cp
);
90 result
.hotkey
= g_strndup (cp
, p
- cp
);
93 result
.end
= g_strdup (cp
);
97 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
)
132 widget_selectcolor (w
, focused
, TRUE
);
133 tty_print_string (hotkey
.hotkey
);
134 widget_selectcolor (w
, focused
, FALSE
);
137 if (hotkey
.end
!= NULL
)
138 tty_print_string (hotkey
.end
);
142 /* Default callback for widgets */
144 default_proc (widget_msg_t msg
, int parm
)
160 return MSG_NOT_HANDLED
;
164 static int button_event (Gpm_Event
* event
, void *);
169 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
171 WButton
*b
= (WButton
*) w
;
174 Dlg_head
*h
= b
->widget
.parent
;
180 * Don't let the default button steal Enter from the current
181 * button. This is a workaround for the flawed event model
182 * when hotkeys are sent to all widgets before the key is
183 * handled by the current widget.
185 if (parm
== '\n' && h
->current
== &b
->widget
)
187 button_callback (w
, WIDGET_KEY
, ' ');
191 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
193 button_callback (w
, WIDGET_KEY
, ' ');
197 if (b
->text
.hotkey
!= NULL
)
199 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
201 button_callback (w
, WIDGET_KEY
, ' ');
205 return MSG_NOT_HANDLED
;
208 if (parm
!= ' ' && parm
!= '\n')
209 return MSG_NOT_HANDLED
;
212 stop
= (*b
->callback
) (b
->action
);
213 if (!b
->callback
|| stop
)
215 h
->ret_value
= b
->action
;
237 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
243 if (msg
== WIDGET_UNFOCUS
)
245 else if (msg
== WIDGET_FOCUS
)
248 widget_selectcolor (w
, b
->selected
, FALSE
);
249 widget_move (w
, 0, 0);
254 tty_print_string ("[< ");
257 tty_print_string ("[ ");
260 tty_print_string ("[");
267 draw_hotkey (w
, b
->text
, b
->selected
);
272 tty_print_string (" >]");
275 tty_print_string (" ]");
278 tty_print_string ("]");
284 release_hotkey (b
->text
);
288 return default_proc (msg
, parm
);
293 button_event (Gpm_Event
* event
, void *data
)
297 if (event
->type
& (GPM_DOWN
| GPM_UP
))
299 Dlg_head
*h
= b
->widget
.parent
;
300 dlg_select_widget (b
);
301 if (event
->type
& GPM_UP
)
303 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
304 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
312 button_get_len (const WButton
* b
)
314 int ret
= hotkey_width (b
->text
);
334 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
336 WButton
*b
= g_new (WButton
, 1);
340 b
->text
= parse_hotkey (text
);
342 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
345 b
->callback
= callback
;
346 widget_want_hotkey (b
->widget
, 1);
347 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
353 button_get_text (const WButton
* b
)
355 if (b
->text
.hotkey
!= NULL
)
356 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
358 return g_strdup (b
->text
.start
);
362 button_set_text (WButton
* b
, const char *text
)
364 release_hotkey (b
->text
);
365 b
->text
= parse_hotkey (text
);
366 b
->widget
.cols
= button_get_len (b
);
367 dlg_redraw (b
->widget
.parent
);
371 /* Radio button widget */
372 static int radio_event (Gpm_Event
* event
, void *);
375 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
377 WRadio
*r
= (WRadio
*) w
;
379 Dlg_head
*h
= r
->widget
.parent
;
385 int lp
= g_ascii_tolower ((gchar
) parm
);
387 for (i
= 0; i
< r
->count
; i
++)
389 if (r
->texts
[i
].hotkey
!= NULL
)
391 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
398 radio_callback (w
, WIDGET_KEY
, ' ');
403 return MSG_NOT_HANDLED
;
410 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
411 radio_callback (w
, WIDGET_FOCUS
, ' ');
421 return MSG_NOT_HANDLED
;
425 if (r
->count
- 1 > r
->pos
)
431 return MSG_NOT_HANDLED
;
434 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
435 radio_callback (w
, WIDGET_FOCUS
, ' ');
436 widget_move (&r
->widget
, r
->pos
, 1);
442 for (i
= 0; i
< r
->count
; i
++)
444 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
445 widget_selectcolor (w
, focused
, FALSE
);
446 widget_move (&r
->widget
, i
, 0);
447 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
448 draw_hotkey (w
, r
->texts
[i
], focused
);
453 for (i
= 0; i
< r
->count
; i
++)
455 release_hotkey (r
->texts
[i
]);
461 return default_proc (msg
, parm
);
466 radio_event (Gpm_Event
* event
, void *data
)
471 if (event
->type
& (GPM_DOWN
| GPM_UP
))
473 Dlg_head
*h
= r
->widget
.parent
;
475 r
->pos
= event
->y
- 1;
476 dlg_select_widget (r
);
477 if (event
->type
& GPM_UP
)
479 radio_callback (w
, WIDGET_KEY
, ' ');
480 radio_callback (w
, WIDGET_FOCUS
, 0);
481 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
489 radio_new (int y
, int x
, int count
, const char **texts
)
491 WRadio
*result
= g_new (WRadio
, 1);
494 /* Compute the longest string */
495 result
->texts
= g_new (struct hotkey_t
, count
);
498 for (i
= 0; i
< count
; i
++)
500 result
->texts
[i
] = parse_hotkey (texts
[i
]);
501 m
= hotkey_width (result
->texts
[i
]);
506 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
510 result
->count
= count
;
511 widget_want_hotkey (result
->widget
, 1);
517 /* Checkbutton widget */
519 static int check_event (Gpm_Event
* event
, void *);
522 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
524 WCheck
*c
= (WCheck
*) w
;
525 Dlg_head
*h
= c
->widget
.parent
;
530 if (c
->text
.hotkey
!= NULL
)
532 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
535 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
539 return MSG_NOT_HANDLED
;
543 return MSG_NOT_HANDLED
;
545 c
->state
^= C_CHANGE
;
546 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
547 check_callback (w
, WIDGET_FOCUS
, ' ');
551 widget_move (&c
->widget
, 0, 1);
557 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
558 widget_move (&c
->widget
, 0, 0);
559 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
560 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
564 release_hotkey (c
->text
);
568 return default_proc (msg
, parm
);
573 check_event (Gpm_Event
* event
, void *data
)
578 if (event
->type
& (GPM_DOWN
| GPM_UP
))
580 Dlg_head
*h
= c
->widget
.parent
;
582 dlg_select_widget (c
);
583 if (event
->type
& GPM_UP
)
585 check_callback (w
, WIDGET_KEY
, ' ');
586 check_callback (w
, WIDGET_FOCUS
, 0);
587 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
595 check_new (int y
, int x
, int state
, const char *text
)
597 WCheck
*c
= g_new (WCheck
, 1);
599 c
->text
= parse_hotkey (text
);
601 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
602 c
->state
= state
? C_BOOL
: 0;
603 widget_want_hotkey (c
->widget
, 1);
609 save_text_to_clip_file (const char *text
)
616 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
617 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
618 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
624 str_len
= strlen (text
);
625 ret
= mc_write (file
, (char *) text
, str_len
);
627 return ret
== (ssize_t
) str_len
;
631 load_text_from_clip_file (char **text
)
636 gboolean first
= TRUE
;
638 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
639 f
= fopen (fname
, "r");
647 while (fgets (buf
, sizeof (buf
), f
))
654 if (buf
[len
- 1] == '\n')
660 *text
= g_strdup (buf
);
664 /* remove \n on EOL */
667 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
676 return (*text
!= NULL
);
680 panel_save_curent_file_to_clip_file (void)
684 if (current_panel
->marked
== 0)
685 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
689 gboolean first
= TRUE
;
692 for (i
= 0; i
< current_panel
->count
; i
++)
693 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
694 { /* Skip the unmarked ones */
697 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
702 /* Add empty lines after the file */
706 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
714 res
= save_text_to_clip_file (flist
);
724 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
726 WLabel
*l
= (WLabel
*) w
;
727 Dlg_head
*h
= l
->widget
.parent
;
734 /* We don't want to get the focus */
736 return MSG_NOT_HANDLED
;
740 char *p
= l
->text
, *q
, c
= 0;
747 tty_setcolor (DEFAULT_COLOR
);
749 tty_setcolor (DLG_NORMALC (h
));
753 q
= strchr (p
, '\n');
760 widget_move (&l
->widget
, y
, 0);
761 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
777 return default_proc (msg
, parm
);
782 label_set_text (WLabel
* label
, const char *text
)
784 int newcols
= label
->widget
.cols
;
787 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
788 return; /* Flickering is not nice */
790 g_free (label
->text
);
794 label
->text
= g_strdup (text
);
795 if (label
->auto_adjust_cols
)
797 str_msg_term_size (text
, &newlines
, &newcols
);
798 if (newcols
> label
->widget
.cols
)
799 label
->widget
.cols
= newcols
;
800 if (newlines
> label
->widget
.lines
)
801 label
->widget
.lines
= newlines
;
807 if (label
->widget
.parent
)
808 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
810 if (newcols
< label
->widget
.cols
)
811 label
->widget
.cols
= newcols
;
815 label_new (int y
, int x
, const char *text
)
822 str_msg_term_size (text
, &lines
, &cols
);
824 l
= g_new (WLabel
, 1);
825 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
826 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
827 l
->auto_adjust_cols
= 1;
829 widget_want_cursor (l
->widget
, 0);
834 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
836 WHLine
*l
= (WHLine
*) w
;
837 Dlg_head
*h
= l
->widget
.parent
;
843 if (l
->auto_adjust_cols
)
845 if (((w
->parent
->flags
& DLG_COMPACT
) != 0))
848 w
->cols
= w
->parent
->cols
;
852 w
->x
= w
->parent
->x
+ 1;
853 w
->cols
= w
->parent
->cols
- 2;
858 /* We don't want to get the focus */
859 return MSG_NOT_HANDLED
;
863 tty_setcolor (DEFAULT_COLOR
);
865 tty_setcolor (DLG_NORMALC (h
));
867 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
869 if (l
->auto_adjust_cols
)
871 widget_move (w
, 0, 0);
872 tty_print_alt_char (ACS_LTEE
, FALSE
);
873 widget_move (w
, 0, w
->cols
- 1);
874 tty_print_alt_char (ACS_RTEE
, FALSE
);
879 return default_proc (msg
, parm
);
885 hline_new (int y
, int x
, int width
)
891 l
= g_new (WHLine
, 1);
892 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
893 l
->auto_adjust_cols
= (width
< 0);
894 l
->transparent
= FALSE
;
895 widget_want_cursor (l
->widget
, 0);
899 /* Gauge widget (progress indicator) */
900 /* Currently width is hardcoded here for text mode */
904 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
906 WGauge
*g
= (WGauge
*) w
;
907 Dlg_head
*h
= g
->widget
.parent
;
909 if (msg
== WIDGET_INIT
)
912 /* We don't want to get the focus */
913 if (msg
== WIDGET_FOCUS
)
914 return MSG_NOT_HANDLED
;
916 if (msg
== WIDGET_DRAW
)
918 widget_move (&g
->widget
, 0, 0);
919 tty_setcolor (DLG_NORMALC (h
));
921 tty_printf ("%*s", gauge_len
, "");
924 int percentage
, columns
;
925 long total
= g
->max
, done
= g
->current
;
927 if (total
<= 0 || done
< 0)
934 while (total
> 65535)
939 percentage
= (200 * done
/ total
+ 1) / 2;
940 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
941 tty_print_char ('[');
942 if (g
->from_left_to_right
)
944 tty_setcolor (GAUGE_COLOR
);
945 tty_printf ("%*s", (int) columns
, "");
946 tty_setcolor (DLG_NORMALC (h
));
947 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
951 tty_setcolor (DLG_NORMALC (h
));
952 tty_printf ("%*s", gauge_len
- columns
- 7, "");
953 tty_setcolor (GAUGE_COLOR
);
954 tty_printf ("%*s", columns
, "");
955 tty_setcolor (DLG_NORMALC (h
));
956 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
962 return default_proc (msg
, parm
);
966 gauge_set_value (WGauge
* g
, int max
, int current
)
968 if (g
->current
== current
&& g
->max
== max
)
969 return; /* Do not flicker */
971 max
= 1; /* I do not like division by zero :) */
973 g
->current
= current
;
975 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
979 gauge_show (WGauge
* g
, int shown
)
981 if (g
->shown
== shown
)
984 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
988 gauge_new (int y
, int x
, int shown
, int max
, int current
)
990 WGauge
*g
= g_new (WGauge
, 1);
992 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
995 max
= 1; /* I do not like division by zero :) */
997 g
->current
= current
;
998 g
->from_left_to_right
= TRUE
;
999 widget_want_cursor (g
->widget
, 0);
1006 /* {{{ history button */
1008 #define LARGE_HISTORY_BUTTON 1
1010 #ifdef LARGE_HISTORY_BUTTON
1011 # define HISTORY_BUTTON_WIDTH 3
1013 # define HISTORY_BUTTON_WIDTH 1
1016 #define should_show_history_button(in) \
1017 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1020 draw_history_button (WInput
* in
)
1023 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1024 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1025 #ifdef LARGE_HISTORY_BUTTON
1028 h
= in
->widget
.parent
;
1029 tty_setcolor (NORMAL_COLOR
);
1030 tty_print_string ("[ ]");
1031 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1032 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1036 tty_setcolor (MARKED_COLOR
);
1041 /* }}} history button */
1044 /* Input widgets now have a global kill ring */
1045 /* Pointer to killed data */
1046 static char *kill_buffer
= NULL
;
1049 update_input (WInput
* in
, int clear_first
)
1051 int has_history
= 0;
1053 int buf_len
= str_length (in
->buffer
);
1057 if (should_show_history_button (in
))
1058 has_history
= HISTORY_BUTTON_WIDTH
;
1060 if (in
->disable_update
)
1063 pw
= str_term_width2 (in
->buffer
, in
->point
);
1065 /* Make the point visible */
1066 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1069 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1070 if (in
->term_first_shown
< 0)
1071 in
->term_first_shown
= 0;
1074 /* Adjust the mark */
1075 if (in
->mark
> buf_len
)
1079 draw_history_button (in
);
1081 tty_setcolor (in
->color
);
1083 widget_move (&in
->widget
, 0, 0);
1085 if (!in
->is_password
)
1087 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1088 in
->field_width
- has_history
));
1093 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++)
1097 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1100 str_cnext_char (&cp
);
1109 winput_set_origin (WInput
* in
, int x
, int field_width
)
1112 in
->field_width
= in
->widget
.cols
= field_width
;
1113 update_input (in
, 0);
1116 /* {{{ history saving and loading */
1118 int num_history_items_recorded
= 60;
1121 This loads and saves the history of an input line to and from the
1122 widget. It is called with the widgets history name on creation of the
1123 widget, and returns the GList list. It stores histories in the file
1124 ~/.mc/history in using the profile code.
1126 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1127 function) then input_new assigns the default text to be the last text
1128 entered, or "" if not found.
1132 history_get (const char *input_name
)
1139 size_t keys_num
= 0;
1142 if (num_history_items_recorded
== 0) /* this is how to disable */
1144 if ((input_name
== NULL
) || (*input_name
== '\0'))
1147 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1148 cfg
= mc_config_init (profile
);
1150 /* get number of keys */
1151 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1154 for (i
= 0; i
< keys_num
; i
++)
1157 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1158 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1160 if (this_entry
!= NULL
)
1161 hist
= list_append_unique (hist
, this_entry
);
1164 mc_config_deinit (cfg
);
1167 /* return pointer to the last entry in the list */
1168 return g_list_last (hist
);
1172 history_put (const char *input_name
, GList
* h
)
1178 if (num_history_items_recorded
== 0) /* this is how to disable */
1180 if ((input_name
== NULL
) || (*input_name
== '\0'))
1185 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1187 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1191 /* Make sure the history is only readable by the user */
1192 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1198 /* go to end of list */
1199 h
= g_list_last (h
);
1201 /* go back 60 places */
1202 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1203 h
= g_list_previous (h
);
1205 cfg
= mc_config_init (profile
);
1207 if (input_name
!= NULL
)
1208 mc_config_del_group (cfg
, input_name
);
1210 /* dump history into profile */
1211 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1213 char *text
= (char *) h
->data
;
1215 /* We shouldn't have null entries, but let's be sure */
1219 g_snprintf (key
, sizeof (key
), "%d", i
++);
1220 mc_config_set_string (cfg
, input_name
, key
, text
);
1224 mc_config_save_file (cfg
, NULL
);
1225 mc_config_deinit (cfg
);
1229 /* }}} history saving and loading */
1232 /* {{{ history display */
1237 return _(" History ");
1248 dlg_hist_reposition (Dlg_head
* dlg_head
)
1250 dlg_hist_data
*data
;
1251 int x
= 0, y
, he
, wi
;
1254 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1255 return MSG_NOT_HANDLED
;
1257 data
= (dlg_hist_data
*) dlg_head
->data
;
1259 y
= data
->widget
->y
;
1260 he
= data
->count
+ 2;
1262 if (he
<= y
|| y
> (LINES
- 6))
1264 he
= min (he
, y
- 1);
1270 he
= min (he
, LINES
- y
);
1273 if (data
->widget
->x
> 2)
1274 x
= data
->widget
->x
- 2;
1276 wi
= data
->maxlen
+ 4;
1278 if ((wi
+ x
) > COLS
)
1280 wi
= min (wi
, COLS
);
1284 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1290 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1295 return dlg_hist_reposition (h
);
1298 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1303 show_hist (GList
** history
, Widget
* widget
)
1305 GList
*z
, *hlist
= NULL
, *hi
;
1306 size_t maxlen
, i
, count
= 0;
1308 Dlg_head
*query_dlg
;
1309 WListbox
*query_list
;
1310 dlg_hist_data hist_data
;
1312 if (*history
== NULL
)
1315 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1317 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1321 i
= str_term_width1 ((char *) z
->data
);
1322 maxlen
= max (maxlen
, i
);
1325 entry
= g_new0 (WLEntry
, 1);
1326 /* history is being reverted here */
1327 entry
->text
= g_strdup ((char *) z
->data
);
1328 hlist
= g_list_prepend (hlist
, entry
);
1331 hist_data
.widget
= widget
;
1332 hist_data
.count
= count
;
1333 hist_data
.maxlen
= maxlen
;
1336 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1337 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1338 query_dlg
->data
= &hist_data
;
1340 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1342 /* this call makes list stick to all sides of dialog, effectively make
1343 it be resized with dialog */
1344 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1346 /* to avoid diplicating of (calculating sizes in two places)
1347 code, call dlg_hist_callback function here, to set dialog and
1349 The main idea - create 4x4 dialog and add 2x2 list in
1350 center of it, and let dialog function resize it to needed
1352 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1354 if (query_dlg
->y
< widget
->y
)
1356 /* draw list entries from bottom upto top */
1357 listbox_set_list (query_list
, hlist
);
1358 listbox_select_last (query_list
);
1362 /* draw list entries from top downto bottom */
1363 /* revert history direction */
1364 hlist
= g_list_reverse (hlist
);
1365 listbox_set_list (query_list
, hlist
);
1368 if (run_dlg (query_dlg
) != B_CANCEL
)
1372 listbox_get_current (query_list
, &q
, NULL
);
1377 /* get modified history from dialog */
1379 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1383 entry
= (WLEntry
*) hi
->data
;
1384 /* history is being reverted here again */
1385 z
= g_list_prepend (z
, entry
->text
);
1389 destroy_dlg (query_dlg
);
1391 /* restore history direction */
1392 if (query_dlg
->y
< widget
->y
)
1393 z
= g_list_reverse (z
);
1395 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1396 g_list_free (*history
);
1397 *history
= g_list_last (z
);
1403 do_show_hist (WInput
* in
)
1407 r
= show_hist (&in
->history
, &in
->widget
);
1410 assign_text (in
, r
);
1415 /* }}} history display */
1418 input_destroy (WInput
* in
)
1422 fprintf (stderr
, "Internal error: null Input *\n");
1423 exit (EXIT_FAILURE
);
1428 if (in
->history
!= NULL
)
1430 if (!in
->is_password
&& (((Widget
*) in
)->parent
->ret_value
!= B_CANCEL
))
1431 history_put (in
->history_name
, in
->history
);
1433 in
->history
= g_list_first (in
->history
);
1434 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1435 g_list_free (in
->history
);
1438 g_free (in
->buffer
);
1439 free_completions (in
);
1440 g_free (in
->history_name
);
1442 g_free (kill_buffer
);
1447 input_disable_update (WInput
* in
)
1449 in
->disable_update
++;
1453 input_enable_update (WInput
* in
)
1455 in
->disable_update
--;
1456 update_input (in
, 0);
1461 push_history (WInput
* in
, const char *text
)
1463 /* input widget where urls with passwords are entered without any
1465 const char *password_input_fields
[] = {
1466 N_(" Link to a remote machine "),
1467 N_(" FTP to machine "),
1468 N_(" SMB link to machine ")
1470 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1480 for (i
= 0; i
< ELEMENTS
; i
++)
1481 password_input_fields
[i
] = _(password_input_fields
[i
]);
1484 t
= g_strstrip (g_strdup (text
));
1487 t
= g_strdup (empty
? "" : text
);
1489 if (in
->history_name
!= NULL
)
1491 const char *p
= in
->history_name
+ 3;
1493 for (i
= 0; i
< ELEMENTS
; i
++)
1494 if (strcmp (p
, password_input_fields
[i
]) == 0)
1497 strip_password (t
, i
>= ELEMENTS
);
1500 in
->history
= list_append_unique (in
->history
, t
);
1504 /* Cleans the input line and adds the current text to the history */
1506 new_input (WInput
* in
)
1508 push_history (in
, in
->buffer
);
1510 in
->buffer
[0] = '\0';
1514 free_completions (in
);
1515 update_input (in
, 0);
1519 move_buffer_backward (WInput
* in
, int start
, int end
)
1522 int str_len
= str_length (in
->buffer
);
1523 if (start
>= str_len
|| end
> str_len
+ 1)
1526 pos
= str_offset_to_pos (in
->buffer
, start
);
1527 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1529 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1530 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1534 insert_char (WInput
* in
, int c_code
)
1540 return MSG_NOT_HANDLED
;
1542 if (in
->charpoint
>= MB_LEN_MAX
)
1545 in
->charbuf
[in
->charpoint
] = c_code
;
1548 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1552 in
->charpoint
= 0; /* broken multibyte char, skip */
1557 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1559 /* Expand the buffer */
1560 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1561 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1565 in
->current_max_size
= new_length
;
1569 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1571 /* bytes from begin */
1572 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1574 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1576 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1577 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1579 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1588 beginning_of_line (WInput
* in
)
1595 end_of_line (WInput
* in
)
1597 in
->point
= str_length (in
->buffer
);
1602 backward_char (WInput
* in
)
1604 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1608 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1614 forward_char (WInput
* in
)
1616 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1619 in
->point
+= str_cnext_noncomb_char (&act
);
1625 forward_word (WInput
* in
)
1627 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1629 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1631 str_cnext_char (&p
);
1634 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1636 str_cnext_char (&p
);
1642 backward_word (WInput
* in
)
1647 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1648 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1650 while (p
!= in
->buffer
)
1653 str_cprev_char (&p
);
1654 if (!str_isspace (p
) && !str_ispunct (p
))
1661 while (p
!= in
->buffer
)
1663 str_cprev_char (&p
);
1664 if (str_isspace (p
) || str_ispunct (p
))
1672 key_left (WInput
* in
)
1678 key_ctrl_left (WInput
* in
)
1684 key_right (WInput
* in
)
1690 key_ctrl_right (WInput
* in
)
1695 backward_delete (WInput
* in
)
1697 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1703 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1704 move_buffer_backward (in
, start
, in
->point
);
1711 delete_char (WInput
* in
)
1713 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1714 int end
= in
->point
;
1716 end
+= str_cnext_noncomb_char (&act
);
1718 move_buffer_backward (in
, in
->point
, end
);
1724 copy_region (WInput
* in
, int x_first
, int x_last
)
1726 int first
= min (x_first
, x_last
);
1727 int last
= max (x_first
, x_last
);
1731 /* Copy selected files to clipboard */
1732 panel_save_curent_file_to_clip_file ();
1736 g_free (kill_buffer
);
1738 first
= str_offset_to_pos (in
->buffer
, first
);
1739 last
= str_offset_to_pos (in
->buffer
, last
);
1741 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1742 save_text_to_clip_file (kill_buffer
);
1746 delete_region (WInput
* in
, int x_first
, int x_last
)
1748 int first
= min (x_first
, x_last
);
1749 int last
= max (x_first
, x_last
);
1753 if (in
->mark
> first
)
1755 last
= str_offset_to_pos (in
->buffer
, last
);
1756 first
= str_offset_to_pos (in
->buffer
, first
);
1757 len
= strlen (&in
->buffer
[last
]) + 1;
1758 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1764 kill_word (WInput
* in
)
1766 int old_point
= in
->point
;
1770 new_point
= in
->point
;
1771 in
->point
= old_point
;
1773 copy_region (in
, old_point
, new_point
);
1774 delete_region (in
, old_point
, new_point
);
1781 back_kill_word (WInput
* in
)
1783 int old_point
= in
->point
;
1787 new_point
= in
->point
;
1788 in
->point
= old_point
;
1790 copy_region (in
, old_point
, new_point
);
1791 delete_region (in
, old_point
, new_point
);
1796 set_mark (WInput
* in
)
1798 in
->mark
= in
->point
;
1802 kill_save (WInput
* in
)
1804 copy_region (in
, in
->mark
, in
->point
);
1808 kill_region (WInput
* in
)
1811 delete_region (in
, in
->point
, in
->mark
);
1815 clear_region (WInput
* in
)
1817 delete_region (in
, in
->point
, in
->mark
);
1823 if (kill_buffer
!= NULL
)
1828 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1829 insert_char (in
, *p
);
1835 kill_line (WInput
* in
)
1837 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1838 g_free (kill_buffer
);
1839 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1840 in
->buffer
[chp
] = '\0';
1845 ins_from_clip (WInput
* in
)
1849 if (load_text_from_clip_file (&p
))
1853 for (pp
= p
; *pp
!= '\0'; pp
++)
1854 insert_char (in
, *pp
);
1861 assign_text (WInput
* in
, const char *text
)
1863 free_completions (in
);
1864 g_free (in
->buffer
);
1865 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1866 in
->current_max_size
= strlen (in
->buffer
) + 1;
1867 in
->point
= str_length (in
->buffer
);
1874 hist_prev (WInput
* in
)
1882 push_history (in
, in
->buffer
);
1884 prev
= g_list_previous (in
->history
);
1888 assign_text (in
, (char *) prev
->data
);
1894 hist_next (WInput
* in
)
1898 push_history (in
, in
->buffer
);
1899 assign_text (in
, "");
1906 if (!in
->history
->next
)
1908 assign_text (in
, "");
1912 in
->history
= g_list_next (in
->history
);
1913 assign_text (in
, (char *) in
->history
->data
);
1918 port_region_marked_for_delete (WInput
* in
)
1920 in
->buffer
[0] = '\0';
1927 input_execute_cmd (WInput
* in
, unsigned long command
)
1929 cb_ret_t res
= MSG_HANDLED
;
1934 beginning_of_line (in
);
1939 case CK_InputMoveLeft
:
1942 case CK_InputWordLeft
:
1945 case CK_InputMoveRight
:
1948 case CK_InputWordRight
:
1949 key_ctrl_right (in
);
1951 case CK_InputBackwardChar
:
1954 case CK_InputBackwardWord
:
1957 case CK_InputForwardChar
:
1960 case CK_InputForwardWord
:
1963 case CK_InputBackwardDelete
:
1964 backward_delete (in
);
1966 case CK_InputDeleteChar
:
1969 case CK_InputKillWord
:
1972 case CK_InputBackwardKillWord
:
1973 back_kill_word (in
);
1975 case CK_InputSetMark
:
1978 case CK_InputKillRegion
:
1981 case CK_InputClearLine
:
1984 case CK_InputKillSave
:
1993 case CK_InputKillLine
:
1996 case CK_InputHistoryPrev
:
1999 case CK_InputHistoryNext
:
2002 case CK_InputHistoryShow
:
2005 case CK_InputComplete
:
2009 res
= MSG_NOT_HANDLED
;
2015 /* This function is a test for a special input key used in complete.c */
2016 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2017 and 2 if it is a complete key */
2019 is_in_input_map (WInput
* in
, int key
)
2022 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2023 if (key
== input_map
[i
].key
)
2025 input_execute_cmd (in
, input_map
[i
].command
);
2026 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2032 handle_char (WInput
* in
, int key
)
2037 v
= MSG_NOT_HANDLED
;
2041 free_completions (in
);
2042 v
= insert_char (in
, key
);
2043 update_input (in
, 1);
2047 for (i
= 0; input_map
[i
].key
; i
++)
2049 if (key
== input_map
[i
].key
)
2051 if (input_map
[i
].command
!= CK_InputComplete
)
2052 free_completions (in
);
2053 input_execute_cmd (in
, input_map
[i
].command
);
2054 update_input (in
, 1);
2059 if (input_map
[i
].command
== 0)
2062 return MSG_NOT_HANDLED
;
2064 port_region_marked_for_delete (in
);
2065 free_completions (in
);
2066 v
= insert_char (in
, key
);
2068 update_input (in
, 1);
2072 /* Inserts text in input line */
2074 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2076 input_disable_update (in
);
2077 while (*text
!= '\0')
2078 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2079 if (insert_extra_space
)
2080 handle_char (in
, ' ');
2081 input_enable_update (in
);
2082 update_input (in
, 1);
2086 input_set_point (WInput
* in
, int pos
)
2088 int max_pos
= str_length (in
->buffer
);
2092 if (pos
!= in
->point
)
2093 free_completions (in
);
2096 update_input (in
, 1);
2100 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2102 WInput
*in
= (WInput
*) w
;
2108 if (parm
== XCTRL ('q'))
2111 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2116 /* Keys we want others to handle */
2117 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2118 || parm
== KEY_F (10) || parm
== '\n')
2119 return MSG_NOT_HANDLED
;
2121 /* When pasting multiline text, insert literal Enter */
2122 if ((parm
& ~KEY_M_MASK
) == '\n')
2125 v
= handle_char (in
, '\n');
2130 return handle_char (in
, parm
);
2132 case WIDGET_COMMAND
:
2133 return input_execute_cmd (in
, parm
);
2136 case WIDGET_UNFOCUS
:
2138 update_input (in
, 0);
2142 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2143 - in
->term_first_shown
);
2146 case WIDGET_DESTROY
:
2151 return default_proc (msg
, parm
);
2156 input_event (Gpm_Event
* event
, void *data
)
2160 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2162 dlg_select_widget (in
);
2164 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2165 && should_show_history_button (in
))
2171 in
->point
= str_length (in
->buffer
);
2172 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2173 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2175 update_input (in
, 1);
2181 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
2182 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2184 WInput
*in
= g_new (WInput
, 1);
2185 size_t initial_buffer_len
;
2187 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2190 in
->history_name
= NULL
;
2192 if ((histname
!= NULL
) && (*histname
!= '\0'))
2194 in
->history_name
= g_strdup (histname
);
2195 in
->history
= history_get (histname
);
2198 if (def_text
== NULL
)
2200 else if (def_text
== INPUT_LAST_TEXT
)
2202 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2203 def_text
= (char *) in
->history
->data
;
2208 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2209 in
->widget
.options
|= W_IS_INPUT
;
2210 in
->completions
= NULL
;
2211 in
->completion_flags
= completion_flags
;
2212 in
->current_max_size
= initial_buffer_len
;
2213 in
->buffer
= g_new (char, initial_buffer_len
);
2215 in
->field_width
= width
;
2217 in
->term_first_shown
= 0;
2218 in
->disable_update
= 0;
2221 in
->is_password
= 0;
2223 strcpy (in
->buffer
, def_text
);
2224 in
->point
= str_length (in
->buffer
);
2231 /* Listbox widget */
2233 /* Should draw the scrollbar, but currently draws only
2234 * indications that there is more information
2238 listbox_entry_free (void *data
)
2246 listbox_drawscroll (WListbox
* l
)
2248 const int max_line
= l
->widget
.lines
- 1;
2252 /* Are we at the top? */
2253 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2255 tty_print_one_vline (TRUE
);
2257 tty_print_char ('^');
2259 /* Are we at the bottom? */
2260 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2261 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2262 tty_print_one_vline (TRUE
);
2264 tty_print_char ('v');
2266 /* Now draw the nice relative pointer */
2268 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2270 for (i
= 1; i
< max_line
; i
++)
2272 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2274 tty_print_one_vline (TRUE
);
2276 tty_print_char ('*');
2281 listbox_draw (WListbox
* l
, gboolean focused
)
2283 const Dlg_head
*h
= l
->widget
.parent
;
2284 const int normalc
= DLG_NORMALC (h
);
2285 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2292 le
= g_list_nth (l
->list
, l
->top
);
2293 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2294 pos
= (le
== NULL
) ? 0 : l
->top
;
2296 for (i
= 0; i
< l
->widget
.lines
; i
++)
2300 /* Display the entry */
2301 if (pos
== l
->pos
&& sel_line
== -1)
2304 tty_setcolor (selc
);
2307 tty_setcolor (normalc
);
2309 widget_move (&l
->widget
, i
, 1);
2311 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2315 WLEntry
*e
= (WLEntry
*) le
->data
;
2317 le
= g_list_next (le
);
2321 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2324 l
->cursor_y
= sel_line
;
2326 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2328 tty_setcolor (normalc
);
2329 listbox_drawscroll (l
);
2334 listbox_check_hotkey (WListbox
* l
, int key
)
2339 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2341 WLEntry
*e
= (WLEntry
*) le
->data
;
2343 if (e
->hotkey
== key
)
2350 /* Selects the last entry and scrolls the list to the bottom */
2352 listbox_select_last (WListbox
* l
)
2354 l
->pos
= l
->count
- 1;
2355 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2358 /* Selects the first entry and scrolls the list to the top */
2360 listbox_select_first (WListbox
* l
)
2362 l
->pos
= l
->top
= 0;
2366 listbox_set_list (WListbox
* l
, GList
* list
)
2368 listbox_remove_list (l
);
2373 l
->top
= l
->pos
= 0;
2374 l
->count
= g_list_length (list
);
2379 listbox_remove_list (WListbox
* l
)
2381 if ((l
!= NULL
) && (l
->count
!= 0))
2383 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2384 g_list_free (l
->list
);
2386 l
->count
= l
->pos
= l
->top
= 0;
2391 listbox_remove_current (WListbox
* l
)
2393 if ((l
!= NULL
) && (l
->count
!= 0))
2397 current
= g_list_nth (l
->list
, l
->pos
);
2398 l
->list
= g_list_remove_link (l
->list
, current
);
2399 listbox_entry_free ((WLEntry
*) current
->data
);
2400 g_list_free_1 (current
);
2404 l
->top
= l
->pos
= 0;
2405 else if (l
->pos
>= l
->count
)
2406 l
->pos
= l
->count
- 1;
2411 listbox_select_entry (WListbox
* l
, int dest
)
2415 gboolean top_seen
= FALSE
;
2421 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2431 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2432 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2437 /* If we are unable to find it, set decent values */
2438 l
->pos
= l
->top
= 0;
2441 /* Selects from base the pos element */
2443 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2445 int last
= l
->count
- 1;
2455 listbox_fwd (WListbox
* l
)
2457 if (l
->pos
+ 1 >= l
->count
)
2458 listbox_select_first (l
);
2460 listbox_select_entry (l
, l
->pos
+ 1);
2464 listbox_back (WListbox
* l
)
2467 listbox_select_last (l
);
2469 listbox_select_entry (l
, l
->pos
- 1);
2472 /* Return MSG_HANDLED if we want a redraw */
2474 listbox_key (WListbox
* l
, int key
)
2478 cb_ret_t j
= MSG_NOT_HANDLED
;
2480 if (l
->list
== NULL
)
2481 return MSG_NOT_HANDLED
;
2483 /* focus on listbox item N by '0'..'9' keys */
2484 if (key
>= '0' && key
<= '9')
2486 int oldpos
= l
->pos
;
2487 listbox_select_entry (l
, key
- '0');
2489 /* need scroll to item? */
2490 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2501 listbox_select_first (l
);
2507 listbox_select_last (l
);
2522 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2531 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2542 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2543 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2545 listbox_remove_current (l
);
2546 if ((l
->top
> 0) && (is_last
|| is_more
))
2551 case (KEY_M_SHIFT
| KEY_DC
):
2553 if (l
->deletable
&& confirm_history_cleanup
2554 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2555 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2556 _("Do you want clean this history?"),
2557 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2559 listbox_remove_list (l
);
2572 listbox_destroy (WListbox
* l
)
2574 /* don't delete list in modifable listbox */
2576 listbox_remove_list (l
);
2580 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2582 WListbox
*l
= (WListbox
*) w
;
2583 Dlg_head
*h
= l
->widget
.parent
;
2595 pos
= listbox_check_hotkey (l
, parm
);
2597 return MSG_NOT_HANDLED
;
2599 listbox_select_entry (l
, pos
);
2600 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2602 if (l
->cback
!= NULL
)
2603 action
= l
->cback (l
);
2605 action
= LISTBOX_DONE
;
2607 if (action
== LISTBOX_DONE
)
2609 h
->ret_value
= B_ENTER
;
2617 ret_code
= listbox_key (l
, parm
);
2618 if (ret_code
!= MSG_NOT_HANDLED
)
2620 listbox_draw (l
, TRUE
);
2621 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2626 widget_move (&l
->widget
, l
->cursor_y
, 0);
2627 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2631 case WIDGET_UNFOCUS
:
2633 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2636 case WIDGET_DESTROY
:
2637 listbox_destroy (l
);
2640 case WIDGET_RESIZED
:
2644 return default_proc (msg
, parm
);
2649 listbox_event (Gpm_Event
* event
, void *data
)
2654 Dlg_head
*h
= l
->widget
.parent
;
2657 if (event
->type
& GPM_DOWN
)
2658 dlg_select_widget (l
);
2660 if (l
->list
== NULL
)
2663 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2665 int ret
= MOU_REPEAT
;
2667 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2671 for (i
= -event
->y
; i
>= 0; i
--)
2673 else if (event
->y
> l
->widget
.lines
)
2674 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2676 else if (event
->buttons
& GPM_B_UP
)
2681 else if (event
->buttons
& GPM_B_DOWN
)
2687 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2689 /* We need to refresh ourselves since the dialog manager doesn't */
2690 /* know about this event */
2691 listbox_draw (l
, TRUE
);
2696 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2700 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2701 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2704 dlg_select_widget (l
);
2705 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2707 if (l
->cback
!= NULL
)
2708 action
= l
->cback (l
);
2710 action
= LISTBOX_DONE
;
2712 if (action
== LISTBOX_DONE
)
2714 h
->ret_value
= B_ENTER
;
2723 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2725 WListbox
*l
= g_new (WListbox
, 1);
2730 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2733 l
->top
= l
->pos
= 0;
2735 l
->deletable
= deletable
;
2736 l
->cback
= callback
;
2737 l
->allow_duplicates
= TRUE
;
2738 l
->scrollbar
= !tty_is_slow ();
2739 widget_want_hotkey (l
->widget
, 1);
2745 listbox_entry_cmp (const void *a
, const void *b
)
2747 const WLEntry
*ea
= (const WLEntry
*) a
;
2748 const WLEntry
*eb
= (const WLEntry
*) b
;
2750 return strcmp (ea
->text
, eb
->text
);
2753 /* Listbox item adding function */
2755 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2759 case LISTBOX_APPEND_AT_END
:
2760 l
->list
= g_list_append (l
->list
, e
);
2763 case LISTBOX_APPEND_BEFORE
:
2764 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2769 case LISTBOX_APPEND_AFTER
:
2770 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2773 case LISTBOX_APPEND_SORTED
:
2774 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2785 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2792 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2795 entry
= g_new (WLEntry
, 1);
2796 entry
->text
= g_strdup (text
);
2798 entry
->hotkey
= hotkey
;
2800 listbox_append_item (l
, entry
, pos
);
2806 listbox_search_text (WListbox
* l
, const char *text
)
2813 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2815 WLEntry
*e
= (WLEntry
*) le
->data
;
2817 if (strcmp (e
->text
, text
) == 0)
2825 /* Returns the current string text as well as the associated extra data */
2827 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2833 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2838 *string
= ok
? e
->text
: NULL
;
2841 *extra
= ok
? e
->data
: NULL
;
2845 /* ButtonBar widget */
2847 /* returns TRUE if a function has been called, FALSE otherwise. */
2849 buttonbar_call (WButtonBar
* bb
, int i
)
2851 cb_ret_t ret
= MSG_NOT_HANDLED
;
2853 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
2854 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2855 (Widget
*) bb
, DLG_ACTION
,
2856 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
2860 /* calculate width of one button, width is never lesser than 7 */
2862 buttonbat_get_button_width (void)
2864 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2865 return (result
>= 7) ? result
: 7;
2869 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2871 WButtonBar
*bb
= (WButtonBar
*) w
;
2878 return MSG_NOT_HANDLED
;
2881 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2882 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2884 return MSG_NOT_HANDLED
;
2890 int count_free_positions
;
2892 widget_move (&bb
->widget
, 0, 0);
2893 tty_setcolor (DEFAULT_COLOR
);
2894 bb
->btn_width
= buttonbat_get_button_width ();
2895 tty_printf ("%-*s", bb
->widget
.cols
, "");
2896 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2898 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++)
2900 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2901 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2902 tty_printf ("%2d", i
+ 1);
2903 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2904 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2905 tty_print_string (str_fit_to_term (text
,
2906 bb
->btn_width
- 2 + (int) (offset
<
2907 count_free_positions
),
2910 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2916 case WIDGET_DESTROY
:
2917 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2918 g_free (bb
->labels
[i
].text
);
2922 return default_proc (msg
, parm
);
2927 buttonbar_event (Gpm_Event
* event
, void *data
)
2929 WButtonBar
*bb
= data
;
2932 if (!(event
->type
& GPM_UP
))
2936 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2937 if (button
< BUTTONBAR_LABELS_NUM
)
2938 buttonbar_call (bb
, button
);
2943 buttonbar_new (gboolean visible
)
2947 bb
= g_new0 (WButtonBar
, 1);
2949 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
2950 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2951 bb
->visible
= visible
;
2952 widget_want_hotkey (bb
->widget
, 1);
2953 widget_want_cursor (bb
->widget
, 0);
2954 bb
->btn_width
= buttonbat_get_button_width ();
2960 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
2962 g_free (bb
->labels
[lc_index
- 1].text
);
2963 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2966 /* Find ButtonBar widget in the dialog */
2968 find_buttonbar (const Dlg_head
* h
)
2970 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2974 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
2975 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
2977 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
2979 unsigned long command
= CK_Ignore_Key
;
2982 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2984 if ((text
== NULL
) || (text
[0] == '\0'))
2985 set_label_text (bb
, idx
, "");
2987 set_label_text (bb
, idx
, text
);
2989 bb
->labels
[idx
- 1].command
= command
;
2990 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
2995 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
2997 bb
->visible
= visible
;
3001 buttonbar_redraw (WButtonBar
* bb
)
3004 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3008 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3010 WGroupbox
*g
= (WGroupbox
*) w
;
3018 return MSG_NOT_HANDLED
;
3021 tty_setcolor (COLOR_NORMAL
);
3022 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3023 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3025 tty_setcolor (COLOR_HOT_NORMAL
);
3026 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3027 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
3028 tty_print_string (g
->title
);
3031 case WIDGET_DESTROY
:
3036 return default_proc (msg
, parm
);
3041 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3043 WGroupbox
*g
= g_new (WGroupbox
, 1);
3045 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3047 g
->widget
.options
&= ~W_WANT_CURSOR
;
3048 widget_want_hotkey (g
->widget
, 0);
3050 /* Strip existing spaces, add one space before and after the title */
3054 t
= g_strstrip (g_strdup (title
));
3055 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);