1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
11 2009, 2010 Andrew Borodin
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 * \brief Source: widgets
42 #include <sys/types.h>
44 #include "lib/global.h"
46 #include "lib/tty/tty.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
50 #include "lib/mcconfig.h" /* for history loading and saving */
51 #include "lib/vfs/mc-vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
59 #include "cmddef.h" /* CK_ cmd name const */
60 #include "keybind.h" /* global_keymap_t */
61 #include "panel.h" /* current_panel */
62 #include "main.h" /* confirm_history_cleanup */
63 #include "setup.h" /* num_history_items_recorded */
66 widget_selectcolor (Widget
* w
, gboolean focused
, gboolean hotkey
)
68 Dlg_head
*h
= w
->parent
;
73 : DLG_HOT_NORMALC (h
)) : (focused
? DLG_FOCUSC (h
) : DLG_NORMALC (h
)));
77 parse_hotkey (const char *text
)
79 struct hotkey_t result
;
82 /* search for '&', that is not on the of text */
83 cp
= strchr (text
, '&');
84 if (cp
!= NULL
&& cp
[1] != '\0')
86 result
.start
= g_strndup (text
, cp
- text
);
90 p
= str_cget_next_char (cp
);
91 result
.hotkey
= g_strndup (cp
, p
- cp
);
94 result
.end
= g_strdup (cp
);
98 result
.start
= g_strdup (text
);
107 release_hotkey (const struct hotkey_t hotkey
)
109 g_free (hotkey
.start
);
110 g_free (hotkey
.hotkey
);
115 hotkey_width (const struct hotkey_t hotkey
)
119 result
= str_term_width1 (hotkey
.start
);
120 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
121 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
126 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
128 widget_selectcolor (w
, focused
, FALSE
);
129 tty_print_string (hotkey
.start
);
131 if (hotkey
.hotkey
!= NULL
)
133 widget_selectcolor (w
, focused
, TRUE
);
134 tty_print_string (hotkey
.hotkey
);
135 widget_selectcolor (w
, focused
, FALSE
);
138 if (hotkey
.end
!= NULL
)
139 tty_print_string (hotkey
.end
);
143 /* Default callback for widgets */
145 default_proc (widget_msg_t msg
, int parm
)
161 return MSG_NOT_HANDLED
;
165 static int button_event (Gpm_Event
* event
, void *);
170 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
172 WButton
*b
= (WButton
*) w
;
175 Dlg_head
*h
= b
->widget
.parent
;
181 * Don't let the default button steal Enter from the current
182 * button. This is a workaround for the flawed event model
183 * when hotkeys are sent to all widgets before the key is
184 * handled by the current widget.
186 if (parm
== '\n' && h
->current
== &b
->widget
)
188 button_callback (w
, WIDGET_KEY
, ' ');
192 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
194 button_callback (w
, WIDGET_KEY
, ' ');
198 if (b
->text
.hotkey
!= NULL
)
200 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
202 button_callback (w
, WIDGET_KEY
, ' ');
206 return MSG_NOT_HANDLED
;
209 if (parm
!= ' ' && parm
!= '\n')
210 return MSG_NOT_HANDLED
;
213 stop
= (*b
->callback
) (b
->action
);
214 if (!b
->callback
|| stop
)
216 h
->ret_value
= b
->action
;
238 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
244 if (msg
== WIDGET_UNFOCUS
)
246 else if (msg
== WIDGET_FOCUS
)
249 widget_selectcolor (w
, b
->selected
, FALSE
);
250 widget_move (w
, 0, 0);
255 tty_print_string ("[< ");
258 tty_print_string ("[ ");
261 tty_print_string ("[");
268 draw_hotkey (w
, b
->text
, b
->selected
);
273 tty_print_string (" >]");
276 tty_print_string (" ]");
279 tty_print_string ("]");
285 release_hotkey (b
->text
);
289 return default_proc (msg
, parm
);
294 button_event (Gpm_Event
* event
, void *data
)
298 if (event
->type
& (GPM_DOWN
| GPM_UP
))
300 Dlg_head
*h
= b
->widget
.parent
;
301 dlg_select_widget (b
);
302 if (event
->type
& GPM_UP
)
304 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
305 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
313 button_get_len (const WButton
* b
)
315 int ret
= hotkey_width (b
->text
);
335 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
337 WButton
*b
= g_new (WButton
, 1);
341 b
->text
= parse_hotkey (text
);
343 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
346 b
->callback
= callback
;
347 widget_want_hotkey (b
->widget
, 1);
348 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
354 button_get_text (const WButton
* b
)
356 if (b
->text
.hotkey
!= NULL
)
357 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
359 return g_strdup (b
->text
.start
);
363 button_set_text (WButton
* b
, const char *text
)
365 release_hotkey (b
->text
);
366 b
->text
= parse_hotkey (text
);
367 b
->widget
.cols
= button_get_len (b
);
368 dlg_redraw (b
->widget
.parent
);
372 /* Radio button widget */
373 static int radio_event (Gpm_Event
* event
, void *);
376 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
378 WRadio
*r
= (WRadio
*) w
;
380 Dlg_head
*h
= r
->widget
.parent
;
386 int lp
= g_ascii_tolower ((gchar
) parm
);
388 for (i
= 0; i
< r
->count
; i
++)
390 if (r
->texts
[i
].hotkey
!= NULL
)
392 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
399 radio_callback (w
, WIDGET_KEY
, ' ');
404 return MSG_NOT_HANDLED
;
411 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
412 radio_callback (w
, WIDGET_FOCUS
, ' ');
422 return MSG_NOT_HANDLED
;
426 if (r
->count
- 1 > r
->pos
)
432 return MSG_NOT_HANDLED
;
435 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
436 radio_callback (w
, WIDGET_FOCUS
, ' ');
437 widget_move (&r
->widget
, r
->pos
, 1);
443 for (i
= 0; i
< r
->count
; i
++)
445 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
446 widget_selectcolor (w
, focused
, FALSE
);
447 widget_move (&r
->widget
, i
, 0);
448 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
449 draw_hotkey (w
, r
->texts
[i
], focused
);
454 for (i
= 0; i
< r
->count
; i
++)
456 release_hotkey (r
->texts
[i
]);
462 return default_proc (msg
, parm
);
467 radio_event (Gpm_Event
* event
, void *data
)
472 if (event
->type
& (GPM_DOWN
| GPM_UP
))
474 Dlg_head
*h
= r
->widget
.parent
;
476 r
->pos
= event
->y
- 1;
477 dlg_select_widget (r
);
478 if (event
->type
& GPM_UP
)
480 radio_callback (w
, WIDGET_KEY
, ' ');
481 radio_callback (w
, WIDGET_FOCUS
, 0);
482 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
490 radio_new (int y
, int x
, int count
, const char **texts
)
492 WRadio
*result
= g_new (WRadio
, 1);
495 /* Compute the longest string */
496 result
->texts
= g_new (struct hotkey_t
, count
);
499 for (i
= 0; i
< count
; i
++)
501 result
->texts
[i
] = parse_hotkey (texts
[i
]);
502 m
= hotkey_width (result
->texts
[i
]);
507 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
511 result
->count
= count
;
512 widget_want_hotkey (result
->widget
, 1);
518 /* Checkbutton widget */
520 static int check_event (Gpm_Event
* event
, void *);
523 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
525 WCheck
*c
= (WCheck
*) w
;
526 Dlg_head
*h
= c
->widget
.parent
;
531 if (c
->text
.hotkey
!= NULL
)
533 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
536 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
540 return MSG_NOT_HANDLED
;
544 return MSG_NOT_HANDLED
;
546 c
->state
^= C_CHANGE
;
547 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
548 check_callback (w
, WIDGET_FOCUS
, ' ');
552 widget_move (&c
->widget
, 0, 1);
558 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
559 widget_move (&c
->widget
, 0, 0);
560 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
561 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
565 release_hotkey (c
->text
);
569 return default_proc (msg
, parm
);
574 check_event (Gpm_Event
* event
, void *data
)
579 if (event
->type
& (GPM_DOWN
| GPM_UP
))
581 Dlg_head
*h
= c
->widget
.parent
;
583 dlg_select_widget (c
);
584 if (event
->type
& GPM_UP
)
586 check_callback (w
, WIDGET_KEY
, ' ');
587 check_callback (w
, WIDGET_FOCUS
, 0);
588 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
596 check_new (int y
, int x
, int state
, const char *text
)
598 WCheck
*c
= g_new (WCheck
, 1);
600 c
->text
= parse_hotkey (text
);
602 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
), check_callback
, check_event
);
603 c
->state
= state
? C_BOOL
: 0;
604 widget_want_hotkey (c
->widget
, 1);
610 save_text_to_clip_file (const char *text
)
617 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
618 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
619 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
625 str_len
= strlen (text
);
626 ret
= mc_write (file
, (char *) text
, str_len
);
628 return ret
== (ssize_t
) str_len
;
632 load_text_from_clip_file (char **text
)
637 gboolean first
= TRUE
;
639 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
640 f
= fopen (fname
, "r");
648 while (fgets (buf
, sizeof (buf
), f
))
655 if (buf
[len
- 1] == '\n')
661 *text
= g_strdup (buf
);
665 /* remove \n on EOL */
668 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
677 return (*text
!= NULL
);
681 panel_save_curent_file_to_clip_file (void)
685 if (current_panel
->marked
== 0)
686 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
690 gboolean first
= TRUE
;
693 for (i
= 0; i
< current_panel
->count
; i
++)
694 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
695 { /* Skip the unmarked ones */
698 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
703 /* Add empty lines after the file */
707 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
715 res
= save_text_to_clip_file (flist
);
725 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
727 WLabel
*l
= (WLabel
*) w
;
728 Dlg_head
*h
= l
->widget
.parent
;
735 /* We don't want to get the focus */
737 return MSG_NOT_HANDLED
;
741 char *p
= l
->text
, *q
, c
= 0;
748 tty_setcolor (DEFAULT_COLOR
);
750 tty_setcolor (DLG_NORMALC (h
));
754 q
= strchr (p
, '\n');
761 widget_move (&l
->widget
, y
, 0);
762 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
778 return default_proc (msg
, parm
);
783 label_set_text (WLabel
* label
, const char *text
)
785 int newcols
= label
->widget
.cols
;
788 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
789 return; /* Flickering is not nice */
791 g_free (label
->text
);
795 label
->text
= g_strdup (text
);
796 if (label
->auto_adjust_cols
)
798 str_msg_term_size (text
, &newlines
, &newcols
);
799 if (newcols
> label
->widget
.cols
)
800 label
->widget
.cols
= newcols
;
801 if (newlines
> label
->widget
.lines
)
802 label
->widget
.lines
= newlines
;
808 if (label
->widget
.parent
)
809 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
811 if (newcols
< label
->widget
.cols
)
812 label
->widget
.cols
= newcols
;
816 label_new (int y
, int x
, const char *text
)
823 str_msg_term_size (text
, &lines
, &cols
);
825 l
= g_new (WLabel
, 1);
826 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
827 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
828 l
->auto_adjust_cols
= 1;
830 widget_want_cursor (l
->widget
, 0);
835 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
837 WHLine
*l
= (WHLine
*) w
;
838 Dlg_head
*h
= l
->widget
.parent
;
844 if (l
->auto_adjust_cols
)
846 if (((w
->parent
->flags
& DLG_COMPACT
) != 0))
849 w
->cols
= w
->parent
->cols
;
853 w
->x
= w
->parent
->x
+ 1;
854 w
->cols
= w
->parent
->cols
- 2;
859 /* We don't want to get the focus */
860 return MSG_NOT_HANDLED
;
864 tty_setcolor (DEFAULT_COLOR
);
866 tty_setcolor (DLG_NORMALC (h
));
868 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
870 if (l
->auto_adjust_cols
)
872 widget_move (w
, 0, 0);
873 tty_print_alt_char (ACS_LTEE
, FALSE
);
874 widget_move (w
, 0, w
->cols
- 1);
875 tty_print_alt_char (ACS_RTEE
, FALSE
);
880 return default_proc (msg
, parm
);
886 hline_new (int y
, int x
, int width
)
892 l
= g_new (WHLine
, 1);
893 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
894 l
->auto_adjust_cols
= (width
< 0);
895 l
->transparent
= FALSE
;
896 widget_want_cursor (l
->widget
, 0);
900 /* Gauge widget (progress indicator) */
901 /* Currently width is hardcoded here for text mode */
905 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
907 WGauge
*g
= (WGauge
*) w
;
908 Dlg_head
*h
= g
->widget
.parent
;
910 if (msg
== WIDGET_INIT
)
913 /* We don't want to get the focus */
914 if (msg
== WIDGET_FOCUS
)
915 return MSG_NOT_HANDLED
;
917 if (msg
== WIDGET_DRAW
)
919 widget_move (&g
->widget
, 0, 0);
920 tty_setcolor (DLG_NORMALC (h
));
922 tty_printf ("%*s", gauge_len
, "");
925 int percentage
, columns
;
926 long total
= g
->max
, done
= g
->current
;
928 if (total
<= 0 || done
< 0)
935 while (total
> 65535)
940 percentage
= (200 * done
/ total
+ 1) / 2;
941 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
942 tty_print_char ('[');
943 if (g
->from_left_to_right
)
945 tty_setcolor (GAUGE_COLOR
);
946 tty_printf ("%*s", (int) columns
, "");
947 tty_setcolor (DLG_NORMALC (h
));
948 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
952 tty_setcolor (DLG_NORMALC (h
));
953 tty_printf ("%*s", gauge_len
- columns
- 7, "");
954 tty_setcolor (GAUGE_COLOR
);
955 tty_printf ("%*s", columns
, "");
956 tty_setcolor (DLG_NORMALC (h
));
957 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
963 return default_proc (msg
, parm
);
967 gauge_set_value (WGauge
* g
, int max
, int current
)
969 if (g
->current
== current
&& g
->max
== max
)
970 return; /* Do not flicker */
972 max
= 1; /* I do not like division by zero :) */
974 g
->current
= current
;
976 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
980 gauge_show (WGauge
* g
, int shown
)
982 if (g
->shown
== shown
)
985 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
989 gauge_new (int y
, int x
, int shown
, int max
, int current
)
991 WGauge
*g
= g_new (WGauge
, 1);
993 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
996 max
= 1; /* I do not like division by zero :) */
998 g
->current
= current
;
999 g
->from_left_to_right
= TRUE
;
1000 widget_want_cursor (g
->widget
, 0);
1007 /* {{{ history button */
1009 #define LARGE_HISTORY_BUTTON 1
1011 #ifdef LARGE_HISTORY_BUTTON
1012 # define HISTORY_BUTTON_WIDTH 3
1014 # define HISTORY_BUTTON_WIDTH 1
1017 #define should_show_history_button(in) \
1018 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1021 draw_history_button (WInput
* in
)
1024 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1025 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1026 #ifdef LARGE_HISTORY_BUTTON
1029 h
= in
->widget
.parent
;
1030 tty_setcolor (NORMAL_COLOR
);
1031 tty_print_string ("[ ]");
1032 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1033 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1037 tty_setcolor (MARKED_COLOR
);
1042 /* }}} history button */
1045 /* Input widgets now have a global kill ring */
1046 /* Pointer to killed data */
1047 static char *kill_buffer
= NULL
;
1050 input_set_markers (WInput
* in
, long m1
)
1056 input_mark_cmd (WInput
* in
, gboolean mark
)
1060 in
->highlight
= FALSE
;
1061 input_set_markers (in
, 0);
1065 in
->highlight
= TRUE
;
1066 input_set_markers (in
, in
->point
);
1071 input_eval_marks (WInput
* in
, long *start_mark
, long *end_mark
)
1075 *start_mark
= min (in
->mark
, in
->point
);
1076 *end_mark
= max (in
->mark
, in
->point
);
1081 *start_mark
= *end_mark
= 0;
1087 delete_region (WInput
* in
, int x_first
, int x_last
)
1089 int first
= min (x_first
, x_last
);
1090 int last
= max (x_first
, x_last
);
1093 input_mark_cmd (in
, FALSE
);
1095 last
= str_offset_to_pos (in
->buffer
, last
);
1096 first
= str_offset_to_pos (in
->buffer
, first
);
1097 len
= strlen (&in
->buffer
[last
]) + 1;
1098 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1104 update_input (WInput
* in
, int clear_first
)
1106 int has_history
= 0;
1108 int buf_len
= str_length (in
->buffer
);
1112 if (should_show_history_button (in
))
1113 has_history
= HISTORY_BUTTON_WIDTH
;
1115 if (in
->disable_update
)
1118 pw
= str_term_width2 (in
->buffer
, in
->point
);
1120 /* Make the point visible */
1121 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1124 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1125 if (in
->term_first_shown
< 0)
1126 in
->term_first_shown
= 0;
1129 /* Adjust the mark */
1130 if (in
->mark
> buf_len
)
1134 draw_history_button (in
);
1137 tty_setcolor (in
->unchanged_color
);
1139 tty_setcolor (in
->color
);
1141 widget_move (&in
->widget
, 0, 0);
1143 if (!in
->is_password
)
1147 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1148 in
->field_width
- has_history
));
1153 if (input_eval_marks (in
, &m1
, &m2
))
1155 tty_setcolor (in
->color
);
1156 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1157 tty_print_string (cp
);
1158 tty_setcolor (in
->mark_color
);
1159 if (m1
< in
->term_first_shown
)
1161 widget_move (&in
->widget
, 0, 0);
1162 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
, m2
- in
->term_first_shown
));
1167 widget_move (&in
->widget
, 0, m1
- in
->term_first_shown
);
1168 sel_width
= min (m2
- m1
, (in
->field_width
- has_history
) - (str_term_width2 (in
->buffer
, m1
) - in
->term_first_shown
));
1169 tty_print_string (str_term_substring (in
->buffer
, m1
, sel_width
));
1176 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1177 for (i
= 0; i
< in
->field_width
- has_history
; i
++)
1181 tty_setcolor (in
->color
);
1182 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1185 str_cnext_char (&cp
);
1194 winput_set_origin (WInput
* in
, int x
, int field_width
)
1197 in
->field_width
= in
->widget
.cols
= field_width
;
1198 update_input (in
, 0);
1201 /* {{{ history saving and loading */
1203 int num_history_items_recorded
= 60;
1206 This loads and saves the history of an input line to and from the
1207 widget. It is called with the widgets history name on creation of the
1208 widget, and returns the GList list. It stores histories in the file
1209 ~/.mc/history in using the profile code.
1211 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1212 function) then input_new assigns the default text to be the last text
1213 entered, or "" if not found.
1217 history_get (const char *input_name
)
1224 size_t keys_num
= 0;
1227 if (num_history_items_recorded
== 0) /* this is how to disable */
1229 if ((input_name
== NULL
) || (*input_name
== '\0'))
1232 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1233 cfg
= mc_config_init (profile
);
1235 /* get number of keys */
1236 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1239 for (i
= 0; i
< keys_num
; i
++)
1242 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1243 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1245 if (this_entry
!= NULL
)
1246 hist
= list_append_unique (hist
, this_entry
);
1249 mc_config_deinit (cfg
);
1252 /* return pointer to the last entry in the list */
1253 return g_list_last (hist
);
1257 history_put (const char *input_name
, GList
* h
)
1263 if (num_history_items_recorded
== 0) /* this is how to disable */
1265 if ((input_name
== NULL
) || (*input_name
== '\0'))
1270 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1272 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1276 /* Make sure the history is only readable by the user */
1277 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1283 /* go to end of list */
1284 h
= g_list_last (h
);
1286 /* go back 60 places */
1287 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1288 h
= g_list_previous (h
);
1290 cfg
= mc_config_init (profile
);
1292 if (input_name
!= NULL
)
1293 mc_config_del_group (cfg
, input_name
);
1295 /* dump history into profile */
1296 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1298 char *text
= (char *) h
->data
;
1300 /* We shouldn't have null entries, but let's be sure */
1304 g_snprintf (key
, sizeof (key
), "%d", i
++);
1305 mc_config_set_string (cfg
, input_name
, key
, text
);
1309 mc_config_save_file (cfg
, NULL
);
1310 mc_config_deinit (cfg
);
1314 /* }}} history saving and loading */
1317 /* {{{ history display */
1322 return _(" History ");
1333 dlg_hist_reposition (Dlg_head
* dlg_head
)
1335 dlg_hist_data
*data
;
1336 int x
= 0, y
, he
, wi
;
1339 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1340 return MSG_NOT_HANDLED
;
1342 data
= (dlg_hist_data
*) dlg_head
->data
;
1344 y
= data
->widget
->y
;
1345 he
= data
->count
+ 2;
1347 if (he
<= y
|| y
> (LINES
- 6))
1349 he
= min (he
, y
- 1);
1355 he
= min (he
, LINES
- y
);
1358 if (data
->widget
->x
> 2)
1359 x
= data
->widget
->x
- 2;
1361 wi
= data
->maxlen
+ 4;
1363 if ((wi
+ x
) > COLS
)
1365 wi
= min (wi
, COLS
);
1369 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1375 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1380 return dlg_hist_reposition (h
);
1383 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1388 show_hist (GList
** history
, Widget
* widget
)
1390 GList
*z
, *hlist
= NULL
, *hi
;
1391 size_t maxlen
, i
, count
= 0;
1393 Dlg_head
*query_dlg
;
1394 WListbox
*query_list
;
1395 dlg_hist_data hist_data
;
1397 if (*history
== NULL
)
1400 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1402 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1406 i
= str_term_width1 ((char *) z
->data
);
1407 maxlen
= max (maxlen
, i
);
1410 entry
= g_new0 (WLEntry
, 1);
1411 /* history is being reverted here */
1412 entry
->text
= g_strdup ((char *) z
->data
);
1413 hlist
= g_list_prepend (hlist
, entry
);
1416 hist_data
.widget
= widget
;
1417 hist_data
.count
= count
;
1418 hist_data
.maxlen
= maxlen
;
1421 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1422 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1423 query_dlg
->data
= &hist_data
;
1425 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1427 /* this call makes list stick to all sides of dialog, effectively make
1428 it be resized with dialog */
1429 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1431 /* to avoid diplicating of (calculating sizes in two places)
1432 code, call dlg_hist_callback function here, to set dialog and
1434 The main idea - create 4x4 dialog and add 2x2 list in
1435 center of it, and let dialog function resize it to needed
1437 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1439 if (query_dlg
->y
< widget
->y
)
1441 /* draw list entries from bottom upto top */
1442 listbox_set_list (query_list
, hlist
);
1443 listbox_select_last (query_list
);
1447 /* draw list entries from top downto bottom */
1448 /* revert history direction */
1449 hlist
= g_list_reverse (hlist
);
1450 listbox_set_list (query_list
, hlist
);
1453 if (run_dlg (query_dlg
) != B_CANCEL
)
1457 listbox_get_current (query_list
, &q
, NULL
);
1462 /* get modified history from dialog */
1464 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1468 entry
= (WLEntry
*) hi
->data
;
1469 /* history is being reverted here again */
1470 z
= g_list_prepend (z
, entry
->text
);
1474 destroy_dlg (query_dlg
);
1476 /* restore history direction */
1477 if (query_dlg
->y
< widget
->y
)
1478 z
= g_list_reverse (z
);
1480 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1481 g_list_free (*history
);
1482 *history
= g_list_last (z
);
1488 do_show_hist (WInput
* in
)
1492 r
= show_hist (&in
->history
, &in
->widget
);
1495 assign_text (in
, r
);
1500 /* }}} history display */
1503 input_destroy (WInput
* in
)
1507 fprintf (stderr
, "Internal error: null Input *\n");
1508 exit (EXIT_FAILURE
);
1513 if (in
->history
!= NULL
)
1515 if (!in
->is_password
&& (((Widget
*) in
)->parent
->ret_value
!= B_CANCEL
))
1516 history_put (in
->history_name
, in
->history
);
1518 in
->history
= g_list_first (in
->history
);
1519 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1520 g_list_free (in
->history
);
1523 g_free (in
->buffer
);
1524 free_completions (in
);
1525 g_free (in
->history_name
);
1527 g_free (kill_buffer
);
1532 input_disable_update (WInput
* in
)
1534 in
->disable_update
++;
1538 input_enable_update (WInput
* in
)
1540 in
->disable_update
--;
1541 update_input (in
, 0);
1546 push_history (WInput
* in
, const char *text
)
1548 /* input widget where urls with passwords are entered without any
1550 const char *password_input_fields
[] = {
1551 N_(" Link to a remote machine "),
1552 N_(" FTP to machine "),
1553 N_(" SMB link to machine ")
1555 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1565 for (i
= 0; i
< ELEMENTS
; i
++)
1566 password_input_fields
[i
] = _(password_input_fields
[i
]);
1569 t
= g_strstrip (g_strdup (text
));
1572 t
= g_strdup (empty
? "" : text
);
1574 if (in
->history_name
!= NULL
)
1576 const char *p
= in
->history_name
+ 3;
1578 for (i
= 0; i
< ELEMENTS
; i
++)
1579 if (strcmp (p
, password_input_fields
[i
]) == 0)
1582 strip_password (t
, i
>= ELEMENTS
);
1585 in
->history
= list_append_unique (in
->history
, t
);
1589 /* Cleans the input line and adds the current text to the history */
1591 new_input (WInput
* in
)
1593 push_history (in
, in
->buffer
);
1595 in
->buffer
[0] = '\0';
1599 in
->highlight
= FALSE
;
1600 free_completions (in
);
1601 update_input (in
, 0);
1605 move_buffer_backward (WInput
* in
, int start
, int end
)
1608 int str_len
= str_length (in
->buffer
);
1609 if (start
>= str_len
|| end
> str_len
+ 1)
1612 pos
= str_offset_to_pos (in
->buffer
, start
);
1613 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1615 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1616 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1620 insert_char (WInput
* in
, int c_code
)
1628 if (input_eval_marks (in
, &m1
, &m2
))
1629 delete_region (in
, m1
, m2
);
1632 return MSG_NOT_HANDLED
;
1634 if (in
->charpoint
>= MB_LEN_MAX
)
1637 in
->charbuf
[in
->charpoint
] = c_code
;
1640 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1644 in
->charpoint
= 0; /* broken multibyte char, skip */
1649 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1651 /* Expand the buffer */
1652 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1653 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1657 in
->current_max_size
= new_length
;
1661 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1663 /* bytes from begin */
1664 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1666 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1668 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1669 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1671 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1680 beginning_of_line (WInput
* in
)
1687 end_of_line (WInput
* in
)
1689 in
->point
= str_length (in
->buffer
);
1694 backward_char (WInput
* in
)
1696 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1700 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1706 forward_char (WInput
* in
)
1708 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1711 in
->point
+= str_cnext_noncomb_char (&act
);
1717 forward_word (WInput
* in
)
1719 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1721 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1723 str_cnext_char (&p
);
1726 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1728 str_cnext_char (&p
);
1734 backward_word (WInput
* in
)
1739 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1740 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1742 while (p
!= in
->buffer
)
1745 str_cprev_char (&p
);
1746 if (!str_isspace (p
) && !str_ispunct (p
))
1753 while (p
!= in
->buffer
)
1755 str_cprev_char (&p
);
1756 if (str_isspace (p
) || str_ispunct (p
))
1764 key_left (WInput
* in
)
1770 key_ctrl_left (WInput
* in
)
1776 key_right (WInput
* in
)
1782 key_ctrl_right (WInput
* in
)
1787 backward_delete (WInput
* in
)
1789 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1795 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1796 move_buffer_backward (in
, start
, in
->point
);
1803 delete_char (WInput
* in
)
1805 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1806 int end
= in
->point
;
1808 end
+= str_cnext_noncomb_char (&act
);
1810 move_buffer_backward (in
, in
->point
, end
);
1816 copy_region (WInput
* in
, int x_first
, int x_last
)
1818 int first
= min (x_first
, x_last
);
1819 int last
= max (x_first
, x_last
);
1823 /* Copy selected files to clipboard */
1824 panel_save_curent_file_to_clip_file ();
1828 g_free (kill_buffer
);
1830 first
= str_offset_to_pos (in
->buffer
, first
);
1831 last
= str_offset_to_pos (in
->buffer
, last
);
1833 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1834 save_text_to_clip_file (kill_buffer
);
1838 kill_word (WInput
* in
)
1840 int old_point
= in
->point
;
1844 new_point
= in
->point
;
1845 in
->point
= old_point
;
1847 copy_region (in
, old_point
, new_point
);
1848 delete_region (in
, old_point
, new_point
);
1855 back_kill_word (WInput
* in
)
1857 int old_point
= in
->point
;
1861 new_point
= in
->point
;
1862 in
->point
= old_point
;
1864 copy_region (in
, old_point
, new_point
);
1865 delete_region (in
, old_point
, new_point
);
1870 set_mark (WInput
* in
)
1872 input_mark_cmd (in
, TRUE
);
1876 kill_save (WInput
* in
)
1878 copy_region (in
, in
->mark
, in
->point
);
1882 kill_region (WInput
* in
)
1885 delete_region (in
, in
->point
, in
->mark
);
1889 clear_region (WInput
* in
)
1891 delete_region (in
, in
->point
, in
->mark
);
1897 if (kill_buffer
!= NULL
)
1902 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1903 insert_char (in
, *p
);
1909 kill_line (WInput
* in
)
1911 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1912 g_free (kill_buffer
);
1913 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1914 in
->buffer
[chp
] = '\0';
1919 ins_from_clip (WInput
* in
)
1923 if (load_text_from_clip_file (&p
))
1927 for (pp
= p
; *pp
!= '\0'; pp
++)
1928 insert_char (in
, *pp
);
1935 assign_text (WInput
* in
, const char *text
)
1937 free_completions (in
);
1938 g_free (in
->buffer
);
1939 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1940 in
->current_max_size
= strlen (in
->buffer
) + 1;
1941 in
->point
= str_length (in
->buffer
);
1948 hist_prev (WInput
* in
)
1956 push_history (in
, in
->buffer
);
1958 prev
= g_list_previous (in
->history
);
1962 assign_text (in
, (char *) prev
->data
);
1968 hist_next (WInput
* in
)
1972 push_history (in
, in
->buffer
);
1973 assign_text (in
, "");
1980 if (!in
->history
->next
)
1982 assign_text (in
, "");
1986 in
->history
= g_list_next (in
->history
);
1987 assign_text (in
, (char *) in
->history
->data
);
1992 port_region_marked_for_delete (WInput
* in
)
1994 in
->buffer
[0] = '\0';
2001 input_execute_cmd (WInput
* in
, unsigned long command
)
2003 cb_ret_t res
= MSG_HANDLED
;
2005 /* a highlight command like shift-arrow */
2006 if (command
== CK_InputLeftHighlight
||
2007 command
== CK_InputRightHighlight
||
2008 command
== CK_InputWordLeftHighlight
||
2009 command
== CK_InputWordRightHighlight
||
2010 command
== CK_InputBolHighlight
||
2011 command
== CK_InputEolHighlight
)
2015 input_mark_cmd (in
, FALSE
); /* clear */
2016 input_mark_cmd (in
, TRUE
); /* marking on */
2022 case CK_InputForwardWord
:
2023 case CK_InputBackwardWord
:
2024 case CK_InputForwardChar
:
2025 case CK_InputBackwardChar
:
2027 input_mark_cmd (in
, FALSE
);
2033 case CK_InputBolHighlight
:
2034 beginning_of_line (in
);
2037 case CK_InputEolHighlight
:
2040 case CK_InputMoveLeft
:
2041 case CK_InputLeftHighlight
:
2044 case CK_InputWordLeft
:
2045 case CK_InputWordLeftHighlight
:
2048 case CK_InputMoveRight
:
2049 case CK_InputRightHighlight
:
2052 case CK_InputWordRight
:
2053 case CK_InputWordRightHighlight
:
2054 key_ctrl_right (in
);
2056 case CK_InputBackwardChar
:
2059 case CK_InputBackwardWord
:
2062 case CK_InputForwardChar
:
2065 case CK_InputForwardWord
:
2068 case CK_InputBackwardDelete
:
2072 if (input_eval_marks (in
, &m1
, &m2
))
2073 delete_region (in
, m1
, m2
);
2077 backward_delete (in
);
2080 case CK_InputDeleteChar
:
2082 port_region_marked_for_delete (in
);
2083 else if (in
->highlight
)
2086 if (input_eval_marks (in
, &m1
, &m2
))
2087 delete_region (in
, m1
, m2
);
2092 case CK_InputKillWord
:
2095 case CK_InputBackwardKillWord
:
2096 back_kill_word (in
);
2098 case CK_InputSetMark
:
2101 case CK_InputKillRegion
:
2104 case CK_InputClearLine
:
2107 case CK_InputKillSave
:
2116 case CK_InputKillLine
:
2119 case CK_InputHistoryPrev
:
2122 case CK_InputHistoryNext
:
2125 case CK_InputHistoryShow
:
2128 case CK_InputComplete
:
2132 res
= MSG_NOT_HANDLED
;
2135 if (command
!= CK_InputLeftHighlight
&&
2136 command
!= CK_InputRightHighlight
&&
2137 command
!= CK_InputWordLeftHighlight
&&
2138 command
!= CK_InputWordRightHighlight
&&
2139 command
!= CK_InputBolHighlight
&&
2140 command
!= CK_InputEolHighlight
)
2142 in
->highlight
= FALSE
;
2148 /* This function is a test for a special input key used in complete.c */
2149 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2150 and 2 if it is a complete key */
2152 is_in_input_map (WInput
* in
, int key
)
2155 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2156 if (key
== input_map
[i
].key
)
2158 input_execute_cmd (in
, input_map
[i
].command
);
2159 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2165 handle_char (WInput
* in
, int key
)
2170 v
= MSG_NOT_HANDLED
;
2174 free_completions (in
);
2175 v
= insert_char (in
, key
);
2176 update_input (in
, 1);
2180 for (i
= 0; input_map
[i
].key
; i
++)
2182 if (key
== input_map
[i
].key
)
2184 if (input_map
[i
].command
!= CK_InputComplete
)
2185 free_completions (in
);
2186 input_execute_cmd (in
, input_map
[i
].command
);
2187 update_input (in
, 1);
2192 if (input_map
[i
].command
== 0)
2195 return MSG_NOT_HANDLED
;
2197 port_region_marked_for_delete (in
);
2198 free_completions (in
);
2199 v
= insert_char (in
, key
);
2201 update_input (in
, 1);
2205 /* Inserts text in input line */
2207 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2209 input_disable_update (in
);
2210 while (*text
!= '\0')
2211 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2212 if (insert_extra_space
)
2213 handle_char (in
, ' ');
2214 input_enable_update (in
);
2215 update_input (in
, 1);
2219 input_set_point (WInput
* in
, int pos
)
2221 int max_pos
= str_length (in
->buffer
);
2225 if (pos
!= in
->point
)
2226 free_completions (in
);
2229 update_input (in
, 1);
2233 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2235 WInput
*in
= (WInput
*) w
;
2241 if (parm
== XCTRL ('q'))
2244 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2249 /* Keys we want others to handle */
2250 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2251 || parm
== KEY_F (10) || parm
== '\n')
2252 return MSG_NOT_HANDLED
;
2254 /* When pasting multiline text, insert literal Enter */
2255 if ((parm
& ~KEY_M_MASK
) == '\n')
2258 v
= handle_char (in
, '\n');
2263 return handle_char (in
, parm
);
2265 case WIDGET_COMMAND
:
2266 return input_execute_cmd (in
, parm
);
2269 case WIDGET_UNFOCUS
:
2271 update_input (in
, 0);
2275 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2276 - in
->term_first_shown
);
2279 case WIDGET_DESTROY
:
2284 return default_proc (msg
, parm
);
2289 input_event (Gpm_Event
* event
, void *data
)
2293 if (event
->type
& GPM_DOWN
)
2296 input_mark_cmd (in
, FALSE
);
2298 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2300 dlg_select_widget (in
);
2302 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2303 && should_show_history_button (in
))
2309 in
->point
= str_length (in
->buffer
);
2310 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2311 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2313 update_input (in
, 1);
2315 /* A lone up mustn't do anything */
2316 if (in
->highlight
&& event
->type
& (GPM_UP
| GPM_DRAG
))
2319 if (!(event
->type
& GPM_DRAG
))
2320 input_mark_cmd (in
, TRUE
);
2326 input_new (int y
, int x
, int *input_colors
, int width
, const char *def_text
,
2327 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2329 WInput
*in
= g_new (WInput
, 1);
2330 size_t initial_buffer_len
;
2332 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2335 in
->history_name
= NULL
;
2337 if ((histname
!= NULL
) && (*histname
!= '\0'))
2339 in
->history_name
= g_strdup (histname
);
2340 in
->history
= history_get (histname
);
2343 if (def_text
== NULL
)
2345 else if (def_text
== INPUT_LAST_TEXT
)
2347 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2348 def_text
= (char *) in
->history
->data
;
2353 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2354 in
->widget
.options
|= W_IS_INPUT
;
2355 in
->completions
= NULL
;
2356 in
->completion_flags
= completion_flags
;
2357 in
->current_max_size
= initial_buffer_len
;
2358 in
->buffer
= g_new (char, initial_buffer_len
);
2359 in
->color
= input_colors
[0];
2360 in
->unchanged_color
= input_colors
[1];
2361 in
->mark_color
= input_colors
[2];
2362 in
->field_width
= width
;
2364 in
->highlight
= FALSE
;
2365 in
->term_first_shown
= 0;
2366 in
->disable_update
= 0;
2369 in
->is_password
= 0;
2371 strcpy (in
->buffer
, def_text
);
2372 in
->point
= str_length (in
->buffer
);
2379 /* Listbox widget */
2381 /* Should draw the scrollbar, but currently draws only
2382 * indications that there is more information
2386 listbox_entry_free (void *data
)
2394 listbox_drawscroll (WListbox
* l
)
2396 const int max_line
= l
->widget
.lines
- 1;
2400 /* Are we at the top? */
2401 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2403 tty_print_one_vline (TRUE
);
2405 tty_print_char ('^');
2407 /* Are we at the bottom? */
2408 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2409 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2410 tty_print_one_vline (TRUE
);
2412 tty_print_char ('v');
2414 /* Now draw the nice relative pointer */
2416 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2418 for (i
= 1; i
< max_line
; i
++)
2420 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2422 tty_print_one_vline (TRUE
);
2424 tty_print_char ('*');
2429 listbox_draw (WListbox
* l
, gboolean focused
)
2431 const Dlg_head
*h
= l
->widget
.parent
;
2432 const int normalc
= DLG_NORMALC (h
);
2433 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2440 le
= g_list_nth (l
->list
, l
->top
);
2441 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2442 pos
= (le
== NULL
) ? 0 : l
->top
;
2444 for (i
= 0; i
< l
->widget
.lines
; i
++)
2448 /* Display the entry */
2449 if (pos
== l
->pos
&& sel_line
== -1)
2452 tty_setcolor (selc
);
2455 tty_setcolor (normalc
);
2457 widget_move (&l
->widget
, i
, 1);
2459 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2463 WLEntry
*e
= (WLEntry
*) le
->data
;
2465 le
= g_list_next (le
);
2469 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2472 l
->cursor_y
= sel_line
;
2474 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2476 tty_setcolor (normalc
);
2477 listbox_drawscroll (l
);
2482 listbox_check_hotkey (WListbox
* l
, int key
)
2487 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2489 WLEntry
*e
= (WLEntry
*) le
->data
;
2491 if (e
->hotkey
== key
)
2498 /* Selects the last entry and scrolls the list to the bottom */
2500 listbox_select_last (WListbox
* l
)
2502 l
->pos
= l
->count
- 1;
2503 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2506 /* Selects the first entry and scrolls the list to the top */
2508 listbox_select_first (WListbox
* l
)
2510 l
->pos
= l
->top
= 0;
2514 listbox_set_list (WListbox
* l
, GList
* list
)
2516 listbox_remove_list (l
);
2521 l
->top
= l
->pos
= 0;
2522 l
->count
= g_list_length (list
);
2527 listbox_remove_list (WListbox
* l
)
2529 if ((l
!= NULL
) && (l
->count
!= 0))
2531 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2532 g_list_free (l
->list
);
2534 l
->count
= l
->pos
= l
->top
= 0;
2539 listbox_remove_current (WListbox
* l
)
2541 if ((l
!= NULL
) && (l
->count
!= 0))
2545 current
= g_list_nth (l
->list
, l
->pos
);
2546 l
->list
= g_list_remove_link (l
->list
, current
);
2547 listbox_entry_free ((WLEntry
*) current
->data
);
2548 g_list_free_1 (current
);
2552 l
->top
= l
->pos
= 0;
2553 else if (l
->pos
>= l
->count
)
2554 l
->pos
= l
->count
- 1;
2559 listbox_select_entry (WListbox
* l
, int dest
)
2563 gboolean top_seen
= FALSE
;
2569 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2579 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2580 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2585 /* If we are unable to find it, set decent values */
2586 l
->pos
= l
->top
= 0;
2589 /* Selects from base the pos element */
2591 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2593 int last
= l
->count
- 1;
2603 listbox_fwd (WListbox
* l
)
2605 if (l
->pos
+ 1 >= l
->count
)
2606 listbox_select_first (l
);
2608 listbox_select_entry (l
, l
->pos
+ 1);
2612 listbox_back (WListbox
* l
)
2615 listbox_select_last (l
);
2617 listbox_select_entry (l
, l
->pos
- 1);
2620 /* Return MSG_HANDLED if we want a redraw */
2622 listbox_key (WListbox
* l
, int key
)
2626 cb_ret_t j
= MSG_NOT_HANDLED
;
2628 if (l
->list
== NULL
)
2629 return MSG_NOT_HANDLED
;
2631 /* focus on listbox item N by '0'..'9' keys */
2632 if (key
>= '0' && key
<= '9')
2634 int oldpos
= l
->pos
;
2635 listbox_select_entry (l
, key
- '0');
2637 /* need scroll to item? */
2638 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2649 listbox_select_first (l
);
2655 listbox_select_last (l
);
2670 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2679 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2690 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2691 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2693 listbox_remove_current (l
);
2694 if ((l
->top
> 0) && (is_last
|| is_more
))
2699 case (KEY_M_SHIFT
| KEY_DC
):
2701 if (l
->deletable
&& confirm_history_cleanup
2702 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2703 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2704 _("Do you want clean this history?"),
2705 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2707 listbox_remove_list (l
);
2720 listbox_destroy (WListbox
* l
)
2722 /* don't delete list in modifable listbox */
2724 listbox_remove_list (l
);
2728 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2730 WListbox
*l
= (WListbox
*) w
;
2731 Dlg_head
*h
= l
->widget
.parent
;
2743 pos
= listbox_check_hotkey (l
, parm
);
2745 return MSG_NOT_HANDLED
;
2747 listbox_select_entry (l
, pos
);
2748 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2750 if (l
->cback
!= NULL
)
2751 action
= l
->cback (l
);
2753 action
= LISTBOX_DONE
;
2755 if (action
== LISTBOX_DONE
)
2757 h
->ret_value
= B_ENTER
;
2765 ret_code
= listbox_key (l
, parm
);
2766 if (ret_code
!= MSG_NOT_HANDLED
)
2768 listbox_draw (l
, TRUE
);
2769 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2774 widget_move (&l
->widget
, l
->cursor_y
, 0);
2775 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2779 case WIDGET_UNFOCUS
:
2781 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2784 case WIDGET_DESTROY
:
2785 listbox_destroy (l
);
2788 case WIDGET_RESIZED
:
2792 return default_proc (msg
, parm
);
2797 listbox_event (Gpm_Event
* event
, void *data
)
2802 Dlg_head
*h
= l
->widget
.parent
;
2805 if (event
->type
& GPM_DOWN
)
2806 dlg_select_widget (l
);
2808 if (l
->list
== NULL
)
2811 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2813 int ret
= MOU_REPEAT
;
2815 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2819 for (i
= -event
->y
; i
>= 0; i
--)
2821 else if (event
->y
> l
->widget
.lines
)
2822 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2824 else if (event
->buttons
& GPM_B_UP
)
2829 else if (event
->buttons
& GPM_B_DOWN
)
2835 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2837 /* We need to refresh ourselves since the dialog manager doesn't */
2838 /* know about this event */
2839 listbox_draw (l
, TRUE
);
2844 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2848 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2849 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2852 dlg_select_widget (l
);
2853 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2855 if (l
->cback
!= NULL
)
2856 action
= l
->cback (l
);
2858 action
= LISTBOX_DONE
;
2860 if (action
== LISTBOX_DONE
)
2862 h
->ret_value
= B_ENTER
;
2871 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2873 WListbox
*l
= g_new (WListbox
, 1);
2878 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2881 l
->top
= l
->pos
= 0;
2883 l
->deletable
= deletable
;
2884 l
->cback
= callback
;
2885 l
->allow_duplicates
= TRUE
;
2886 l
->scrollbar
= !tty_is_slow ();
2887 widget_want_hotkey (l
->widget
, 1);
2888 widget_want_cursor (l
->widget
, 0);
2894 listbox_entry_cmp (const void *a
, const void *b
)
2896 const WLEntry
*ea
= (const WLEntry
*) a
;
2897 const WLEntry
*eb
= (const WLEntry
*) b
;
2899 return strcmp (ea
->text
, eb
->text
);
2902 /* Listbox item adding function */
2904 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2908 case LISTBOX_APPEND_AT_END
:
2909 l
->list
= g_list_append (l
->list
, e
);
2912 case LISTBOX_APPEND_BEFORE
:
2913 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2918 case LISTBOX_APPEND_AFTER
:
2919 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2922 case LISTBOX_APPEND_SORTED
:
2923 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2934 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2941 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2944 entry
= g_new (WLEntry
, 1);
2945 entry
->text
= g_strdup (text
);
2947 entry
->hotkey
= hotkey
;
2949 listbox_append_item (l
, entry
, pos
);
2955 listbox_search_text (WListbox
* l
, const char *text
)
2962 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2964 WLEntry
*e
= (WLEntry
*) le
->data
;
2966 if (strcmp (e
->text
, text
) == 0)
2974 /* Returns the current string text as well as the associated extra data */
2976 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
2982 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2987 *string
= ok
? e
->text
: NULL
;
2990 *extra
= ok
? e
->data
: NULL
;
2994 /* ButtonBar widget */
2996 /* returns TRUE if a function has been called, FALSE otherwise. */
2998 buttonbar_call (WButtonBar
* bb
, int i
)
3000 cb_ret_t ret
= MSG_NOT_HANDLED
;
3002 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
3003 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
3004 (Widget
*) bb
, DLG_ACTION
,
3005 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
3009 /* calculate width of one button, width is never lesser than 7 */
3011 buttonbat_get_button_width (void)
3013 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
3014 return (result
>= 7) ? result
: 7;
3018 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3020 WButtonBar
*bb
= (WButtonBar
*) w
;
3027 return MSG_NOT_HANDLED
;
3030 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3031 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
3033 return MSG_NOT_HANDLED
;
3039 int count_free_positions
;
3041 widget_move (&bb
->widget
, 0, 0);
3042 tty_setcolor (DEFAULT_COLOR
);
3043 bb
->btn_width
= buttonbat_get_button_width ();
3044 tty_printf ("%-*s", bb
->widget
.cols
, "");
3045 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
3047 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++)
3049 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
3050 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
3051 tty_printf ("%2d", i
+ 1);
3052 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
3053 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
3054 tty_print_string (str_fit_to_term (text
,
3055 bb
->btn_width
- 2 + (int) (offset
<
3056 count_free_positions
),
3059 if (count_free_positions
!= 0 && offset
< count_free_positions
)
3065 case WIDGET_DESTROY
:
3066 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3067 g_free (bb
->labels
[i
].text
);
3071 return default_proc (msg
, parm
);
3076 buttonbar_event (Gpm_Event
* event
, void *data
)
3078 WButtonBar
*bb
= data
;
3081 if (!(event
->type
& GPM_UP
))
3085 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
3086 if (button
< BUTTONBAR_LABELS_NUM
)
3087 buttonbar_call (bb
, button
);
3092 buttonbar_new (gboolean visible
)
3096 bb
= g_new0 (WButtonBar
, 1);
3098 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
3099 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
3100 bb
->visible
= visible
;
3101 widget_want_hotkey (bb
->widget
, 1);
3102 widget_want_cursor (bb
->widget
, 0);
3103 bb
->btn_width
= buttonbat_get_button_width ();
3109 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
3111 g_free (bb
->labels
[lc_index
- 1].text
);
3112 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
3115 /* Find ButtonBar widget in the dialog */
3117 find_buttonbar (const Dlg_head
* h
)
3119 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
3123 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
3124 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
3126 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
3128 unsigned long command
= CK_Ignore_Key
;
3131 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
3133 if ((text
== NULL
) || (text
[0] == '\0'))
3134 set_label_text (bb
, idx
, "");
3136 set_label_text (bb
, idx
, text
);
3138 bb
->labels
[idx
- 1].command
= command
;
3139 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
3144 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
3146 bb
->visible
= visible
;
3150 buttonbar_redraw (WButtonBar
* bb
)
3153 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3157 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3159 WGroupbox
*g
= (WGroupbox
*) w
;
3167 return MSG_NOT_HANDLED
;
3170 tty_setcolor (COLOR_NORMAL
);
3171 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3172 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3174 tty_setcolor (COLOR_HOT_NORMAL
);
3175 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
3176 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
3177 tty_print_string (g
->title
);
3180 case WIDGET_DESTROY
:
3185 return default_proc (msg
, parm
);
3190 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3192 WGroupbox
*g
= g_new (WGroupbox
, 1);
3194 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3196 g
->widget
.options
&= ~W_WANT_CURSOR
;
3197 widget_want_hotkey (g
->widget
, 0);
3199 /* Strip existing spaces, add one space before and after the title */
3203 t
= g_strstrip (g_strdup (title
));
3204 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);