1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 * \brief Source: widgets
41 #include <sys/types.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
49 #include "lib/mcconfig.h" /* for history loading and saving */
50 #include "lib/vfs/mc-vfs/vfs.h"
51 #include "lib/fileloc.h"
52 #include "lib/strutil.h"
58 #include "cmddef.h" /* CK_ cmd name const */
59 #include "keybind.h" /* global_keymap_t */
60 #include "panel.h" /* current_panel */
62 const global_keymap_t
*input_map
;
65 widget_selectcolor (Widget
*w
, gboolean focused
, gboolean hotkey
)
67 Dlg_head
*h
= w
->parent
;
72 : DLG_HOT_NORMALC (h
))
79 parse_hotkey (const char *text
)
81 struct hotkey_t result
;
84 /* search for '&', that is not on the of text */
85 cp
= strchr (text
, '&');
86 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
);
97 result
.start
= g_strdup (text
);
105 release_hotkey (const struct hotkey_t hotkey
)
107 g_free (hotkey
.start
);
108 g_free (hotkey
.hotkey
);
113 hotkey_width (const struct hotkey_t hotkey
)
117 result
= str_term_width1 (hotkey
.start
);
118 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
119 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
124 draw_hotkey (Widget
*w
, const struct hotkey_t hotkey
, gboolean focused
)
126 widget_selectcolor (w
, focused
, FALSE
);
127 tty_print_string (hotkey
.start
);
129 if (hotkey
.hotkey
!= NULL
) {
130 widget_selectcolor (w
, focused
, TRUE
);
131 tty_print_string (hotkey
.hotkey
);
132 widget_selectcolor (w
, focused
, FALSE
);
135 if (hotkey
.end
!= NULL
)
136 tty_print_string (hotkey
.end
);
140 /* Default callback for widgets */
142 default_proc (widget_msg_t msg
, int parm
)
157 return MSG_NOT_HANDLED
;
161 static int button_event (Gpm_Event
*event
, void *);
166 button_callback (Widget
*w
, widget_msg_t msg
, int parm
)
168 WButton
*b
= (WButton
*) w
;
171 Dlg_head
*h
= b
->widget
.parent
;
176 * Don't let the default button steal Enter from the current
177 * button. This is a workaround for the flawed event model
178 * when hotkeys are sent to all widgets before the key is
179 * handled by the current widget.
181 if (parm
== '\n' && h
->current
== &b
->widget
) {
182 button_callback (w
, WIDGET_KEY
, ' ');
186 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
) {
187 button_callback (w
, WIDGET_KEY
, ' ');
191 if (b
->text
.hotkey
!= NULL
) {
192 if (g_ascii_tolower ((gchar
)b
->text
.hotkey
[0]) ==
193 g_ascii_tolower ((gchar
)parm
)) {
194 button_callback (w
, WIDGET_KEY
, ' ');
198 return MSG_NOT_HANDLED
;
201 if (parm
!= ' ' && parm
!= '\n')
202 return MSG_NOT_HANDLED
;
205 stop
= (*b
->callback
) (b
->action
);
206 if (!b
->callback
|| stop
) {
207 h
->ret_value
= b
->action
;
228 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
234 if (msg
== WIDGET_UNFOCUS
)
236 else if (msg
== WIDGET_FOCUS
)
239 widget_selectcolor (w
, b
->selected
, FALSE
);
240 widget_move (w
, 0, 0);
244 tty_print_string ("[< ");
247 tty_print_string ("[ ");
250 tty_print_string ("[");
257 draw_hotkey (w
, b
->text
, b
->selected
);
261 tty_print_string (" >]");
264 tty_print_string (" ]");
267 tty_print_string ("]");
273 release_hotkey (b
->text
);
277 return default_proc (msg
, parm
);
282 button_event (Gpm_Event
*event
, void *data
)
286 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
287 Dlg_head
*h
= b
->widget
.parent
;
288 dlg_select_widget (b
);
289 if (event
->type
& GPM_UP
){
290 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
291 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
299 button_get_len (const WButton
*b
)
301 int ret
= hotkey_width (b
->text
);
320 button_new (int y
, int x
, int action
, int flags
, const char *text
,
323 WButton
*b
= g_new (WButton
, 1);
327 b
->text
= parse_hotkey (text
);
329 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
),
330 button_callback
, button_event
);
333 b
->callback
= callback
;
334 widget_want_hotkey (b
->widget
, 1);
335 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
341 button_get_text (const WButton
*b
)
343 if (b
->text
.hotkey
!= NULL
)
344 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
,
345 b
->text
.end
, (char *) NULL
);
347 return g_strdup (b
->text
.start
);
351 button_set_text (WButton
*b
, const char *text
)
353 release_hotkey (b
->text
);
354 b
->text
= parse_hotkey (text
);
355 b
->widget
.cols
= button_get_len (b
);
356 dlg_redraw (b
->widget
.parent
);
360 /* Radio button widget */
361 static int radio_event (Gpm_Event
*event
, void *);
364 radio_callback (Widget
*w
, widget_msg_t msg
, int parm
)
366 WRadio
*r
= (WRadio
*) w
;
368 Dlg_head
*h
= r
->widget
.parent
;
373 int lp
= g_ascii_tolower ((gchar
)parm
);
375 for (i
= 0; i
< r
->count
; i
++) {
376 if (r
->texts
[i
].hotkey
!= NULL
) {
377 int c
= g_ascii_tolower ((gchar
)r
->texts
[i
].hotkey
[0]);
384 radio_callback (w
, WIDGET_KEY
, ' ');
389 return MSG_NOT_HANDLED
;
395 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
396 radio_callback (w
, WIDGET_FOCUS
, ' ');
405 return MSG_NOT_HANDLED
;
409 if (r
->count
- 1 > r
->pos
) {
414 return MSG_NOT_HANDLED
;
417 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
418 radio_callback (w
, WIDGET_FOCUS
, ' ');
419 widget_move (&r
->widget
, r
->pos
, 1);
425 for (i
= 0; i
< r
->count
; i
++) {
426 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
427 widget_selectcolor (w
, focused
, FALSE
);
428 widget_move (&r
->widget
, i
, 0);
429 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
430 draw_hotkey (w
, r
->texts
[i
], focused
);
435 for (i
= 0; i
< r
->count
; i
++) {
436 release_hotkey (r
->texts
[i
]);
442 return default_proc (msg
, parm
);
447 radio_event (Gpm_Event
*event
, void *data
)
452 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
453 Dlg_head
*h
= r
->widget
.parent
;
455 r
->pos
= event
->y
- 1;
456 dlg_select_widget (r
);
457 if (event
->type
& GPM_UP
){
458 radio_callback (w
, WIDGET_KEY
, ' ');
459 radio_callback (w
, WIDGET_FOCUS
, 0);
460 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
468 radio_new (int y
, int x
, int count
, const char **texts
)
470 WRadio
*result
= g_new (WRadio
, 1);
473 /* Compute the longest string */
474 result
->texts
= g_new (struct hotkey_t
, count
);
477 for (i
= 0; i
< count
; i
++){
478 result
->texts
[i
] = parse_hotkey (texts
[i
]);
479 m
= hotkey_width (result
->texts
[i
]);
484 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
488 result
->count
= count
;
489 widget_want_hotkey (result
->widget
, 1);
495 /* Checkbutton widget */
497 static int check_event (Gpm_Event
*event
, void *);
500 check_callback (Widget
*w
, widget_msg_t msg
, int parm
)
502 WCheck
*c
= (WCheck
*) w
;
503 Dlg_head
*h
= c
->widget
.parent
;
507 if (c
->text
.hotkey
!= NULL
) {
508 if (g_ascii_tolower ((gchar
)c
->text
.hotkey
[0]) ==
509 g_ascii_tolower ((gchar
)parm
)) {
511 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
515 return MSG_NOT_HANDLED
;
519 return MSG_NOT_HANDLED
;
521 c
->state
^= C_CHANGE
;
522 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
523 check_callback (w
, WIDGET_FOCUS
, ' ');
527 widget_move (&c
->widget
, 0, 1);
533 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
534 widget_move (&c
->widget
, 0, 0);
535 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
536 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
540 release_hotkey (c
->text
);
544 return default_proc (msg
, parm
);
549 check_event (Gpm_Event
*event
, void *data
)
554 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
555 Dlg_head
*h
= c
->widget
.parent
;
557 dlg_select_widget (c
);
558 if (event
->type
& GPM_UP
){
559 check_callback (w
, WIDGET_KEY
, ' ');
560 check_callback (w
, WIDGET_FOCUS
, 0);
561 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
569 check_new (int y
, int x
, int state
, const char *text
)
571 WCheck
*c
= g_new (WCheck
, 1);
573 c
->text
= parse_hotkey (text
);
575 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
),
576 check_callback
, check_event
);
577 c
->state
= state
? C_BOOL
: 0;
578 widget_want_hotkey (c
->widget
, 1);
584 save_text_to_clip_file (const char *text
)
589 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
590 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
591 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
597 mc_write (file
, (char *) text
, strlen (text
));
603 load_text_from_clip_file (char **text
)
608 gboolean first
= TRUE
;
610 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
611 f
= fopen (fname
, "r");
619 while (fgets (buf
, sizeof (buf
), f
)) {
624 if (buf
[len
- 1] == '\n')
629 *text
= g_strdup (buf
);
631 /* remove \n on EOL */
634 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
643 return (*text
!= NULL
);
647 panel_save_curent_file_to_clip_file (void)
651 if (current_panel
->marked
== 0)
652 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
655 gboolean first
= TRUE
;
658 for (i
= 0; i
< current_panel
->count
; i
++)
659 if (current_panel
->dir
.list
[i
].f
.marked
!= 0) { /* Skip the unmarked ones */
661 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
664 /* Add empty lines after the file */
667 tmp
= g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
674 res
= save_text_to_clip_file (flist
);
685 label_callback (Widget
*w
, widget_msg_t msg
, int parm
)
687 WLabel
*l
= (WLabel
*) w
;
688 Dlg_head
*h
= l
->widget
.parent
;
694 /* We don't want to get the focus */
696 return MSG_NOT_HANDLED
;
700 char *p
= l
->text
, *q
, c
= 0;
707 tty_setcolor (DEFAULT_COLOR
);
709 tty_setcolor (DLG_NORMALC (h
));
712 q
= strchr (p
, '\n');
718 widget_move (&l
->widget
, y
, 0);
719 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
735 return default_proc (msg
, parm
);
740 label_set_text (WLabel
*label
, const char *text
)
742 int newcols
= label
->widget
.cols
;
745 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
746 return; /* Flickering is not nice */
748 g_free (label
->text
);
751 label
->text
= g_strdup (text
);
752 if (label
->auto_adjust_cols
) {
753 str_msg_term_size (text
, &newlines
, &newcols
);
754 if (newcols
> label
->widget
.cols
)
755 label
->widget
.cols
= newcols
;
756 if (newlines
> label
->widget
.lines
)
757 label
->widget
.lines
= newlines
;
759 } else label
->text
= NULL
;
761 if (label
->widget
.parent
)
762 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
764 if (newcols
< label
->widget
.cols
)
765 label
->widget
.cols
= newcols
;
769 label_new (int y
, int x
, const char *text
)
776 str_msg_term_size (text
, &lines
, &cols
);
778 l
= g_new (WLabel
, 1);
779 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
780 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
781 l
->auto_adjust_cols
= 1;
783 widget_want_cursor (l
->widget
, 0);
788 /* Gauge widget (progress indicator) */
789 /* Currently width is hardcoded here for text mode */
793 gauge_callback (Widget
*w
, widget_msg_t msg
, int parm
)
795 WGauge
*g
= (WGauge
*) w
;
796 Dlg_head
*h
= g
->widget
.parent
;
798 if (msg
== WIDGET_INIT
)
801 /* We don't want to get the focus */
802 if (msg
== WIDGET_FOCUS
)
803 return MSG_NOT_HANDLED
;
805 if (msg
== WIDGET_DRAW
){
806 widget_move (&g
->widget
, 0, 0);
807 tty_setcolor (DLG_NORMALC (h
));
809 tty_printf ("%*s", gauge_len
, "");
811 int percentage
, columns
;
812 long total
= g
->max
, done
= g
->current
;
814 if (total
<= 0 || done
< 0) {
820 while (total
> 65535) {
824 percentage
= (200 * done
/ total
+ 1) / 2;
825 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
826 tty_print_char ('[');
827 tty_setcolor (GAUGE_COLOR
);
828 tty_printf ("%*s", (int) columns
, "");
829 tty_setcolor (DLG_NORMALC (h
));
830 tty_printf ("%*s] %3d%%", (int)(gauge_len
- 7 - columns
), "", (int) percentage
);
835 return default_proc (msg
, parm
);
839 gauge_set_value (WGauge
*g
, int max
, int current
)
841 if (g
->current
== current
&& g
->max
== max
)
842 return; /* Do not flicker */
844 max
= 1; /* I do not like division by zero :) */
846 g
->current
= current
;
848 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
852 gauge_show (WGauge
*g
, int shown
)
854 if (g
->shown
== shown
)
857 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
861 gauge_new (int y
, int x
, int shown
, int max
, int current
)
863 WGauge
*g
= g_new (WGauge
, 1);
865 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
868 max
= 1; /* I do not like division by zero :) */
870 g
->current
= current
;
871 widget_want_cursor (g
->widget
, 0);
878 /* {{{ history button */
880 #define LARGE_HISTORY_BUTTON 1
882 #ifdef LARGE_HISTORY_BUTTON
883 # define HISTORY_BUTTON_WIDTH 3
885 # define HISTORY_BUTTON_WIDTH 1
888 #define should_show_history_button(in) \
889 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
891 static void draw_history_button (WInput
* in
)
894 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
895 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
896 #ifdef LARGE_HISTORY_BUTTON
899 h
= in
->widget
.parent
;
900 tty_setcolor (NORMAL_COLOR
);
901 tty_print_string ("[ ]");
902 /* Too distracting: tty_setcolor (MARKED_COLOR); */
903 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
907 tty_setcolor (MARKED_COLOR
);
912 /* }}} history button */
915 /* Input widgets now have a global kill ring */
916 /* Pointer to killed data */
917 static char *kill_buffer
= 0;
920 update_input (WInput
*in
, int clear_first
)
924 int buf_len
= str_length (in
->buffer
);
928 if (should_show_history_button (in
))
929 has_history
= HISTORY_BUTTON_WIDTH
;
931 if (in
->disable_update
)
934 pw
= str_term_width2 (in
->buffer
, in
->point
);
936 /* Make the point visible */
937 if ((pw
< in
->term_first_shown
) ||
938 (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
)) {
940 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
941 if (in
->term_first_shown
< 0)
942 in
->term_first_shown
= 0;
945 /* Adjust the mark */
946 if (in
->mark
> buf_len
)
950 draw_history_button (in
);
952 tty_setcolor (in
->color
);
954 widget_move (&in
->widget
, 0, 0);
956 if (!in
->is_password
) {
957 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
958 in
->field_width
- has_history
));
961 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++){
963 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
965 if (cp
[0] != '\0') str_cnext_char (&cp
);
974 winput_set_origin (WInput
*in
, int x
, int field_width
)
977 in
->field_width
= in
->widget
.cols
= field_width
;
978 update_input (in
, 0);
981 /* {{{ history saving and loading */
983 int num_history_items_recorded
= 60;
986 This loads and saves the history of an input line to and from the
987 widget. It is called with the widgets history name on creation of the
988 widget, and returns the GList list. It stores histories in the file
989 ~/.mc/history in using the profile code.
991 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
992 function) then input_new assigns the default text to be the last text
993 entered, or "" if not found.
997 history_get (const char *input_name
)
1004 size_t keys_num
= 0;
1007 if (num_history_items_recorded
== 0) /* this is how to disable */
1009 if ((input_name
== NULL
) || (*input_name
== '\0'))
1012 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1013 cfg
= mc_config_init (profile
);
1015 /* get number of keys */
1016 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1019 for (i
= 0; i
< keys_num
; i
++) {
1021 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long)i
);
1022 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1024 if (this_entry
!= NULL
)
1025 hist
= list_append_unique (hist
, this_entry
);
1028 mc_config_deinit (cfg
);
1031 /* return pointer to the last entry in the list */
1032 return g_list_last (hist
);
1036 history_put (const char *input_name
, GList
*h
)
1042 if (num_history_items_recorded
== 0) /* this is how to disable */
1044 if ((input_name
== NULL
) || (*input_name
== '\0'))
1049 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1051 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1055 /* Make sure the history is only readable by the user */
1056 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
) {
1061 /* go to end of list */
1062 h
= g_list_last (h
);
1064 /* go back 60 places */
1065 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1066 h
= g_list_previous (h
);
1068 cfg
= mc_config_init (profile
);
1070 if (input_name
!= NULL
)
1071 mc_config_del_group (cfg
, input_name
);
1073 /* dump history into profile */
1074 for (i
= 0; h
!= NULL
; h
= g_list_next (h
)) {
1075 char *text
= (char *) h
->data
;
1077 /* We shouldn't have null entries, but let's be sure */
1080 g_snprintf (key
, sizeof (key
), "%d", i
++);
1081 mc_config_set_string (cfg
, input_name
, key
, text
);
1085 mc_config_save_file (cfg
, NULL
);
1086 mc_config_deinit(cfg
);
1090 /* }}} history saving and loading */
1093 /* {{{ history display */
1098 return _(" History ");
1108 dlg_hist_reposition (Dlg_head
*dlg_head
)
1110 dlg_hist_data
*data
;
1111 int x
= 0, y
, he
, wi
;
1114 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1115 return MSG_NOT_HANDLED
;
1117 data
= (dlg_hist_data
*) dlg_head
->data
;
1119 y
= data
->widget
->y
;
1120 he
= data
->count
+ 2;
1122 if (he
<= y
|| y
> (LINES
- 6)) {
1123 he
= min (he
, y
- 1);
1127 he
= min (he
, LINES
- y
);
1130 if (data
->widget
->x
> 2)
1131 x
= data
->widget
->x
- 2;
1133 wi
= data
->maxlen
+ 4;
1135 if ((wi
+ x
) > COLS
) {
1136 wi
= min (wi
, COLS
);
1140 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1146 dlg_hist_callback (Dlg_head
*h
, Widget
*sender
,
1147 dlg_msg_t msg
, int parm
, void *data
)
1151 return dlg_hist_reposition (h
);
1154 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1159 show_hist (GList
**history
, Widget
*widget
)
1161 GList
*z
, *hlist
= NULL
, *hi
;
1162 size_t maxlen
, i
, count
= 0;
1164 Dlg_head
*query_dlg
;
1165 WListbox
*query_list
;
1166 dlg_hist_data hist_data
;
1168 if (*history
== NULL
)
1171 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1173 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
)) {
1176 i
= str_term_width1 ((char *) z
->data
);
1177 maxlen
= max (maxlen
, i
);
1180 entry
= g_new0 (WLEntry
, 1);
1181 /* history is being reverted here */
1182 entry
->text
= g_strdup ((char *) z
->data
);
1183 hlist
= g_list_prepend (hlist
, entry
);
1186 hist_data
.widget
= widget
;
1187 hist_data
.count
= count
;
1188 hist_data
.maxlen
= maxlen
;
1191 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1192 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1193 query_dlg
->data
= &hist_data
;
1195 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1197 /* this call makes list stick to all sides of dialog, effectively make
1198 it be resized with dialog */
1199 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1201 /* to avoid diplicating of (calculating sizes in two places)
1202 code, call dlg_hist_callback function here, to set dialog and
1204 The main idea - create 4x4 dialog and add 2x2 list in
1205 center of it, and let dialog function resize it to needed
1207 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1209 if (query_dlg
->y
< widget
->y
) {
1210 /* draw list entries from bottom upto top */
1211 listbox_set_list (query_list
, hlist
);
1212 listbox_select_last (query_list
);
1214 /* draw list entries from top downto bottom */
1215 /* revert history direction */
1216 hlist
= g_list_reverse (hlist
);
1217 listbox_set_list (query_list
, hlist
);
1220 if (run_dlg (query_dlg
) != B_CANCEL
) {
1223 listbox_get_current (query_list
, &q
, NULL
);
1228 /* get modified history from dialog */
1230 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
)) {
1233 entry
= (WLEntry
*) hi
->data
;
1234 /* history is being reverted here again */
1235 z
= g_list_prepend (z
, entry
->text
);
1239 destroy_dlg (query_dlg
);
1241 /* restore history direction */
1242 if (query_dlg
->y
< widget
->y
)
1243 z
= g_list_reverse (z
);
1245 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1246 g_list_free (*history
);
1247 *history
= g_list_last (z
);
1253 do_show_hist (WInput
*in
)
1257 r
= show_hist (&in
->history
, &in
->widget
);
1259 assign_text (in
, r
);
1264 /* }}} history display */
1267 input_destroy (WInput
*in
)
1270 fprintf (stderr
, "Internal error: null Input *\n");
1277 if (!in
->is_password
) /* don't save passwords ;-) */
1278 history_put (in
->history_name
, in
->history
);
1280 in
->history
= g_list_first (in
->history
);
1281 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1282 g_list_free (in
->history
);
1285 g_free (in
->buffer
);
1286 free_completions (in
);
1287 g_free (in
->history_name
);
1291 input_disable_update (WInput
*in
)
1293 in
->disable_update
++;
1297 input_enable_update (WInput
*in
)
1299 in
->disable_update
--;
1300 update_input (in
, 0);
1305 push_history (WInput
*in
, const char *text
)
1307 /* input widget where urls with passwords are entered without any
1309 const char *password_input_fields
[] = {
1310 N_(" Link to a remote machine "),
1311 N_(" FTP to machine "),
1312 N_(" SMB link to machine ")
1314 const size_t ELEMENTS
= (sizeof (password_input_fields
) /
1315 sizeof (password_input_fields
[0]));
1325 for (i
= 0; i
< ELEMENTS
; i
++)
1326 password_input_fields
[i
] = _(password_input_fields
[i
]);
1329 t
= g_strstrip (g_strdup (text
));
1332 t
= g_strdup (empty
? "" : text
);
1334 if (in
->history_name
!= NULL
) {
1335 const char *p
= in
->history_name
+ 3;
1337 for (i
= 0; i
< ELEMENTS
; i
++)
1338 if (strcmp (p
, password_input_fields
[i
]) == 0)
1341 strip_password (t
, i
>= ELEMENTS
);
1344 in
->history
= list_append_unique (in
->history
, t
);
1348 /* Cleans the input line and adds the current text to the history */
1350 new_input (WInput
*in
)
1352 push_history (in
, in
->buffer
);
1354 in
->buffer
[0] = '\0';
1358 free_completions (in
);
1359 update_input (in
, 0);
1363 move_buffer_backward (WInput
*in
, int start
, int end
)
1366 int str_len
= str_length (in
->buffer
);
1367 if (start
>= str_len
|| end
> str_len
+ 1) return;
1369 pos
= str_offset_to_pos (in
->buffer
, start
);
1370 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1372 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1373 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1377 insert_char (WInput
*in
, int c_code
)
1383 return MSG_NOT_HANDLED
;
1385 if (in
->charpoint
>= MB_LEN_MAX
)
1388 in
->charbuf
[in
->charpoint
] = c_code
;
1391 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1394 in
->charpoint
= 0; /* broken multibyte char, skip */
1399 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
){
1400 /* Expand the buffer */
1401 size_t new_length
= in
->current_max_size
+
1402 in
->field_width
+ in
->charpoint
;
1403 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1406 in
->current_max_size
= new_length
;
1410 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
) {
1411 /* bytes from begin */
1412 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1414 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1416 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1417 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] =
1418 in
->buffer
[ins_point
+ i
- 1];
1420 memcpy(in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1429 beginning_of_line (WInput
*in
)
1436 end_of_line (WInput
*in
)
1438 in
->point
= str_length (in
->buffer
);
1443 backward_char (WInput
*in
)
1445 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1447 if (in
->point
> 0) {
1448 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1454 forward_char (WInput
*in
)
1456 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1457 if (act
[0] != '\0') {
1458 in
->point
+= str_cnext_noncomb_char (&act
);
1464 forward_word (WInput
* in
)
1466 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1468 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
))) {
1469 str_cnext_char (&p
);
1472 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
)) {
1473 str_cnext_char (&p
);
1479 backward_word (WInput
*in
)
1485 p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1486 (p
!= in
->buffer
) && (p
[0] == '\0');
1487 str_cprev_char (&p
), in
->point
--
1490 while (p
!= in
->buffer
) {
1492 str_cprev_char (&p
);
1493 if (!str_isspace (p
) && !str_ispunct (p
)) {
1499 while (p
!= in
->buffer
) {
1500 str_cprev_char (&p
);
1501 if (str_isspace (p
) || str_ispunct (p
))
1509 key_left (WInput
*in
)
1515 key_ctrl_left (WInput
*in
)
1521 key_right (WInput
*in
)
1527 key_ctrl_right (WInput
*in
)
1532 backward_delete (WInput
*in
)
1534 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1540 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1541 move_buffer_backward(in
, start
, in
->point
);
1548 delete_char (WInput
*in
)
1550 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1551 int end
= in
->point
;
1553 end
+= str_cnext_noncomb_char (&act
);
1555 move_buffer_backward(in
, in
->point
, end
);
1561 copy_region (WInput
*in
, int x_first
, int x_last
)
1563 int first
= min (x_first
, x_last
);
1564 int last
= max (x_first
, x_last
);
1566 if (last
== first
) {
1567 /* Copy selected files to clipboard */
1568 panel_save_curent_file_to_clip_file ();
1572 g_free (kill_buffer
);
1574 first
= str_offset_to_pos (in
->buffer
, first
);
1575 last
= str_offset_to_pos (in
->buffer
, last
);
1577 kill_buffer
= g_strndup(in
->buffer
+ first
, last
- first
);
1578 save_text_to_clip_file (kill_buffer
);
1582 delete_region (WInput
*in
, int x_first
, int x_last
)
1584 int first
= min (x_first
, x_last
);
1585 int last
= max (x_first
, x_last
);
1589 if (in
->mark
> first
)
1591 last
= str_offset_to_pos (in
->buffer
, last
);
1592 first
= str_offset_to_pos (in
->buffer
, first
);
1593 len
= strlen (&in
->buffer
[last
]) + 1;
1594 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1600 kill_word (WInput
*in
)
1602 int old_point
= in
->point
;
1606 new_point
= in
->point
;
1607 in
->point
= old_point
;
1609 copy_region (in
, old_point
, new_point
);
1610 delete_region (in
, old_point
, new_point
);
1617 back_kill_word (WInput
*in
)
1619 int old_point
= in
->point
;
1623 new_point
= in
->point
;
1624 in
->point
= old_point
;
1626 copy_region (in
, old_point
, new_point
);
1627 delete_region (in
, old_point
, new_point
);
1632 set_mark (WInput
*in
)
1634 in
->mark
= in
->point
;
1638 kill_save (WInput
*in
)
1640 copy_region (in
, in
->mark
, in
->point
);
1644 kill_region (WInput
*in
)
1647 delete_region (in
, in
->point
, in
->mark
);
1651 clear_region (WInput
*in
)
1653 delete_region (in
, in
->point
, in
->mark
);
1664 for (p
= kill_buffer
; *p
; p
++)
1665 insert_char (in
, *p
);
1670 kill_line (WInput
*in
)
1672 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1673 g_free (kill_buffer
);
1674 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1675 in
->buffer
[chp
] = '\0';
1680 ins_from_clip (WInput
*in
)
1684 if (load_text_from_clip_file (&p
)) {
1687 for (pp
= p
; *pp
!= '\0'; pp
++)
1688 insert_char (in
, *pp
);
1695 assign_text (WInput
*in
, const char *text
)
1697 free_completions (in
);
1698 g_free (in
->buffer
);
1699 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1700 in
->current_max_size
= strlen (in
->buffer
) + 1;
1701 in
->point
= str_length (in
->buffer
);
1708 hist_prev (WInput
*in
)
1716 push_history (in
, in
->buffer
);
1718 prev
= g_list_previous (in
->history
);
1721 assign_text (in
, (char *) prev
->data
);
1727 hist_next (WInput
*in
)
1729 if (in
->need_push
) {
1730 push_history (in
, in
->buffer
);
1731 assign_text (in
, "");
1738 if (!in
->history
->next
) {
1739 assign_text (in
, "");
1743 in
->history
= g_list_next (in
->history
);
1744 assign_text (in
, (char *) in
->history
->data
);
1749 port_region_marked_for_delete (WInput
*in
)
1751 in
->buffer
[0] = '\0';
1758 input_execute_cmd (WInput
*in
, unsigned long command
)
1760 cb_ret_t res
= MSG_HANDLED
;
1764 beginning_of_line (in
);
1769 case CK_InputMoveLeft
:
1772 case CK_InputWordLeft
:
1775 case CK_InputMoveRight
:
1778 case CK_InputWordRight
:
1779 key_ctrl_right (in
);
1781 case CK_InputBackwardChar
:
1784 case CK_InputBackwardWord
:
1787 case CK_InputForwardChar
:
1790 case CK_InputForwardWord
:
1793 case CK_InputBackwardDelete
:
1794 backward_delete (in
);
1796 case CK_InputDeleteChar
:
1799 case CK_InputKillWord
:
1802 case CK_InputBackwardKillWord
:
1803 back_kill_word (in
);
1805 case CK_InputSetMark
:
1808 case CK_InputKillRegion
:
1811 case CK_InputClearLine
:
1814 case CK_InputKillSave
:
1823 case CK_InputKillLine
:
1826 case CK_InputHistoryPrev
:
1829 case CK_InputHistoryNext
:
1832 case CK_InputHistoryShow
:
1835 case CK_InputComplete
:
1839 res
= MSG_NOT_HANDLED
;
1845 /* This function is a test for a special input key used in complete.c */
1846 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1847 and 2 if it is a complete key */
1849 is_in_input_map (WInput
*in
, int key
)
1852 for (i
= 0; input_map
[i
].key
!= 0; i
++)
1853 if (key
== input_map
[i
].key
) {
1854 input_execute_cmd (in
, input_map
[i
].command
);
1855 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
1861 handle_char (WInput
*in
, int key
)
1866 v
= MSG_NOT_HANDLED
;
1869 free_completions (in
);
1870 v
= insert_char (in
, key
);
1871 update_input (in
, 1);
1875 for (i
= 0; input_map
[i
].key
; i
++) {
1876 if (key
== input_map
[i
].key
) {
1877 if (input_map
[i
].command
!= CK_InputComplete
)
1878 free_completions (in
);
1879 input_execute_cmd (in
, input_map
[i
].command
);
1880 update_input (in
, 1);
1885 if (input_map
[i
].command
== 0) {
1887 return MSG_NOT_HANDLED
;
1889 port_region_marked_for_delete (in
);
1890 free_completions (in
);
1891 v
= insert_char (in
, key
);
1893 update_input (in
, 1);
1897 /* Inserts text in input line */
1899 stuff (WInput
*in
, const char *text
, int insert_extra_space
)
1901 input_disable_update (in
);
1902 while (*text
!= '\0')
1903 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
1904 if (insert_extra_space
)
1905 handle_char (in
, ' ');
1906 input_enable_update (in
);
1907 update_input (in
, 1);
1911 input_set_point (WInput
*in
, int pos
)
1913 int max_pos
= str_length (in
->buffer
);
1917 if (pos
!= in
->point
)
1918 free_completions (in
);
1921 update_input (in
, 1);
1925 input_callback (Widget
*w
, widget_msg_t msg
, int parm
)
1927 WInput
*in
= (WInput
*) w
;
1932 if (parm
== XCTRL ('q')) {
1934 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
1939 /* Keys we want others to handle */
1940 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
1941 || parm
== KEY_F (10) || parm
== '\n')
1942 return MSG_NOT_HANDLED
;
1944 /* When pasting multiline text, insert literal Enter */
1945 if ((parm
& ~KEY_M_MASK
) == '\n') {
1947 v
= handle_char (in
, '\n');
1952 return handle_char (in
, parm
);
1954 case WIDGET_COMMAND
:
1955 return input_execute_cmd (in
, parm
);
1958 case WIDGET_UNFOCUS
:
1960 update_input (in
, 0);
1964 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
1965 - in
->term_first_shown
);
1968 case WIDGET_DESTROY
:
1973 return default_proc (msg
, parm
);
1978 input_event (Gpm_Event
* event
, void *data
)
1982 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
1983 dlg_select_widget (in
);
1985 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
1986 && should_show_history_button (in
)) {
1989 in
->point
= str_length (in
->buffer
);
1990 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
1991 in
->point
= str_column_to_pos (in
->buffer
, event
->x
1992 + in
->term_first_shown
- 1);
1994 update_input (in
, 1);
2000 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
2001 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2003 WInput
*in
= g_new (WInput
, 1);
2004 size_t initial_buffer_len
;
2006 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2009 in
->history_name
= NULL
;
2011 if ((histname
!= NULL
) && (*histname
!= '\0')) {
2012 in
->history_name
= g_strdup (histname
);
2013 in
->history
= history_get (histname
);
2016 if (def_text
== NULL
)
2018 else if (def_text
== INPUT_LAST_TEXT
) {
2019 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2020 def_text
= (char *) in
->history
->data
;
2025 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2026 in
->widget
.options
|= W_IS_INPUT
;
2027 in
->completions
= NULL
;
2028 in
->completion_flags
= completion_flags
;
2029 in
->current_max_size
= initial_buffer_len
;
2030 in
->buffer
= g_new (char, initial_buffer_len
);
2032 in
->field_width
= width
;
2034 in
->term_first_shown
= 0;
2035 in
->disable_update
= 0;
2038 in
->is_password
= 0;
2040 strcpy (in
->buffer
, def_text
);
2041 in
->point
= str_length (in
->buffer
);
2048 /* Listbox widget */
2050 /* Should draw the scrollbar, but currently draws only
2051 * indications that there is more information
2055 listbox_entry_free (void *data
)
2063 listbox_drawscroll (WListbox
*l
)
2065 const int max_line
= l
->widget
.lines
- 1;
2069 /* Are we at the top? */
2070 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2072 tty_print_one_vline ();
2074 tty_print_char ('^');
2076 /* Are we at the bottom? */
2077 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2078 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2079 tty_print_one_vline ();
2081 tty_print_char ('v');
2083 /* Now draw the nice relative pointer */
2085 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2087 for (i
= 1; i
< max_line
; i
++) {
2088 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2090 tty_print_one_vline ();
2092 tty_print_char ('*');
2097 listbox_draw (WListbox
*l
, gboolean focused
)
2099 const Dlg_head
*h
= l
->widget
.parent
;
2100 const int normalc
= DLG_NORMALC (h
);
2101 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2108 le
= g_list_nth (l
->list
, l
->top
);
2109 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2110 pos
= (le
== NULL
) ? 0 : l
->top
;
2112 for (i
= 0; i
< l
->widget
.lines
; i
++) {
2115 /* Display the entry */
2116 if (pos
== l
->pos
&& sel_line
== -1) {
2118 tty_setcolor (selc
);
2120 tty_setcolor (normalc
);
2122 widget_move (&l
->widget
, i
, 1);
2124 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2127 WLEntry
*e
= (WLEntry
*) le
->data
;
2129 le
= g_list_next (le
);
2133 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2136 l
->cursor_y
= sel_line
;
2138 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
)) {
2139 tty_setcolor (normalc
);
2140 listbox_drawscroll (l
);
2145 listbox_check_hotkey (WListbox
*l
, int key
)
2150 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
)) {
2151 WLEntry
*e
= (WLEntry
*) le
->data
;
2153 if (e
->hotkey
== key
)
2160 /* Selects the last entry and scrolls the list to the bottom */
2162 listbox_select_last (WListbox
*l
)
2164 l
->pos
= l
->count
- 1;
2165 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2168 /* Selects the first entry and scrolls the list to the top */
2170 listbox_select_first (WListbox
*l
)
2172 l
->pos
= l
->top
= 0;
2176 listbox_set_list (WListbox
*l
, GList
*list
)
2178 listbox_remove_list (l
);
2182 l
->top
= l
->pos
= 0;
2183 l
->count
= g_list_length (list
);
2188 listbox_remove_list (WListbox
*l
)
2190 if ((l
!= NULL
) && (l
->count
!= 0)) {
2191 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2192 g_list_free (l
->list
);
2194 l
->count
= l
->pos
= l
->top
= 0;
2199 listbox_remove_current (WListbox
*l
)
2201 if ((l
!= NULL
) && (l
->count
!= 0)) {
2204 current
= g_list_nth (l
->list
, l
->pos
);
2205 l
->list
= g_list_remove_link (l
->list
, current
);
2206 listbox_entry_free ((WLEntry
*) current
->data
);
2207 g_list_free_1 (current
);
2211 l
->top
= l
->pos
= 0;
2212 else if (l
->pos
>= l
->count
)
2213 l
->pos
= l
->count
- 1;
2218 listbox_select_entry (WListbox
*l
, int dest
)
2222 gboolean top_seen
= FALSE
;
2228 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
)) {
2237 if (l
->pos
- l
->top
>= l
->widget
.lines
)
2238 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2243 /* If we are unable to find it, set decent values */
2244 l
->pos
= l
->top
= 0;
2247 /* Selects from base the pos element */
2249 listbox_select_pos (WListbox
*l
, int base
, int pos
)
2251 int last
= l
->count
- 1;
2261 listbox_fwd (WListbox
*l
)
2263 if (l
->pos
+ 1 >= l
->count
)
2264 listbox_select_first (l
);
2266 listbox_select_entry (l
, l
->pos
+ 1);
2270 listbox_back (WListbox
*l
)
2273 listbox_select_last (l
);
2275 listbox_select_entry (l
, l
->pos
- 1);
2278 /* Return MSG_HANDLED if we want a redraw */
2280 listbox_key (WListbox
*l
, int key
)
2284 cb_ret_t j
= MSG_NOT_HANDLED
;
2286 /* focus on listbox item N by '0'..'9' keys */
2287 if (key
>= '0' && key
<= '9') {
2288 int oldpos
= l
->pos
;
2289 listbox_select_entry (l
, key
- '0');
2291 /* need scroll to item? */
2292 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2298 if (l
->list
== NULL
)
2299 return MSG_NOT_HANDLED
;
2305 listbox_select_first (l
);
2311 listbox_select_last (l
);
2326 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++) {
2334 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++) {
2342 gboolean is_last
= (l
->pos
+ 1 == l
->count
);
2344 listbox_remove_current (l
);
2345 if (is_last
&& (l
->top
> 0))
2354 return MSG_NOT_HANDLED
;
2358 listbox_destroy (WListbox
*l
)
2360 /* don't delete list in modifable listbox */
2362 listbox_remove_list (l
);
2366 listbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2368 WListbox
*l
= (WListbox
*) w
;
2369 Dlg_head
*h
= l
->widget
.parent
;
2380 pos
= listbox_check_hotkey (l
, parm
);
2382 return MSG_NOT_HANDLED
;
2384 listbox_select_entry (l
, pos
);
2385 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2387 if (l
->cback
!= NULL
)
2388 action
= l
->cback (l
);
2390 action
= LISTBOX_DONE
;
2392 if (action
== LISTBOX_DONE
) {
2393 h
->ret_value
= B_ENTER
;
2401 ret_code
= listbox_key (l
, parm
);
2402 if (ret_code
!= MSG_NOT_HANDLED
) {
2403 listbox_draw (l
, TRUE
);
2404 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2409 widget_move (&l
->widget
, l
->cursor_y
, 0);
2410 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2414 case WIDGET_UNFOCUS
:
2416 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2419 case WIDGET_DESTROY
:
2420 listbox_destroy (l
);
2423 case WIDGET_RESIZED
:
2427 return default_proc (msg
, parm
);
2432 listbox_event (Gpm_Event
*event
, void *data
)
2437 Dlg_head
*h
= l
->widget
.parent
;
2440 if (event
->type
& GPM_DOWN
)
2441 dlg_select_widget (l
);
2443 if (l
->list
== NULL
)
2446 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2447 int ret
= MOU_REPEAT
;
2449 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2453 for (i
= -event
->y
; i
>= 0; i
--)
2455 else if (event
->y
> l
->widget
.lines
)
2456 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2458 else if (event
->buttons
& GPM_B_UP
) {
2461 } else if (event
->buttons
& GPM_B_DOWN
) {
2465 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2467 /* We need to refresh ourselves since the dialog manager doesn't */
2468 /* know about this event */
2469 listbox_draw (l
, TRUE
);
2474 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
)) {
2477 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2478 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2481 dlg_select_widget (l
);
2482 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2484 if (l
->cback
!= NULL
)
2485 action
= l
->cback (l
);
2487 action
= LISTBOX_DONE
;
2489 if (action
== LISTBOX_DONE
) {
2490 h
->ret_value
= B_ENTER
;
2499 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2501 WListbox
*l
= g_new (WListbox
, 1);
2506 init_widget (&l
->widget
, y
, x
, height
, width
,
2507 listbox_callback
, listbox_event
);
2510 l
->top
= l
->pos
= 0;
2512 l
->deletable
= deletable
;
2513 l
->cback
= callback
;
2514 l
->allow_duplicates
= TRUE
;
2515 l
->scrollbar
= !tty_is_slow ();
2516 widget_want_hotkey (l
->widget
, 1);
2522 listbox_entry_cmp (const void *a
, const void *b
)
2524 const WLEntry
*ea
= (const WLEntry
*) a
;
2525 const WLEntry
*eb
= (const WLEntry
*) b
;
2527 return strcmp (ea
->text
, eb
->text
);
2530 /* Listbox item adding function */
2532 listbox_append_item (WListbox
*l
, WLEntry
*e
, listbox_append_t pos
)
2535 case LISTBOX_APPEND_AT_END
:
2536 l
->list
= g_list_append (l
->list
, e
);
2539 case LISTBOX_APPEND_BEFORE
:
2540 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2545 case LISTBOX_APPEND_AFTER
:
2546 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2549 case LISTBOX_APPEND_SORTED
:
2550 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2561 listbox_add_item (WListbox
*l
, listbox_append_t pos
, int hotkey
,
2562 const char *text
, void *data
)
2569 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2572 entry
= g_new (WLEntry
, 1);
2573 entry
->text
= g_strdup (text
);
2575 entry
->hotkey
= hotkey
;
2577 listbox_append_item (l
, entry
, pos
);
2583 listbox_search_text (WListbox
*l
, const char *text
)
2589 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
)) {
2590 WLEntry
*e
= (WLEntry
*) le
->data
;
2592 if (strcmp (e
->text
, text
) == 0)
2600 /* Returns the current string text as well as the associated extra data */
2602 listbox_get_current (WListbox
*l
, char **string
, void **extra
)
2608 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
2613 *string
= ok
? e
->text
: NULL
;
2616 *extra
= ok
? e
->data
: NULL
;
2620 /* ButtonBar widget */
2622 /* returns TRUE if a function has been called, FALSE otherwise. */
2624 buttonbar_call (WButtonBar
*bb
, int i
)
2626 cb_ret_t ret
= MSG_NOT_HANDLED
;
2629 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2630 (Widget
*) bb
, DLG_ACTION
,
2631 bb
->labels
[i
].command
,
2632 bb
->labels
[i
].receiver
);
2636 /* calculate width of one button, width is never lesser than 7 */
2638 buttonbat_get_button_width (void)
2640 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2641 return (result
>= 7) ? result
: 7;
2645 buttonbar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2647 WButtonBar
*bb
= (WButtonBar
*) w
;
2653 return MSG_NOT_HANDLED
;
2656 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2657 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2659 return MSG_NOT_HANDLED
;
2664 int count_free_positions
;
2666 widget_move (&bb
->widget
, 0, 0);
2667 tty_setcolor (DEFAULT_COLOR
);
2668 bb
->btn_width
= buttonbat_get_button_width ();
2669 tty_printf ("%-*s", bb
->widget
.cols
, "");
2670 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2672 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++) {
2673 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2674 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2675 tty_printf ("%2d", i
+ 1);
2676 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2677 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2678 tty_print_string (str_fit_to_term (
2680 bb
->btn_width
- 2 + (int)(offset
< count_free_positions
),
2683 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2689 case WIDGET_DESTROY
:
2690 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2691 g_free (bb
->labels
[i
].text
);
2695 return default_proc (msg
, parm
);
2700 buttonbar_event (Gpm_Event
*event
, void *data
)
2702 WButtonBar
*bb
= data
;
2705 if (!(event
->type
& GPM_UP
))
2709 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2710 if (button
< BUTTONBAR_LABELS_NUM
)
2711 buttonbar_call (bb
, button
);
2716 buttonbar_new (gboolean visible
)
2720 bb
= g_new0 (WButtonBar
, 1);
2722 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
,
2723 buttonbar_callback
, buttonbar_event
);
2724 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2725 bb
->visible
= visible
;
2726 widget_want_hotkey (bb
->widget
, 1);
2727 widget_want_cursor (bb
->widget
, 0);
2728 bb
->btn_width
= buttonbat_get_button_width ();
2734 set_label_text (WButtonBar
*bb
, int lc_index
, const char *text
)
2736 g_free (bb
->labels
[lc_index
- 1].text
);
2737 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2740 /* Find ButtonBar widget in the dialog */
2742 find_buttonbar (const Dlg_head
*h
)
2744 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2748 buttonbar_set_label (WButtonBar
*bb
, int idx
, const char *text
,
2749 const struct global_keymap_t
*keymap
, const Widget
*receiver
)
2751 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
)) {
2752 unsigned long command
= CK_Ignore_Key
;
2755 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2757 if ((text
== NULL
) || (text
[0] == '\0'))
2758 set_label_text (bb
, idx
, "");
2760 set_label_text (bb
, idx
, text
);
2762 bb
->labels
[idx
- 1].command
= command
;
2763 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
2768 buttonbar_set_visible (WButtonBar
*bb
, gboolean visible
)
2770 bb
->visible
= visible
;
2774 buttonbar_redraw (WButtonBar
*bb
)
2777 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
2781 groupbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2783 WGroupbox
*g
= (WGroupbox
*) w
;
2790 return MSG_NOT_HANDLED
;
2793 tty_setcolor (COLOR_NORMAL
);
2794 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2795 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
,
2798 tty_setcolor (COLOR_HOT_NORMAL
);
2799 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2800 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
2801 tty_print_string (g
->title
);
2804 case WIDGET_DESTROY
:
2809 return default_proc (msg
, parm
);
2814 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
2816 WGroupbox
*g
= g_new (WGroupbox
, 1);
2818 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
2820 g
->widget
.options
&= ~W_WANT_CURSOR
;
2821 widget_want_hotkey (g
->widget
, 0);
2823 /* Strip existing spaces, add one space before and after the title */
2826 t
= g_strstrip (g_strdup (title
));
2827 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);