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
)) : (focused
? DLG_FOCUSC (h
) : DLG_NORMALC (h
)));
78 parse_hotkey (const char *text
)
80 struct hotkey_t result
;
83 /* search for '&', that is not on the of text */
84 cp
= strchr (text
, '&');
85 if (cp
!= NULL
&& cp
[1] != '\0')
87 result
.start
= g_strndup (text
, cp
- text
);
91 p
= str_cget_next_char (cp
);
92 result
.hotkey
= g_strndup (cp
, p
- cp
);
95 result
.end
= g_strdup (cp
);
99 result
.start
= g_strdup (text
);
100 result
.hotkey
= NULL
;
108 release_hotkey (const struct hotkey_t hotkey
)
110 g_free (hotkey
.start
);
111 g_free (hotkey
.hotkey
);
116 hotkey_width (const struct hotkey_t hotkey
)
120 result
= str_term_width1 (hotkey
.start
);
121 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
122 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
127 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
129 widget_selectcolor (w
, focused
, FALSE
);
130 tty_print_string (hotkey
.start
);
132 if (hotkey
.hotkey
!= NULL
)
134 widget_selectcolor (w
, focused
, TRUE
);
135 tty_print_string (hotkey
.hotkey
);
136 widget_selectcolor (w
, focused
, FALSE
);
139 if (hotkey
.end
!= NULL
)
140 tty_print_string (hotkey
.end
);
144 /* Default callback for widgets */
146 default_proc (widget_msg_t msg
, int parm
)
162 return MSG_NOT_HANDLED
;
166 static int button_event (Gpm_Event
* event
, void *);
171 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
173 WButton
*b
= (WButton
*) w
;
176 Dlg_head
*h
= b
->widget
.parent
;
182 * Don't let the default button steal Enter from the current
183 * button. This is a workaround for the flawed event model
184 * when hotkeys are sent to all widgets before the key is
185 * handled by the current widget.
187 if (parm
== '\n' && h
->current
== &b
->widget
)
189 button_callback (w
, WIDGET_KEY
, ' ');
193 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
195 button_callback (w
, WIDGET_KEY
, ' ');
199 if (b
->text
.hotkey
!= NULL
)
201 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
203 button_callback (w
, WIDGET_KEY
, ' ');
207 return MSG_NOT_HANDLED
;
210 if (parm
!= ' ' && parm
!= '\n')
211 return MSG_NOT_HANDLED
;
214 stop
= (*b
->callback
) (b
->action
);
215 if (!b
->callback
|| stop
)
217 h
->ret_value
= b
->action
;
239 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
245 if (msg
== WIDGET_UNFOCUS
)
247 else if (msg
== WIDGET_FOCUS
)
250 widget_selectcolor (w
, b
->selected
, FALSE
);
251 widget_move (w
, 0, 0);
256 tty_print_string ("[< ");
259 tty_print_string ("[ ");
262 tty_print_string ("[");
269 draw_hotkey (w
, b
->text
, b
->selected
);
274 tty_print_string (" >]");
277 tty_print_string (" ]");
280 tty_print_string ("]");
286 release_hotkey (b
->text
);
290 return default_proc (msg
, parm
);
295 button_event (Gpm_Event
* event
, void *data
)
299 if (event
->type
& (GPM_DOWN
| GPM_UP
))
301 Dlg_head
*h
= b
->widget
.parent
;
302 dlg_select_widget (b
);
303 if (event
->type
& GPM_UP
)
305 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
306 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
314 button_get_len (const WButton
* b
)
316 int ret
= hotkey_width (b
->text
);
336 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
338 WButton
*b
= g_new (WButton
, 1);
342 b
->text
= parse_hotkey (text
);
344 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
347 b
->callback
= callback
;
348 widget_want_hotkey (b
->widget
, 1);
349 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
355 button_get_text (const WButton
* b
)
357 if (b
->text
.hotkey
!= NULL
)
358 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
360 return g_strdup (b
->text
.start
);
364 button_set_text (WButton
* b
, const char *text
)
366 release_hotkey (b
->text
);
367 b
->text
= parse_hotkey (text
);
368 b
->widget
.cols
= button_get_len (b
);
369 dlg_redraw (b
->widget
.parent
);
373 /* Radio button widget */
374 static int radio_event (Gpm_Event
* event
, void *);
377 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
379 WRadio
*r
= (WRadio
*) w
;
381 Dlg_head
*h
= r
->widget
.parent
;
387 int lp
= g_ascii_tolower ((gchar
) parm
);
389 for (i
= 0; i
< r
->count
; i
++)
391 if (r
->texts
[i
].hotkey
!= NULL
)
393 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
400 radio_callback (w
, WIDGET_KEY
, ' ');
405 return MSG_NOT_HANDLED
;
412 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
413 radio_callback (w
, WIDGET_FOCUS
, ' ');
423 return MSG_NOT_HANDLED
;
427 if (r
->count
- 1 > r
->pos
)
433 return MSG_NOT_HANDLED
;
436 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
437 radio_callback (w
, WIDGET_FOCUS
, ' ');
438 widget_move (&r
->widget
, r
->pos
, 1);
444 for (i
= 0; i
< r
->count
; i
++)
446 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
447 widget_selectcolor (w
, focused
, FALSE
);
448 widget_move (&r
->widget
, i
, 0);
449 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
450 draw_hotkey (w
, r
->texts
[i
], focused
);
455 for (i
= 0; i
< r
->count
; i
++)
457 release_hotkey (r
->texts
[i
]);
463 return default_proc (msg
, parm
);
468 radio_event (Gpm_Event
* event
, void *data
)
473 if (event
->type
& (GPM_DOWN
| GPM_UP
))
475 Dlg_head
*h
= r
->widget
.parent
;
477 r
->pos
= event
->y
- 1;
478 dlg_select_widget (r
);
479 if (event
->type
& GPM_UP
)
481 radio_callback (w
, WIDGET_KEY
, ' ');
482 radio_callback (w
, WIDGET_FOCUS
, 0);
483 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
491 radio_new (int y
, int x
, int count
, const char **texts
)
493 WRadio
*result
= g_new (WRadio
, 1);
496 /* Compute the longest string */
497 result
->texts
= g_new (struct hotkey_t
, count
);
500 for (i
= 0; i
< count
; i
++)
502 result
->texts
[i
] = parse_hotkey (texts
[i
]);
503 m
= hotkey_width (result
->texts
[i
]);
508 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
512 result
->count
= count
;
513 widget_want_hotkey (result
->widget
, 1);
519 /* Checkbutton widget */
521 static int check_event (Gpm_Event
* event
, void *);
524 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
526 WCheck
*c
= (WCheck
*) w
;
527 Dlg_head
*h
= c
->widget
.parent
;
532 if (c
->text
.hotkey
!= NULL
)
534 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
537 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
541 return MSG_NOT_HANDLED
;
545 return MSG_NOT_HANDLED
;
547 c
->state
^= C_CHANGE
;
548 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
549 check_callback (w
, WIDGET_FOCUS
, ' ');
553 widget_move (&c
->widget
, 0, 1);
559 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
560 widget_move (&c
->widget
, 0, 0);
561 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
562 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
566 release_hotkey (c
->text
);
570 return default_proc (msg
, parm
);
575 check_event (Gpm_Event
* event
, void *data
)
580 if (event
->type
& (GPM_DOWN
| GPM_UP
))
582 Dlg_head
*h
= c
->widget
.parent
;
584 dlg_select_widget (c
);
585 if (event
->type
& GPM_UP
)
587 check_callback (w
, WIDGET_KEY
, ' ');
588 check_callback (w
, WIDGET_FOCUS
, 0);
589 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
597 check_new (int y
, int x
, int state
, const char *text
)
599 WCheck
*c
= g_new (WCheck
, 1);
601 c
->text
= parse_hotkey (text
);
603 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
604 c
->state
= state
? C_BOOL
: 0;
605 widget_want_hotkey (c
->widget
, 1);
611 save_text_to_clip_file (const char *text
)
618 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
619 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
620 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
626 str_len
= strlen (text
);
627 ret
= mc_write (file
, (char *) text
, str_len
);
629 return ret
== (ssize_t
) str_len
;
633 load_text_from_clip_file (char **text
)
638 gboolean first
= TRUE
;
640 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
641 f
= fopen (fname
, "r");
649 while (fgets (buf
, sizeof (buf
), f
))
656 if (buf
[len
- 1] == '\n')
662 *text
= g_strdup (buf
);
666 /* remove \n on EOL */
669 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
678 return (*text
!= NULL
);
682 panel_save_curent_file_to_clip_file (void)
686 if (current_panel
->marked
== 0)
687 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
691 gboolean first
= TRUE
;
694 for (i
= 0; i
< current_panel
->count
; i
++)
695 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
696 { /* Skip the unmarked ones */
699 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
704 /* Add empty lines after the file */
708 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
716 res
= save_text_to_clip_file (flist
);
726 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
728 WLabel
*l
= (WLabel
*) w
;
729 Dlg_head
*h
= l
->widget
.parent
;
736 /* We don't want to get the focus */
738 return MSG_NOT_HANDLED
;
742 char *p
= l
->text
, *q
, c
= 0;
749 tty_setcolor (DEFAULT_COLOR
);
751 tty_setcolor (DLG_NORMALC (h
));
755 q
= strchr (p
, '\n');
762 widget_move (&l
->widget
, y
, 0);
763 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
779 return default_proc (msg
, parm
);
784 label_set_text (WLabel
* label
, const char *text
)
786 int newcols
= label
->widget
.cols
;
789 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
790 return; /* Flickering is not nice */
792 g_free (label
->text
);
796 label
->text
= g_strdup (text
);
797 if (label
->auto_adjust_cols
)
799 str_msg_term_size (text
, &newlines
, &newcols
);
800 if (newcols
> label
->widget
.cols
)
801 label
->widget
.cols
= newcols
;
802 if (newlines
> label
->widget
.lines
)
803 label
->widget
.lines
= newlines
;
809 if (label
->widget
.parent
)
810 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
812 if (newcols
< label
->widget
.cols
)
813 label
->widget
.cols
= newcols
;
817 label_new (int y
, int x
, const char *text
)
824 str_msg_term_size (text
, &lines
, &cols
);
826 l
= g_new (WLabel
, 1);
827 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
828 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
829 l
->auto_adjust_cols
= 1;
831 widget_want_cursor (l
->widget
, 0);
836 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
838 WHLine
*l
= (WHLine
*) w
;
839 Dlg_head
*h
= l
->widget
.parent
;
845 if (l
->auto_adjust_cols
)
847 if (((w
->parent
->flags
& DLG_COMPACT
) != 0))
850 w
->cols
= w
->parent
->cols
;
854 w
->x
= w
->parent
->x
+ 1;
855 w
->cols
= w
->parent
->cols
- 2;
860 /* We don't want to get the focus */
861 return MSG_NOT_HANDLED
;
865 tty_setcolor (DEFAULT_COLOR
);
867 tty_setcolor (DLG_NORMALC (h
));
869 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
871 if (l
->auto_adjust_cols
)
873 widget_move (w
, 0, 0);
874 tty_print_alt_char (ACS_LTEE
, FALSE
);
875 widget_move (w
, 0, w
->cols
- 1);
876 tty_print_alt_char (ACS_RTEE
, FALSE
);
881 return default_proc (msg
, parm
);
887 hline_new (int y
, int x
, int width
)
893 l
= g_new (WHLine
, 1);
894 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
895 l
->auto_adjust_cols
= (width
< 0);
896 l
->transparent
= FALSE
;
897 widget_want_cursor (l
->widget
, 0);
901 /* Gauge widget (progress indicator) */
902 /* Currently width is hardcoded here for text mode */
906 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
908 WGauge
*g
= (WGauge
*) w
;
909 Dlg_head
*h
= g
->widget
.parent
;
911 if (msg
== WIDGET_INIT
)
914 /* We don't want to get the focus */
915 if (msg
== WIDGET_FOCUS
)
916 return MSG_NOT_HANDLED
;
918 if (msg
== WIDGET_DRAW
)
920 widget_move (&g
->widget
, 0, 0);
921 tty_setcolor (DLG_NORMALC (h
));
923 tty_printf ("%*s", gauge_len
, "");
926 int percentage
, columns
;
927 long total
= g
->max
, done
= g
->current
;
929 if (total
<= 0 || done
< 0)
936 while (total
> 65535)
941 percentage
= (200 * done
/ total
+ 1) / 2;
942 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
943 tty_print_char ('[');
944 if (g
->from_left_to_right
)
946 tty_setcolor (GAUGE_COLOR
);
947 tty_printf ("%*s", (int) columns
, "");
948 tty_setcolor (DLG_NORMALC (h
));
949 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
953 tty_setcolor (DLG_NORMALC (h
));
954 tty_printf ("%*s", gauge_len
- columns
- 7, "");
955 tty_setcolor (GAUGE_COLOR
);
956 tty_printf ("%*s", columns
, "");
957 tty_setcolor (DLG_NORMALC (h
));
958 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
964 return default_proc (msg
, parm
);
968 gauge_set_value (WGauge
* g
, int max
, int current
)
970 if (g
->current
== current
&& g
->max
== max
)
971 return; /* Do not flicker */
973 max
= 1; /* I do not like division by zero :) */
975 g
->current
= current
;
977 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
981 gauge_show (WGauge
* g
, int shown
)
983 if (g
->shown
== shown
)
986 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
990 gauge_new (int y
, int x
, int shown
, int max
, int current
)
992 WGauge
*g
= g_new (WGauge
, 1);
994 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
997 max
= 1; /* I do not like division by zero :) */
999 g
->current
= current
;
1000 g
->from_left_to_right
= TRUE
;
1001 widget_want_cursor (g
->widget
, 0);
1008 /* {{{ history button */
1010 #define LARGE_HISTORY_BUTTON 1
1012 #ifdef LARGE_HISTORY_BUTTON
1013 # define HISTORY_BUTTON_WIDTH 3
1015 # define HISTORY_BUTTON_WIDTH 1
1018 #define should_show_history_button(in) \
1019 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1022 draw_history_button (WInput
* in
)
1025 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1026 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1027 #ifdef LARGE_HISTORY_BUTTON
1030 h
= in
->widget
.parent
;
1031 tty_setcolor (NORMAL_COLOR
);
1032 tty_print_string ("[ ]");
1033 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1034 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1038 tty_setcolor (MARKED_COLOR
);
1043 /* }}} history button */
1046 /* Input widgets now have a global kill ring */
1047 /* Pointer to killed data */
1048 static char *kill_buffer
= NULL
;
1051 update_input (WInput
* in
, int clear_first
)
1053 int has_history
= 0;
1055 int buf_len
= str_length (in
->buffer
);
1059 if (should_show_history_button (in
))
1060 has_history
= HISTORY_BUTTON_WIDTH
;
1062 if (in
->disable_update
)
1065 pw
= str_term_width2 (in
->buffer
, in
->point
);
1067 /* Make the point visible */
1068 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1071 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1072 if (in
->term_first_shown
< 0)
1073 in
->term_first_shown
= 0;
1076 /* Adjust the mark */
1077 if (in
->mark
> buf_len
)
1081 draw_history_button (in
);
1083 tty_setcolor (in
->color
);
1085 widget_move (&in
->widget
, 0, 0);
1087 if (!in
->is_password
)
1089 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1090 in
->field_width
- has_history
));
1095 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++)
1099 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1102 str_cnext_char (&cp
);
1111 winput_set_origin (WInput
* in
, int x
, int field_width
)
1114 in
->field_width
= in
->widget
.cols
= field_width
;
1115 update_input (in
, 0);
1118 /* {{{ history saving and loading */
1120 int num_history_items_recorded
= 60;
1123 This loads and saves the history of an input line to and from the
1124 widget. It is called with the widgets history name on creation of the
1125 widget, and returns the GList list. It stores histories in the file
1126 ~/.mc/history in using the profile code.
1128 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1129 function) then input_new assigns the default text to be the last text
1130 entered, or "" if not found.
1134 history_get (const char *input_name
)
1141 size_t keys_num
= 0;
1144 if (num_history_items_recorded
== 0) /* this is how to disable */
1146 if ((input_name
== NULL
) || (*input_name
== '\0'))
1149 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1150 cfg
= mc_config_init (profile
);
1152 /* get number of keys */
1153 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1156 for (i
= 0; i
< keys_num
; i
++)
1159 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1160 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1162 if (this_entry
!= NULL
)
1163 hist
= list_append_unique (hist
, this_entry
);
1166 mc_config_deinit (cfg
);
1169 /* return pointer to the last entry in the list */
1170 return g_list_last (hist
);
1174 history_put (const char *input_name
, GList
* h
)
1180 if (num_history_items_recorded
== 0) /* this is how to disable */
1182 if ((input_name
== NULL
) || (*input_name
== '\0'))
1187 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1189 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1193 /* Make sure the history is only readable by the user */
1194 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1200 /* go to end of list */
1201 h
= g_list_last (h
);
1203 /* go back 60 places */
1204 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1205 h
= g_list_previous (h
);
1207 cfg
= mc_config_init (profile
);
1209 if (input_name
!= NULL
)
1210 mc_config_del_group (cfg
, input_name
);
1212 /* dump history into profile */
1213 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1215 char *text
= (char *) h
->data
;
1217 /* We shouldn't have null entries, but let's be sure */
1221 g_snprintf (key
, sizeof (key
), "%d", i
++);
1222 mc_config_set_string (cfg
, input_name
, key
, text
);
1226 mc_config_save_file (cfg
, NULL
);
1227 mc_config_deinit (cfg
);
1231 /* }}} history saving and loading */
1234 /* {{{ history display */
1239 return _(" History ");
1250 dlg_hist_reposition (Dlg_head
* dlg_head
)
1252 dlg_hist_data
*data
;
1253 int x
= 0, y
, he
, wi
;
1256 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1257 return MSG_NOT_HANDLED
;
1259 data
= (dlg_hist_data
*) dlg_head
->data
;
1261 y
= data
->widget
->y
;
1262 he
= data
->count
+ 2;
1264 if (he
<= y
|| y
> (LINES
- 6))
1266 he
= min (he
, y
- 1);
1272 he
= min (he
, LINES
- y
);
1275 if (data
->widget
->x
> 2)
1276 x
= data
->widget
->x
- 2;
1278 wi
= data
->maxlen
+ 4;
1280 if ((wi
+ x
) > COLS
)
1282 wi
= min (wi
, COLS
);
1286 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1292 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1297 return dlg_hist_reposition (h
);
1300 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1305 show_hist (GList
** history
, Widget
* widget
)
1307 GList
*z
, *hlist
= NULL
, *hi
;
1308 size_t maxlen
, i
, count
= 0;
1310 Dlg_head
*query_dlg
;
1311 WListbox
*query_list
;
1312 dlg_hist_data hist_data
;
1314 if (*history
== NULL
)
1317 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1319 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1323 i
= str_term_width1 ((char *) z
->data
);
1324 maxlen
= max (maxlen
, i
);
1327 entry
= g_new0 (WLEntry
, 1);
1328 /* history is being reverted here */
1329 entry
->text
= g_strdup ((char *) z
->data
);
1330 hlist
= g_list_prepend (hlist
, entry
);
1333 hist_data
.widget
= widget
;
1334 hist_data
.count
= count
;
1335 hist_data
.maxlen
= maxlen
;
1338 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1339 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1340 query_dlg
->data
= &hist_data
;
1342 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1344 /* this call makes list stick to all sides of dialog, effectively make
1345 it be resized with dialog */
1346 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1348 /* to avoid diplicating of (calculating sizes in two places)
1349 code, call dlg_hist_callback function here, to set dialog and
1351 The main idea - create 4x4 dialog and add 2x2 list in
1352 center of it, and let dialog function resize it to needed
1354 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1356 if (query_dlg
->y
< widget
->y
)
1358 /* draw list entries from bottom upto top */
1359 listbox_set_list (query_list
, hlist
);
1360 listbox_select_last (query_list
);
1364 /* draw list entries from top downto bottom */
1365 /* revert history direction */
1366 hlist
= g_list_reverse (hlist
);
1367 listbox_set_list (query_list
, hlist
);
1370 if (run_dlg (query_dlg
) != B_CANCEL
)
1374 listbox_get_current (query_list
, &q
, NULL
);
1379 /* get modified history from dialog */
1381 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1385 entry
= (WLEntry
*) hi
->data
;
1386 /* history is being reverted here again */
1387 z
= g_list_prepend (z
, entry
->text
);
1391 destroy_dlg (query_dlg
);
1393 /* restore history direction */
1394 if (query_dlg
->y
< widget
->y
)
1395 z
= g_list_reverse (z
);
1397 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1398 g_list_free (*history
);
1399 *history
= g_list_last (z
);
1405 do_show_hist (WInput
* in
)
1409 r
= show_hist (&in
->history
, &in
->widget
);
1412 assign_text (in
, r
);
1417 /* }}} history display */
1420 input_destroy (WInput
* in
)
1424 fprintf (stderr
, "Internal error: null Input *\n");
1425 exit (EXIT_FAILURE
);
1430 if (in
->history
!= NULL
)
1432 if (!in
->is_password
&& (((Widget
*) in
)->parent
->ret_value
!= B_CANCEL
))
1433 history_put (in
->history_name
, in
->history
);
1435 in
->history
= g_list_first (in
->history
);
1436 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1437 g_list_free (in
->history
);
1440 g_free (in
->buffer
);
1441 free_completions (in
);
1442 g_free (in
->history_name
);
1444 g_free (kill_buffer
);
1449 input_disable_update (WInput
* in
)
1451 in
->disable_update
++;
1455 input_enable_update (WInput
* in
)
1457 in
->disable_update
--;
1458 update_input (in
, 0);
1463 push_history (WInput
* in
, const char *text
)
1465 /* input widget where urls with passwords are entered without any
1467 const char *password_input_fields
[] = {
1468 N_(" Link to a remote machine "),
1469 N_(" FTP to machine "),
1470 N_(" SMB link to machine ")
1472 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1482 for (i
= 0; i
< ELEMENTS
; i
++)
1483 password_input_fields
[i
] = _(password_input_fields
[i
]);
1486 t
= g_strstrip (g_strdup (text
));
1489 t
= g_strdup (empty
? "" : text
);
1491 if (in
->history_name
!= NULL
)
1493 const char *p
= in
->history_name
+ 3;
1495 for (i
= 0; i
< ELEMENTS
; i
++)
1496 if (strcmp (p
, password_input_fields
[i
]) == 0)
1499 strip_password (t
, i
>= ELEMENTS
);
1502 in
->history
= list_append_unique (in
->history
, t
);
1506 /* Cleans the input line and adds the current text to the history */
1508 new_input (WInput
* in
)
1510 push_history (in
, in
->buffer
);
1512 in
->buffer
[0] = '\0';
1516 free_completions (in
);
1517 update_input (in
, 0);
1521 move_buffer_backward (WInput
* in
, int start
, int end
)
1524 int str_len
= str_length (in
->buffer
);
1525 if (start
>= str_len
|| end
> str_len
+ 1)
1528 pos
= str_offset_to_pos (in
->buffer
, start
);
1529 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1531 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1532 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1536 insert_char (WInput
* in
, int c_code
)
1542 return MSG_NOT_HANDLED
;
1544 if (in
->charpoint
>= MB_LEN_MAX
)
1547 in
->charbuf
[in
->charpoint
] = c_code
;
1550 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1554 in
->charpoint
= 0; /* broken multibyte char, skip */
1559 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1561 /* Expand the buffer */
1562 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1563 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1567 in
->current_max_size
= new_length
;
1571 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1573 /* bytes from begin */
1574 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1576 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1578 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1579 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1581 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1590 beginning_of_line (WInput
* in
)
1597 end_of_line (WInput
* in
)
1599 in
->point
= str_length (in
->buffer
);
1604 backward_char (WInput
* in
)
1606 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1610 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1616 forward_char (WInput
* in
)
1618 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1621 in
->point
+= str_cnext_noncomb_char (&act
);
1627 forward_word (WInput
* in
)
1629 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1631 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1633 str_cnext_char (&p
);
1636 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1638 str_cnext_char (&p
);
1644 backward_word (WInput
* in
)
1649 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1650 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1652 while (p
!= in
->buffer
)
1655 str_cprev_char (&p
);
1656 if (!str_isspace (p
) && !str_ispunct (p
))
1663 while (p
!= in
->buffer
)
1665 str_cprev_char (&p
);
1666 if (str_isspace (p
) || str_ispunct (p
))
1674 key_left (WInput
* in
)
1680 key_ctrl_left (WInput
* in
)
1686 key_right (WInput
* in
)
1692 key_ctrl_right (WInput
* in
)
1697 backward_delete (WInput
* in
)
1699 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1705 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1706 move_buffer_backward (in
, start
, in
->point
);
1713 delete_char (WInput
* in
)
1715 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1716 int end
= in
->point
;
1718 end
+= str_cnext_noncomb_char (&act
);
1720 move_buffer_backward (in
, in
->point
, end
);
1726 copy_region (WInput
* in
, int x_first
, int x_last
)
1728 int first
= min (x_first
, x_last
);
1729 int last
= max (x_first
, x_last
);
1733 /* Copy selected files to clipboard */
1734 panel_save_curent_file_to_clip_file ();
1738 g_free (kill_buffer
);
1740 first
= str_offset_to_pos (in
->buffer
, first
);
1741 last
= str_offset_to_pos (in
->buffer
, last
);
1743 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1744 save_text_to_clip_file (kill_buffer
);
1748 delete_region (WInput
* in
, int x_first
, int x_last
)
1750 int first
= min (x_first
, x_last
);
1751 int last
= max (x_first
, x_last
);
1755 if (in
->mark
> first
)
1757 last
= str_offset_to_pos (in
->buffer
, last
);
1758 first
= str_offset_to_pos (in
->buffer
, first
);
1759 len
= strlen (&in
->buffer
[last
]) + 1;
1760 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1766 kill_word (WInput
* in
)
1768 int old_point
= in
->point
;
1772 new_point
= in
->point
;
1773 in
->point
= old_point
;
1775 copy_region (in
, old_point
, new_point
);
1776 delete_region (in
, old_point
, new_point
);
1783 back_kill_word (WInput
* in
)
1785 int old_point
= in
->point
;
1789 new_point
= in
->point
;
1790 in
->point
= old_point
;
1792 copy_region (in
, old_point
, new_point
);
1793 delete_region (in
, old_point
, new_point
);
1798 set_mark (WInput
* in
)
1800 in
->mark
= in
->point
;
1804 kill_save (WInput
* in
)
1806 copy_region (in
, in
->mark
, in
->point
);
1810 kill_region (WInput
* in
)
1813 delete_region (in
, in
->point
, in
->mark
);
1817 clear_region (WInput
* in
)
1819 delete_region (in
, in
->point
, in
->mark
);
1825 if (kill_buffer
!= NULL
)
1830 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1831 insert_char (in
, *p
);
1837 kill_line (WInput
* in
)
1839 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1840 g_free (kill_buffer
);
1841 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1842 in
->buffer
[chp
] = '\0';
1847 ins_from_clip (WInput
* in
)
1851 if (load_text_from_clip_file (&p
))
1855 for (pp
= p
; *pp
!= '\0'; pp
++)
1856 insert_char (in
, *pp
);
1863 assign_text (WInput
* in
, const char *text
)
1865 free_completions (in
);
1866 g_free (in
->buffer
);
1867 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1868 in
->current_max_size
= strlen (in
->buffer
) + 1;
1869 in
->point
= str_length (in
->buffer
);
1876 hist_prev (WInput
* in
)
1884 push_history (in
, in
->buffer
);
1886 prev
= g_list_previous (in
->history
);
1890 assign_text (in
, (char *) prev
->data
);
1896 hist_next (WInput
* in
)
1900 push_history (in
, in
->buffer
);
1901 assign_text (in
, "");
1908 if (!in
->history
->next
)
1910 assign_text (in
, "");
1914 in
->history
= g_list_next (in
->history
);
1915 assign_text (in
, (char *) in
->history
->data
);
1920 port_region_marked_for_delete (WInput
* in
)
1922 in
->buffer
[0] = '\0';
1929 input_execute_cmd (WInput
* in
, unsigned long command
)
1931 cb_ret_t res
= MSG_HANDLED
;
1936 beginning_of_line (in
);
1941 case CK_InputMoveLeft
:
1944 case CK_InputWordLeft
:
1947 case CK_InputMoveRight
:
1950 case CK_InputWordRight
:
1951 key_ctrl_right (in
);
1953 case CK_InputBackwardChar
:
1956 case CK_InputBackwardWord
:
1959 case CK_InputForwardChar
:
1962 case CK_InputForwardWord
:
1965 case CK_InputBackwardDelete
:
1966 backward_delete (in
);
1968 case CK_InputDeleteChar
:
1971 case CK_InputKillWord
:
1974 case CK_InputBackwardKillWord
:
1975 back_kill_word (in
);
1977 case CK_InputSetMark
:
1980 case CK_InputKillRegion
:
1983 case CK_InputClearLine
:
1986 case CK_InputKillSave
:
1995 case CK_InputKillLine
:
1998 case CK_InputHistoryPrev
:
2001 case CK_InputHistoryNext
:
2004 case CK_InputHistoryShow
:
2007 case CK_InputComplete
:
2011 res
= MSG_NOT_HANDLED
;
2017 /* This function is a test for a special input key used in complete.c */
2018 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2019 and 2 if it is a complete key */
2021 is_in_input_map (WInput
* in
, int key
)
2024 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2025 if (key
== input_map
[i
].key
)
2027 input_execute_cmd (in
, input_map
[i
].command
);
2028 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2034 handle_char (WInput
* in
, int key
)
2039 v
= MSG_NOT_HANDLED
;
2043 free_completions (in
);
2044 v
= insert_char (in
, key
);
2045 update_input (in
, 1);
2049 for (i
= 0; input_map
[i
].key
; i
++)
2051 if (key
== input_map
[i
].key
)
2053 if (input_map
[i
].command
!= CK_InputComplete
)
2054 free_completions (in
);
2055 input_execute_cmd (in
, input_map
[i
].command
);
2056 update_input (in
, 1);
2061 if (input_map
[i
].command
== 0)
2064 return MSG_NOT_HANDLED
;
2066 port_region_marked_for_delete (in
);
2067 free_completions (in
);
2068 v
= insert_char (in
, key
);
2070 update_input (in
, 1);
2074 /* Inserts text in input line */
2076 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2078 input_disable_update (in
);
2079 while (*text
!= '\0')
2080 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2081 if (insert_extra_space
)
2082 handle_char (in
, ' ');
2083 input_enable_update (in
);
2084 update_input (in
, 1);
2088 input_set_point (WInput
* in
, int pos
)
2090 int max_pos
= str_length (in
->buffer
);
2094 if (pos
!= in
->point
)
2095 free_completions (in
);
2098 update_input (in
, 1);
2102 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2104 WInput
*in
= (WInput
*) w
;
2110 if (parm
== XCTRL ('q'))
2113 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2118 /* Keys we want others to handle */
2119 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2120 || parm
== KEY_F (10) || parm
== '\n')
2121 return MSG_NOT_HANDLED
;
2123 /* When pasting multiline text, insert literal Enter */
2124 if ((parm
& ~KEY_M_MASK
) == '\n')
2127 v
= handle_char (in
, '\n');
2132 return handle_char (in
, parm
);
2134 case WIDGET_COMMAND
:
2135 return input_execute_cmd (in
, parm
);
2138 case WIDGET_UNFOCUS
:
2140 update_input (in
, 0);
2144 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2145 - in
->term_first_shown
);
2148 case WIDGET_DESTROY
:
2153 return default_proc (msg
, parm
);
2158 input_event (Gpm_Event
* event
, void *data
)
2162 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2164 dlg_select_widget (in
);
2166 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2167 && should_show_history_button (in
))
2173 in
->point
= str_length (in
->buffer
);
2174 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2175 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2177 update_input (in
, 1);
2183 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
2184 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2186 WInput
*in
= g_new (WInput
, 1);
2187 size_t initial_buffer_len
;
2189 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2192 in
->history_name
= NULL
;
2194 if ((histname
!= NULL
) && (*histname
!= '\0'))
2196 in
->history_name
= g_strdup (histname
);
2197 in
->history
= history_get (histname
);
2200 if (def_text
== NULL
)
2202 else if (def_text
== INPUT_LAST_TEXT
)
2204 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2205 def_text
= (char *) in
->history
->data
;
2210 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2211 in
->widget
.options
|= W_IS_INPUT
;
2212 in
->completions
= NULL
;
2213 in
->completion_flags
= completion_flags
;
2214 in
->current_max_size
= initial_buffer_len
;
2215 in
->buffer
= g_new (char, initial_buffer_len
);
2217 in
->field_width
= width
;
2219 in
->term_first_shown
= 0;
2220 in
->disable_update
= 0;
2223 in
->is_password
= 0;
2225 strcpy (in
->buffer
, def_text
);
2226 in
->point
= str_length (in
->buffer
);
2233 /* Listbox widget */
2235 /* Should draw the scrollbar, but currently draws only
2236 * indications that there is more information
2240 listbox_entry_free (void *data
)
2248 listbox_drawscroll (WListbox
* l
)
2250 const int max_line
= l
->widget
.lines
- 1;
2254 /* Are we at the top? */
2255 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2257 tty_print_one_vline (TRUE
);
2259 tty_print_char ('^');
2261 /* Are we at the bottom? */
2262 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2263 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2264 tty_print_one_vline (TRUE
);
2266 tty_print_char ('v');
2268 /* Now draw the nice relative pointer */
2270 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2272 for (i
= 1; i
< max_line
; i
++)
2274 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2276 tty_print_one_vline (TRUE
);
2278 tty_print_char ('*');
2283 listbox_draw (WListbox
* l
, gboolean focused
)
2285 const Dlg_head
*h
= l
->widget
.parent
;
2286 const int normalc
= DLG_NORMALC (h
);
2287 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2294 le
= g_list_nth (l
->list
, l
->top
);
2295 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2296 pos
= (le
== NULL
) ? 0 : l
->top
;
2298 for (i
= 0; i
< l
->widget
.lines
; i
++)
2302 /* Display the entry */
2303 if (pos
== l
->pos
&& sel_line
== -1)
2306 tty_setcolor (selc
);
2309 tty_setcolor (normalc
);
2311 widget_move (&l
->widget
, i
, 1);
2313 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2317 WLEntry
*e
= (WLEntry
*) le
->data
;
2319 le
= g_list_next (le
);
2323 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2326 l
->cursor_y
= sel_line
;
2328 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2330 tty_setcolor (normalc
);
2331 listbox_drawscroll (l
);
2336 listbox_check_hotkey (WListbox
* l
, int key
)
2341 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2343 WLEntry
*e
= (WLEntry
*) le
->data
;
2345 if (e
->hotkey
== key
)
2352 /* Selects the last entry and scrolls the list to the bottom */
2354 listbox_select_last (WListbox
* l
)
2356 l
->pos
= l
->count
- 1;
2357 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2360 /* Selects the first entry and scrolls the list to the top */
2362 listbox_select_first (WListbox
* l
)
2364 l
->pos
= l
->top
= 0;
2368 listbox_set_list (WListbox
* l
, GList
* list
)
2370 listbox_remove_list (l
);
2375 l
->top
= l
->pos
= 0;
2376 l
->count
= g_list_length (list
);
2381 listbox_remove_list (WListbox
* l
)
2383 if ((l
!= NULL
) && (l
->count
!= 0))
2385 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2386 g_list_free (l
->list
);
2388 l
->count
= l
->pos
= l
->top
= 0;
2393 listbox_remove_current (WListbox
* l
)
2395 if ((l
!= NULL
) && (l
->count
!= 0))
2399 current
= g_list_nth (l
->list
, l
->pos
);
2400 l
->list
= g_list_remove_link (l
->list
, current
);
2401 listbox_entry_free ((WLEntry
*) current
->data
);
2402 g_list_free_1 (current
);
2406 l
->top
= l
->pos
= 0;
2407 else if (l
->pos
>= l
->count
)
2408 l
->pos
= l
->count
- 1;
2413 listbox_select_entry (WListbox
* l
, int dest
)
2417 gboolean top_seen
= FALSE
;
2423 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2433 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2434 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2439 /* If we are unable to find it, set decent values */
2440 l
->pos
= l
->top
= 0;
2443 /* Selects from base the pos element */
2445 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2447 int last
= l
->count
- 1;
2457 listbox_fwd (WListbox
* l
)
2459 if (l
->pos
+ 1 >= l
->count
)
2460 listbox_select_first (l
);
2462 listbox_select_entry (l
, l
->pos
+ 1);
2466 listbox_back (WListbox
* l
)
2469 listbox_select_last (l
);
2471 listbox_select_entry (l
, l
->pos
- 1);
2474 /* Return MSG_HANDLED if we want a redraw */
2476 listbox_key (WListbox
* l
, int key
)
2480 cb_ret_t j
= MSG_NOT_HANDLED
;
2482 if (l
->list
== NULL
)
2483 return MSG_NOT_HANDLED
;
2485 /* focus on listbox item N by '0'..'9' keys */
2486 if (key
>= '0' && key
<= '9')
2488 int oldpos
= l
->pos
;
2489 listbox_select_entry (l
, key
- '0');
2491 /* need scroll to item? */
2492 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2503 listbox_select_first (l
);
2509 listbox_select_last (l
);
2524 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2533 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2544 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2545 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2547 listbox_remove_current (l
);
2548 if ((l
->top
> 0) && (is_last
|| is_more
))
2553 case (KEY_M_SHIFT
| KEY_DC
):
2555 if (l
->deletable
&& confirm_history_cleanup
2556 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2557 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2558 _("Do you want clean this history?"),
2559 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2561 listbox_remove_list (l
);
2574 listbox_destroy (WListbox
* l
)
2576 /* don't delete list in modifable listbox */
2578 listbox_remove_list (l
);
2582 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2584 WListbox
*l
= (WListbox
*) w
;
2585 Dlg_head
*h
= l
->widget
.parent
;
2597 pos
= listbox_check_hotkey (l
, parm
);
2599 return MSG_NOT_HANDLED
;
2601 listbox_select_entry (l
, pos
);
2602 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2604 if (l
->cback
!= NULL
)
2605 action
= l
->cback (l
);
2607 action
= LISTBOX_DONE
;
2609 if (action
== LISTBOX_DONE
)
2611 h
->ret_value
= B_ENTER
;
2619 ret_code
= listbox_key (l
, parm
);
2620 if (ret_code
!= MSG_NOT_HANDLED
)
2622 listbox_draw (l
, TRUE
);
2623 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2628 widget_move (&l
->widget
, l
->cursor_y
, 0);
2629 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2633 case WIDGET_UNFOCUS
:
2635 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2638 case WIDGET_DESTROY
:
2639 listbox_destroy (l
);
2642 case WIDGET_RESIZED
:
2646 return default_proc (msg
, parm
);
2651 listbox_event (Gpm_Event
* event
, void *data
)
2656 Dlg_head
*h
= l
->widget
.parent
;
2659 if (event
->type
& GPM_DOWN
)
2660 dlg_select_widget (l
);
2662 if (l
->list
== NULL
)
2665 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2667 int ret
= MOU_REPEAT
;
2669 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2673 for (i
= -event
->y
; i
>= 0; i
--)
2675 else if (event
->y
> l
->widget
.lines
)
2676 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2678 else if (event
->buttons
& GPM_B_UP
)
2683 else if (event
->buttons
& GPM_B_DOWN
)
2689 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2691 /* We need to refresh ourselves since the dialog manager doesn't */
2692 /* know about this event */
2693 listbox_draw (l
, TRUE
);
2698 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2702 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2703 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2706 dlg_select_widget (l
);
2707 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2709 if (l
->cback
!= NULL
)
2710 action
= l
->cback (l
);
2712 action
= LISTBOX_DONE
;
2714 if (action
== LISTBOX_DONE
)
2716 h
->ret_value
= B_ENTER
;
2725 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2727 WListbox
*l
= g_new (WListbox
, 1);
2732 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2735 l
->top
= l
->pos
= 0;
2737 l
->deletable
= deletable
;
2738 l
->cback
= callback
;
2739 l
->allow_duplicates
= TRUE
;
2740 l
->scrollbar
= !tty_is_slow ();
2741 widget_want_hotkey (l
->widget
, 1);
2747 listbox_entry_cmp (const void *a
, const void *b
)
2749 const WLEntry
*ea
= (const WLEntry
*) a
;
2750 const WLEntry
*eb
= (const WLEntry
*) b
;
2752 return strcmp (ea
->text
, eb
->text
);
2755 /* Listbox item adding function */
2757 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2761 case LISTBOX_APPEND_AT_END
:
2762 l
->list
= g_list_append (l
->list
, e
);
2765 case LISTBOX_APPEND_BEFORE
:
2766 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2771 case LISTBOX_APPEND_AFTER
:
2772 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2775 case LISTBOX_APPEND_SORTED
:
2776 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2787 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2794 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2797 entry
= g_new (WLEntry
, 1);
2798 entry
->text
= g_strdup (text
);
2800 entry
->hotkey
= hotkey
;
2802 listbox_append_item (l
, entry
, pos
);
2808 listbox_search_text (WListbox
* l
, const char *text
)
2815 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2817 WLEntry
*e
= (WLEntry
*) le
->data
;
2819 if (strcmp (e
->text
, text
) == 0)
2827 /* Returns the current string text as well as the associated extra data */
2829 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2835 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2840 *string
= ok
? e
->text
: NULL
;
2843 *extra
= ok
? e
->data
: NULL
;
2847 /* ButtonBar widget */
2849 /* returns TRUE if a function has been called, FALSE otherwise. */
2851 buttonbar_call (WButtonBar
* bb
, int i
)
2853 cb_ret_t ret
= MSG_NOT_HANDLED
;
2855 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
2856 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2857 (Widget
*) bb
, DLG_ACTION
,
2858 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
2862 /* calculate width of one button, width is never lesser than 7 */
2864 buttonbat_get_button_width (void)
2866 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2867 return (result
>= 7) ? result
: 7;
2871 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2873 WButtonBar
*bb
= (WButtonBar
*) w
;
2880 return MSG_NOT_HANDLED
;
2883 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2884 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2886 return MSG_NOT_HANDLED
;
2892 int count_free_positions
;
2894 widget_move (&bb
->widget
, 0, 0);
2895 tty_setcolor (DEFAULT_COLOR
);
2896 bb
->btn_width
= buttonbat_get_button_width ();
2897 tty_printf ("%-*s", bb
->widget
.cols
, "");
2898 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2900 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++)
2902 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2903 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2904 tty_printf ("%2d", i
+ 1);
2905 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2906 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2907 tty_print_string (str_fit_to_term (text
,
2908 bb
->btn_width
- 2 + (int) (offset
<
2909 count_free_positions
),
2912 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2918 case WIDGET_DESTROY
:
2919 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2920 g_free (bb
->labels
[i
].text
);
2924 return default_proc (msg
, parm
);
2929 buttonbar_event (Gpm_Event
* event
, void *data
)
2931 WButtonBar
*bb
= data
;
2934 if (!(event
->type
& GPM_UP
))
2938 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2939 if (button
< BUTTONBAR_LABELS_NUM
)
2940 buttonbar_call (bb
, button
);
2945 buttonbar_new (gboolean visible
)
2949 bb
= g_new0 (WButtonBar
, 1);
2951 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
2952 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2953 bb
->visible
= visible
;
2954 widget_want_hotkey (bb
->widget
, 1);
2955 widget_want_cursor (bb
->widget
, 0);
2956 bb
->btn_width
= buttonbat_get_button_width ();
2962 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
2964 g_free (bb
->labels
[lc_index
- 1].text
);
2965 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2968 /* Find ButtonBar widget in the dialog */
2970 find_buttonbar (const Dlg_head
* h
)
2972 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2976 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
2977 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
2979 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
2981 unsigned long command
= CK_Ignore_Key
;
2984 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2986 if ((text
== NULL
) || (text
[0] == '\0'))
2987 set_label_text (bb
, idx
, "");
2989 set_label_text (bb
, idx
, text
);
2991 bb
->labels
[idx
- 1].command
= command
;
2992 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
2997 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
2999 bb
->visible
= visible
;
3003 buttonbar_redraw (WButtonBar
* bb
)
3006 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3010 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3012 WGroupbox
*g
= (WGroupbox
*) w
;
3020 return MSG_NOT_HANDLED
;
3023 tty_setcolor (COLOR_NORMAL
);
3024 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3025 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3027 tty_setcolor (COLOR_HOT_NORMAL
);
3028 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3029 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
3030 tty_print_string (g
->title
);
3033 case WIDGET_DESTROY
:
3038 return default_proc (msg
, parm
);
3043 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3045 WGroupbox
*g
= g_new (WGroupbox
, 1);
3047 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3049 g
->widget
.options
&= ~W_WANT_CURSOR
;
3050 widget_want_hotkey (g
->widget
, 0);
3052 /* Strip existing spaces, add one space before and after the title */
3056 t
= g_strstrip (g_strdup (title
));
3057 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);