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>
45 #include "../src/tty/tty.h"
46 #include "../src/skin/skin.h"
47 #include "../src/tty/mouse.h"
48 #include "../src/tty/key.h" /* XCTRL and ALT macros */
50 #include "../src/mcconfig/mcconfig.h" /* for history loading and saving */
57 #include "cmddef.h" /* CK_ cmd name const */
58 #include "keybind.h" /* global_key_map_t */
61 const global_key_map_t
*input_map
;
64 widget_selectcolor (Widget
*w
, gboolean focused
, gboolean hotkey
)
66 Dlg_head
*h
= w
->parent
;
71 : DLG_HOT_NORMALC (h
))
78 parse_hotkey (const char *text
)
80 struct hotkey_t result
;
85 /* search for '&', that is not on the of text */
86 cp
= strchr (text
, '&');
87 if (cp
!= NULL
&& cp
[1] != '\0') {
88 result
.start
= g_strndup (text
, cp
- text
);
92 p
= str_cget_next_char (cp
);
93 hotkey
= g_strndup (cp
, p
- cp
);
94 res
= g_utf8_get_char_validated (hotkey
, -1);
97 result
.hotkey
= (int) *hotkey
;
104 result
.end
= g_strdup (cp
);
106 result
.start
= g_strdup (text
);
114 release_hotkey (const struct hotkey_t hotkey
)
116 g_free (hotkey
.start
);
121 hotkey_width (const struct hotkey_t hotkey
)
126 result
= str_term_width1 (hotkey
.start
);
127 if (hotkey
.hotkey
> 255) {
128 len
= g_unichar_to_utf8 (hotkey
.hotkey
, (char *) NULL
);
132 if (hotkey
.hotkey
!= 0)
135 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
140 draw_hotkey (Widget
*w
, const struct hotkey_t hotkey
, gboolean focused
)
142 widget_selectcolor (w
, focused
, FALSE
);
143 tty_print_string (hotkey
.start
);
145 if (hotkey
.hotkey
!= 0) {
146 widget_selectcolor (w
, focused
, TRUE
);
147 tty_print_anychar (hotkey
.hotkey
);
148 widget_selectcolor (w
, focused
, FALSE
);
151 if (hotkey
.end
!= NULL
)
152 tty_print_string (hotkey
.end
);
156 /* Default callback for widgets */
158 default_proc (widget_msg_t msg
, int parm
)
173 return MSG_NOT_HANDLED
;
177 static int button_event (Gpm_Event
*event
, void *);
182 button_callback (Widget
*w
, widget_msg_t msg
, int parm
)
184 WButton
*b
= (WButton
*) w
;
187 Dlg_head
*h
= b
->widget
.parent
;
192 * Don't let the default button steal Enter from the current
193 * button. This is a workaround for the flawed event model
194 * when hotkeys are sent to all widgets before the key is
195 * handled by the current widget.
197 if (parm
== '\n' && h
->current
== &b
->widget
) {
198 button_callback (w
, WIDGET_KEY
, ' ');
202 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
) {
203 button_callback (w
, WIDGET_KEY
, ' ');
207 if (b
->text
.hotkey
!= 0) {
208 if (b
->text
.hotkey
== parm
) {
209 button_callback (w
, WIDGET_KEY
, ' ');
213 return MSG_NOT_HANDLED
;
216 if (parm
!= ' ' && parm
!= '\n')
217 return MSG_NOT_HANDLED
;
220 stop
= (*b
->callback
) (b
->action
);
221 if (!b
->callback
|| stop
) {
222 h
->ret_value
= b
->action
;
243 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
249 if (msg
== WIDGET_UNFOCUS
)
251 else if (msg
== WIDGET_FOCUS
)
254 widget_selectcolor (w
, b
->selected
, FALSE
);
255 widget_move (w
, 0, 0);
259 tty_print_string ("[< ");
262 tty_print_string ("[ ");
265 tty_print_string ("[");
272 draw_hotkey (w
, b
->text
, b
->selected
);
276 tty_print_string (" >]");
279 tty_print_string (" ]");
282 tty_print_string ("]");
288 release_hotkey (b
->text
);
292 return default_proc (msg
, parm
);
297 button_event (Gpm_Event
*event
, void *data
)
301 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
302 Dlg_head
*h
=b
->widget
.parent
;
303 dlg_select_widget (b
);
304 if (event
->type
& GPM_UP
){
305 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
306 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
314 button_get_len (const WButton
*b
)
316 int ret
= hotkey_width (b
->text
);
335 button_new (int y
, int x
, int action
, int flags
, const char *text
,
338 WButton
*b
= g_new (WButton
, 1);
342 b
->text
= parse_hotkey (text
);
344 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
),
345 button_callback
, button_event
);
348 b
->callback
= callback
;
349 widget_want_hotkey (b
->widget
, 1);
350 b
->hotpos
= (b
->text
.hotkey
!= 0) ? str_term_width1 (b
->text
.start
) : -1;
356 button_get_text (const WButton
*b
)
358 if (b
->text
.hotkey
!= NULL
)
359 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
,
362 return g_strdup (b
->text
.start
);
366 button_set_text (WButton
*b
, const char *text
)
368 release_hotkey (b
->text
);
369 b
->text
= parse_hotkey (text
);
370 b
->widget
.cols
= button_get_len (b
);
371 dlg_redraw (b
->widget
.parent
);
375 /* Radio button widget */
376 static int radio_event (Gpm_Event
*event
, void *);
379 radio_callback (Widget
*w
, widget_msg_t msg
, int parm
)
381 WRadio
*r
= (WRadio
*) w
;
383 Dlg_head
*h
= r
->widget
.parent
;
390 for (i
= 0; i
< r
->count
; i
++) {
391 if (r
->texts
[i
].hotkey
!= 0) {
392 int c
= r
->texts
[i
].hotkey
;
399 radio_callback (w
, WIDGET_KEY
, ' ');
404 return MSG_NOT_HANDLED
;
410 (*h
->callback
) (h
, DLG_ACTION
, 0);
411 radio_callback (w
, WIDGET_FOCUS
, ' ');
420 return MSG_NOT_HANDLED
;
424 if (r
->count
- 1 > r
->pos
) {
429 return MSG_NOT_HANDLED
;
432 (*h
->callback
) (h
, DLG_ACTION
, 0);
433 radio_callback (w
, WIDGET_FOCUS
, ' ');
434 widget_move (&r
->widget
, r
->pos
, 1);
440 for (i
= 0; i
< r
->count
; i
++) {
441 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
442 widget_selectcolor (w
, focused
, FALSE
);
443 widget_move (&r
->widget
, i
, 0);
444 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
445 draw_hotkey (w
, r
->texts
[i
], focused
);
450 for (i
= 0; i
< r
->count
; i
++) {
451 release_hotkey (r
->texts
[i
]);
457 return default_proc (msg
, parm
);
462 radio_event (Gpm_Event
*event
, void *data
)
467 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
468 Dlg_head
*h
= r
->widget
.parent
;
470 r
->pos
= event
->y
- 1;
471 dlg_select_widget (r
);
472 if (event
->type
& GPM_UP
){
473 radio_callback (w
, WIDGET_KEY
, ' ');
474 radio_callback (w
, WIDGET_FOCUS
, 0);
475 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
483 radio_new (int y
, int x
, int count
, const char **texts
)
485 WRadio
*result
= g_new (WRadio
, 1);
488 /* Compute the longest string */
489 result
->texts
= g_new (struct hotkey_t
, count
);
492 for (i
= 0; i
< count
; i
++){
493 result
->texts
[i
] = parse_hotkey (texts
[i
]);
494 m
= hotkey_width (result
->texts
[i
]);
499 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
503 result
->count
= count
;
504 widget_want_hotkey (result
->widget
, 1);
510 /* Checkbutton widget */
512 static int check_event (Gpm_Event
*event
, void *);
515 check_callback (Widget
*w
, widget_msg_t msg
, int parm
)
517 WCheck
*c
= (WCheck
*) w
;
518 Dlg_head
*h
= c
->widget
.parent
;
522 if (c
->text
.hotkey
!= 0) {
523 if (c
->text
.hotkey
== parm
) {
524 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
528 return MSG_NOT_HANDLED
;
532 return MSG_NOT_HANDLED
;
534 c
->state
^= C_CHANGE
;
535 (*h
->callback
) (h
, DLG_ACTION
, 0);
536 check_callback (w
, WIDGET_FOCUS
, ' ');
540 widget_move (&c
->widget
, 0, 1);
546 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
547 widget_move (&c
->widget
, 0, 0);
548 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
549 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
553 release_hotkey (c
->text
);
557 return default_proc (msg
, parm
);
562 check_event (Gpm_Event
*event
, void *data
)
567 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
568 Dlg_head
*h
= c
->widget
.parent
;
570 dlg_select_widget (c
);
571 if (event
->type
& GPM_UP
){
572 check_callback (w
, WIDGET_KEY
, ' ');
573 check_callback (w
, WIDGET_FOCUS
, 0);
574 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
582 check_new (int y
, int x
, int state
, const char *text
)
584 WCheck
*c
= g_new (WCheck
, 1);
586 c
->text
= parse_hotkey (text
);
588 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
),
589 check_callback
, check_event
);
590 c
->state
= state
? C_BOOL
: 0;
591 widget_want_hotkey (c
->widget
, 1);
600 label_callback (Widget
*w
, widget_msg_t msg
, int parm
)
602 WLabel
*l
= (WLabel
*) w
;
603 Dlg_head
*h
= l
->widget
.parent
;
609 /* We don't want to get the focus */
611 return MSG_NOT_HANDLED
;
615 char *p
= l
->text
, *q
, c
= 0;
622 tty_setcolor (DEFAULT_COLOR
);
624 tty_setcolor (DLG_NORMALC (h
));
627 q
= strchr (p
, '\n');
633 widget_move (&l
->widget
, y
, 0);
634 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
650 return default_proc (msg
, parm
);
655 label_set_text (WLabel
*label
, const char *text
)
657 int newcols
= label
->widget
.cols
;
660 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
661 return; /* Flickering is not nice */
663 g_free (label
->text
);
666 label
->text
= g_strdup (text
);
667 if (label
->auto_adjust_cols
) {
668 str_msg_term_size (text
, &newlines
, &newcols
);
669 if (newcols
> label
->widget
.cols
)
670 label
->widget
.cols
= newcols
;
671 if (newlines
> label
->widget
.lines
)
672 label
->widget
.lines
= newlines
;
674 } else label
->text
= NULL
;
676 if (label
->widget
.parent
)
677 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
679 if (newcols
< label
->widget
.cols
)
680 label
->widget
.cols
= newcols
;
684 label_new (int y
, int x
, const char *text
)
691 str_msg_term_size (text
, &lines
, &cols
);
693 l
= g_new (WLabel
, 1);
694 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
695 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
696 l
->auto_adjust_cols
= 1;
698 widget_want_cursor (l
->widget
, 0);
703 /* Gauge widget (progress indicator) */
704 /* Currently width is hardcoded here for text mode */
708 gauge_callback (Widget
*w
, widget_msg_t msg
, int parm
)
710 WGauge
*g
= (WGauge
*) w
;
711 Dlg_head
*h
= g
->widget
.parent
;
713 if (msg
== WIDGET_INIT
)
716 /* We don't want to get the focus */
717 if (msg
== WIDGET_FOCUS
)
718 return MSG_NOT_HANDLED
;
720 if (msg
== WIDGET_DRAW
){
721 widget_move (&g
->widget
, 0, 0);
722 tty_setcolor (DLG_NORMALC (h
));
724 tty_printf ("%*s", gauge_len
, "");
726 int percentage
, columns
;
727 long total
= g
->max
, done
= g
->current
;
729 if (total
<= 0 || done
< 0) {
735 while (total
> 65535) {
739 percentage
= (200 * done
/ total
+ 1) / 2;
740 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
741 tty_print_char ('[');
742 tty_setcolor (GAUGE_COLOR
);
743 tty_printf ("%*s", (int) columns
, "");
744 tty_setcolor (DLG_NORMALC (h
));
745 tty_printf ("%*s] %3d%%", (int)(gauge_len
- 7 - columns
), "", (int) percentage
);
750 return default_proc (msg
, parm
);
754 gauge_set_value (WGauge
*g
, int max
, int current
)
756 if (g
->current
== current
&& g
->max
== max
)
757 return; /* Do not flicker */
759 max
= 1; /* I do not like division by zero :) */
761 g
->current
= current
;
763 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
767 gauge_show (WGauge
*g
, int shown
)
769 if (g
->shown
== shown
)
772 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
776 gauge_new (int y
, int x
, int shown
, int max
, int current
)
778 WGauge
*g
= g_new (WGauge
, 1);
780 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
783 max
= 1; /* I do not like division by zero :) */
785 g
->current
= current
;
786 widget_want_cursor (g
->widget
, 0);
793 /* {{{ history button */
795 #define LARGE_HISTORY_BUTTON 1
797 #ifdef LARGE_HISTORY_BUTTON
798 # define HISTORY_BUTTON_WIDTH 3
800 # define HISTORY_BUTTON_WIDTH 1
803 #define should_show_history_button(in) \
804 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
806 static void draw_history_button (WInput
* in
)
809 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
810 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
811 #ifdef LARGE_HISTORY_BUTTON
814 h
= in
->widget
.parent
;
815 tty_setcolor (NORMAL_COLOR
);
816 tty_print_string ("[ ]");
817 /* Too distracting: tty_setcolor (MARKED_COLOR); */
818 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
822 tty_setcolor (MARKED_COLOR
);
827 /* }}} history button */
830 /* Input widgets now have a global kill ring */
831 /* Pointer to killed data */
832 static char *kill_buffer
= 0;
835 update_input (WInput
*in
, int clear_first
)
839 int buf_len
= str_length (in
->buffer
);
843 if (should_show_history_button (in
))
844 has_history
= HISTORY_BUTTON_WIDTH
;
846 if (in
->disable_update
)
849 pw
= str_term_width2 (in
->buffer
, in
->point
);
851 /* Make the point visible */
852 if ((pw
< in
->term_first_shown
) ||
853 (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
)) {
855 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
856 if (in
->term_first_shown
< 0)
857 in
->term_first_shown
= 0;
860 /* Adjust the mark */
861 if (in
->mark
> buf_len
)
865 draw_history_button (in
);
867 tty_setcolor (in
->color
);
869 widget_move (&in
->widget
, 0, 0);
871 if (!in
->is_password
) {
872 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
873 in
->field_width
- has_history
));
876 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++){
878 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
880 if (cp
[0] != '\0') str_cnext_char (&cp
);
889 winput_set_origin (WInput
*in
, int x
, int field_width
)
892 in
->field_width
= in
->widget
.cols
= field_width
;
893 update_input (in
, 0);
896 /* {{{ history saving and loading */
898 int num_history_items_recorded
= 60;
901 This loads and saves the history of an input line to and from the
902 widget. It is called with the widgets history name on creation of the
903 widget, and returns the GList list. It stores histories in the file
904 ~/.mc/history in using the profile code.
906 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
907 function) then input_new assigns the default text to be the last text
908 entered, or "" if not found.
912 history_get (const char *input_name
)
922 if (!num_history_items_recorded
) /* this is how to disable */
924 if (!input_name
|| !*input_name
)
927 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
928 cfg
= mc_config_init (profile
);
930 /* get number of keys */
931 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
934 for (i
= 0; i
< keys_num
; i
++) {
935 char key_name
[BUF_TINY
];
936 g_snprintf (key_name
, sizeof (key_name
), "%lu", (unsigned long)i
);
937 this_entry
= mc_config_get_string (cfg
, input_name
, key_name
, "");
939 if (this_entry
&& *this_entry
)
940 hist
= list_append_unique (hist
, this_entry
);
943 mc_config_deinit (cfg
);
946 /* return pointer to the last entry in the list */
947 return g_list_last (hist
);
951 history_put (const char *input_name
, GList
*h
)
966 if (!num_history_items_recorded
) /* this is how to disable */
969 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
971 if ((i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) != -1)
974 /* Make sure the history is only readable by the user */
975 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
) {
980 /* go to end of list */
983 /* go back 60 places */
984 for (i
= 0; i
< num_history_items_recorded
- 1 && h
->prev
; i
++)
985 h
= g_list_previous (h
);
987 cfg
= mc_config_init(profile
);
990 mc_config_del_group(cfg
,input_name
);
992 /* dump histories into profile */
993 for (i
= 0; h
; h
= g_list_next (h
)) {
996 text
= (char *) h
->data
;
998 /* We shouldn't have null entries, but let's be sure */
1000 char key_name
[BUF_TINY
];
1001 g_snprintf (key_name
, sizeof (key_name
), "%d", i
++);
1002 mc_config_set_string(cfg
,input_name
, key_name
, text
);
1006 mc_config_save_file (cfg
);
1007 mc_config_deinit(cfg
);
1011 /* }}} history saving and loading */
1014 /* {{{ history display */
1019 return _(" History ");
1023 listbox_fwd (WListbox
*l
)
1025 if (l
->current
!= l
->list
->prev
)
1026 listbox_select_entry (l
, l
->current
->next
);
1028 listbox_select_first (l
);
1038 dlg_hist_reposition (Dlg_head
*dlg_head
)
1040 dlg_hist_data
*data
;
1041 int x
= 0, y
, he
, wi
;
1044 if ((dlg_head
== NULL
)
1045 || (dlg_head
->data
== NULL
))
1046 return MSG_NOT_HANDLED
;
1048 data
= (dlg_hist_data
*) dlg_head
->data
;
1050 y
= data
->widget
->y
;
1051 he
= data
->count
+ 2;
1053 if (he
<= y
|| y
> (LINES
- 6)) {
1054 he
= min (he
, y
- 1);
1058 he
= min (he
, LINES
- y
);
1061 if (data
->widget
->x
> 2)
1062 x
= data
->widget
->x
- 2;
1064 wi
= data
->maxlen
+ 4;
1066 if ((wi
+ x
) > COLS
) {
1067 wi
= min (wi
, COLS
);
1071 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1077 dlg_hist_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
1081 return dlg_hist_reposition (h
);
1084 return default_dlg_callback (h
, msg
, parm
);
1089 show_hist (GList
*history
, Widget
*widget
)
1092 size_t maxlen
, i
, count
= 0;
1094 Dlg_head
*query_dlg
;
1095 WListbox
*query_list
;
1096 dlg_hist_data hist_data
;
1098 if (history
== NULL
)
1101 maxlen
= str_term_width1 (i18n_htitle ());
1103 z
= g_list_first (history
);
1106 i
= str_term_width1 ((char *) hi
->data
);
1107 maxlen
= max (maxlen
, i
);
1109 hi
= g_list_next (hi
);
1112 hist_data
.maxlen
= maxlen
;
1113 hist_data
.widget
= widget
;
1114 hist_data
.count
= count
;
1117 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1118 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1119 query_dlg
->data
= &hist_data
;
1121 query_list
= listbox_new (1, 1, 2, 2, NULL
);
1123 /* this call makes list stick to all sides of dialog, effectively make
1124 it be resized with dialog */
1125 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1127 /* to avoid diplicating of (calculating sizes in two places)
1128 code, call dlg_hist_callback function here, to set dialog and
1130 The main idea - create 4x4 dialog and add 2x2 list in
1131 center of it, and let dialog function resize it to needed
1133 dlg_hist_callback (query_dlg
, DLG_RESIZE
, 0);
1135 if (query_dlg
->y
< widget
->y
) {
1139 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1140 0, (char *) hi
->data
, NULL
);
1141 hi
= g_list_next (hi
);
1143 listbox_select_last (query_list
);
1145 /* traverse backwards */
1146 hi
= g_list_last (history
);
1148 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1149 0, (char *) hi
->data
, NULL
);
1150 hi
= g_list_previous (hi
);
1154 if (run_dlg (query_dlg
) != B_CANCEL
) {
1155 listbox_get_current (query_list
, &q
, NULL
);
1159 destroy_dlg (query_dlg
);
1164 do_show_hist (WInput
*in
)
1167 r
= show_hist (in
->history
, &in
->widget
);
1169 assign_text (in
, r
);
1174 /* }}} history display */
1177 input_destroy (WInput
*in
)
1180 fprintf (stderr
, "Internal error: null Input *\n");
1187 if (!in
->is_password
) /* don't save passwords ;-) */
1188 history_put (in
->history_name
, in
->history
);
1190 in
->history
= g_list_first (in
->history
);
1191 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1192 g_list_free (in
->history
);
1195 g_free (in
->buffer
);
1196 free_completions (in
);
1197 g_free (in
->history_name
);
1201 input_disable_update (WInput
*in
)
1203 in
->disable_update
++;
1207 input_enable_update (WInput
*in
)
1209 in
->disable_update
--;
1210 update_input (in
, 0);
1213 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1216 push_history (WInput
*in
, const char *text
)
1219 /* input widget where urls with passwords are entered without any
1221 static const char *password_input_fields
[] = {
1222 N_(" Link to a remote machine "),
1223 N_(" FTP to machine "),
1224 N_(" SMB link to machine ")
1232 for (i
= 0; i
< ELEMENTS (password_input_fields
); i
++)
1233 password_input_fields
[i
] = _(password_input_fields
[i
]);
1236 for (p
= text
; *p
== ' ' || *p
== '\t'; p
++);
1241 /* Avoid duplicated entries */
1242 in
->history
= g_list_last (in
->history
);
1243 if (!strcmp ((char *) in
->history
->data
, text
))
1247 t
= g_strdup (text
);
1249 if (in
->history_name
) {
1250 p
= in
->history_name
+ 3;
1251 for (i
= 0; i
< ELEMENTS (password_input_fields
); i
++)
1252 if (strcmp (p
, password_input_fields
[i
]) == 0)
1254 if (i
< ELEMENTS (password_input_fields
))
1255 strip_password (t
, 0);
1257 strip_password (t
, 1);
1260 in
->history
= list_append_unique (in
->history
, t
);
1268 /* Cleans the input line and adds the current text to the history */
1270 new_input (WInput
*in
)
1273 push_history (in
, in
->buffer
);
1275 in
->buffer
[0] = '\0';
1279 free_completions (in
);
1280 update_input (in
, 0);
1284 move_buffer_backward (WInput
*in
, int start
, int end
)
1287 int str_len
= str_length (in
->buffer
);
1288 if (start
>= str_len
|| end
> str_len
+ 1) return;
1290 pos
= str_offset_to_pos (in
->buffer
, start
);
1291 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1293 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1294 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1298 insert_char (WInput
*in
, int c_code
)
1304 return MSG_NOT_HANDLED
;
1306 if (in
->charpoint
>= MB_LEN_MAX
)
1309 in
->charbuf
[in
->charpoint
] = c_code
;
1312 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1315 in
->charpoint
= 0; /* broken multibyte char, skip */
1320 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
){
1321 /* Expand the buffer */
1322 size_t new_length
= in
->current_max_size
+
1323 in
->field_width
+ in
->charpoint
;
1324 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1327 in
->current_max_size
= new_length
;
1331 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
) {
1332 /* bytes from begin */
1333 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1335 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1337 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1338 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] =
1339 in
->buffer
[ins_point
+ i
- 1];
1341 memcpy(in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1350 beginning_of_line (WInput
*in
)
1357 end_of_line (WInput
*in
)
1359 in
->point
= str_length (in
->buffer
);
1364 backward_char (WInput
*in
)
1366 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1368 if (in
->point
> 0) {
1369 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1375 forward_char (WInput
*in
)
1377 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1378 if (act
[0] != '\0') {
1379 in
->point
+= str_cnext_noncomb_char (&act
);
1385 forward_word (WInput
* in
)
1387 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1389 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
))) {
1390 str_cnext_char (&p
);
1393 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
)) {
1394 str_cnext_char (&p
);
1400 backward_word (WInput
*in
)
1406 p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1407 (p
!= in
->buffer
) && (p
[0] == '\0');
1411 while (p
!= in
->buffer
) {
1413 str_cprev_char (&p
);
1414 if (!str_isspace (p
) && !str_ispunct (p
)) {
1420 while (p
!= in
->buffer
) {
1421 str_cprev_char (&p
);
1422 if (str_isspace (p
) || str_ispunct (p
))
1430 key_left (WInput
*in
)
1436 key_ctrl_left (WInput
*in
)
1442 key_right (WInput
*in
)
1448 key_ctrl_right (WInput
*in
)
1453 backward_delete (WInput
*in
)
1455 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1461 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1462 move_buffer_backward(in
, start
, in
->point
);
1469 delete_char (WInput
*in
)
1471 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1472 int end
= in
->point
;
1474 end
+= str_cnext_noncomb_char (&act
);
1476 move_buffer_backward(in
, in
->point
, end
);
1482 copy_region (WInput
*in
, int x_first
, int x_last
)
1484 int first
= min (x_first
, x_last
);
1485 int last
= max (x_first
, x_last
);
1490 g_free (kill_buffer
);
1492 first
= str_offset_to_pos (in
->buffer
, first
);
1493 last
= str_offset_to_pos (in
->buffer
, last
);
1495 kill_buffer
= g_strndup(in
->buffer
+ first
, last
- first
);
1499 delete_region (WInput
*in
, int x_first
, int x_last
)
1501 int first
= min (x_first
, x_last
);
1502 int last
= max (x_first
, x_last
);
1507 last
= str_offset_to_pos (in
->buffer
, last
);
1508 first
= str_offset_to_pos (in
->buffer
, first
);
1509 len
= strlen (&in
->buffer
[last
]) + 1;
1510 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1516 kill_word (WInput
*in
)
1518 int old_point
= in
->point
;
1522 new_point
= in
->point
;
1523 in
->point
= old_point
;
1525 copy_region (in
, old_point
, new_point
);
1526 delete_region (in
, old_point
, new_point
);
1533 back_kill_word (WInput
*in
)
1535 int old_point
= in
->point
;
1539 new_point
= in
->point
;
1540 in
->point
= old_point
;
1542 copy_region (in
, old_point
, new_point
);
1543 delete_region (in
, old_point
, new_point
);
1548 set_mark (WInput
*in
)
1550 in
->mark
= in
->point
;
1554 kill_save (WInput
*in
)
1556 copy_region (in
, in
->mark
, in
->point
);
1560 kill_region (WInput
*in
)
1563 delete_region (in
, in
->point
, in
->mark
);
1574 for (p
= kill_buffer
; *p
; p
++)
1575 insert_char (in
, *p
);
1580 kill_line (WInput
*in
)
1582 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1583 g_free (kill_buffer
);
1584 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1585 in
->buffer
[chp
] = '\0';
1590 assign_text (WInput
*in
, const char *text
)
1592 free_completions (in
);
1593 g_free (in
->buffer
);
1594 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1595 in
->current_max_size
= strlen (in
->buffer
) + 1;
1596 in
->point
= str_length (in
->buffer
);
1603 hist_prev (WInput
*in
)
1608 if (in
->need_push
) {
1609 switch (push_history (in
, in
->buffer
)) {
1611 in
->history
= g_list_previous (in
->history
);
1614 if (in
->history
->prev
)
1615 in
->history
= g_list_previous (in
->history
);
1620 } else if (in
->history
->prev
)
1621 in
->history
= g_list_previous (in
->history
);
1624 assign_text (in
, (char *) in
->history
->data
);
1629 hist_next (WInput
*in
)
1631 if (in
->need_push
) {
1632 switch (push_history (in
, in
->buffer
)) {
1634 assign_text (in
, "");
1644 if (!in
->history
->next
) {
1645 assign_text (in
, "");
1649 in
->history
= g_list_next (in
->history
);
1650 assign_text (in
, (char *) in
->history
->data
);
1655 port_region_marked_for_delete (WInput
*in
)
1657 in
->buffer
[0] = '\0';
1664 input_execute_cmd (WInput
*in
, int command
)
1666 cb_ret_t res
= MSG_HANDLED
;
1670 beginning_of_line (in
);
1675 case CK_InputMoveLeft
:
1678 case CK_InputWordLeft
:
1681 case CK_InputMoveRight
:
1684 case CK_InputWordRight
:
1685 key_ctrl_right (in
);
1687 case CK_InputBackwardChar
:
1690 case CK_InputBackwardWord
:
1693 case CK_InputForwardChar
:
1696 case CK_InputForwardWord
:
1699 case CK_InputBackwardDelete
:
1700 backward_delete (in
);
1702 case CK_InputDeleteChar
:
1705 case CK_InputKillWord
:
1708 case CK_InputBackwardKillWord
:
1709 back_kill_word (in
);
1711 case CK_InputSetMark
:
1714 case CK_InputKillRegion
:
1717 case CK_InputKillSave
:
1723 case CK_InputKillLine
:
1726 case CK_InputHistoryPrev
:
1729 case CK_InputHistoryNext
:
1732 case CK_InputHistoryShow
:
1735 case CK_InputComplete
:
1739 res
= MSG_NOT_HANDLED
;
1745 /* This function is a test for a special input key used in complete.c */
1746 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1747 and 2 if it is a complete key */
1749 is_in_input_map (WInput
*in
, int key
)
1753 for (i
= 0; input_map
[i
].key
; i
++) {
1754 if (key
== input_map
[i
].key
) {
1755 input_execute_cmd (in
, input_map
[i
].command
);
1756 if (input_map
[i
].command
== CK_InputComplete
)
1766 handle_char (WInput
*in
, int key
)
1771 v
= MSG_NOT_HANDLED
;
1774 free_completions (in
);
1775 v
= insert_char (in
, key
);
1776 update_input (in
, 1);
1781 for (i
= 0; input_map
[i
].key
; i
++) {
1782 if (key
== input_map
[i
].key
) {
1783 if (input_map
[i
].command
!= CK_InputComplete
)
1784 free_completions (in
);
1785 input_execute_cmd (in
, input_map
[i
].command
);
1786 update_input (in
, 1);
1791 if (input_map
[i
].command
== 0) {
1793 return MSG_NOT_HANDLED
;
1795 port_region_marked_for_delete (in
);
1796 free_completions (in
);
1797 v
= insert_char (in
, key
);
1799 update_input (in
, 1);
1803 /* Inserts text in input line */
1805 stuff (WInput
*in
, const char *text
, int insert_extra_space
)
1807 input_disable_update (in
);
1809 handle_char (in
, *text
++);
1810 if (insert_extra_space
)
1811 handle_char (in
, ' ');
1812 input_enable_update (in
);
1813 update_input (in
, 1);
1817 input_set_point (WInput
*in
, int pos
)
1819 int max_pos
= str_length (in
->buffer
);
1823 if (pos
!= in
->point
)
1824 free_completions (in
);
1827 update_input (in
, 1);
1831 input_callback (Widget
*w
, widget_msg_t msg
, int parm
)
1833 WInput
*in
= (WInput
*) w
;
1838 if (parm
== XCTRL ('q')) {
1840 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
1845 /* Keys we want others to handle */
1846 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
1847 || parm
== KEY_F (10) || parm
== XCTRL ('g') || parm
== '\n')
1848 return MSG_NOT_HANDLED
;
1850 /* When pasting multiline text, insert literal Enter */
1851 if ((parm
& ~KEY_M_MASK
) == '\n') {
1853 v
= handle_char (in
, '\n');
1858 return handle_char (in
, parm
);
1860 case WIDGET_COMMAND
:
1861 return input_execute_cmd (in
, parm
);
1864 case WIDGET_UNFOCUS
:
1866 update_input (in
, 0);
1870 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
1871 - in
->term_first_shown
);
1874 case WIDGET_DESTROY
:
1879 return default_proc (msg
, parm
);
1884 input_event (Gpm_Event
* event
, void *data
)
1888 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
1889 dlg_select_widget (in
);
1891 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
1892 && should_show_history_button (in
)) {
1895 in
->point
= str_length (in
->buffer
);
1896 if (event
->x
+ in
->term_first_shown
- 1 <
1897 str_term_width1 (in
->buffer
))
1899 in
->point
= str_column_to_pos (in
->buffer
, event
->x
1900 + in
->term_first_shown
- 1);
1903 update_input (in
, 1);
1909 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
1910 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
1912 WInput
*in
= g_new (WInput
, 1);
1913 int initial_buffer_len
;
1915 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
1919 in
->history_name
= 0;
1922 in
->history_name
= g_strdup (histname
);
1923 in
->history
= history_get (histname
);
1927 if (def_text
== NULL
)
1930 if (def_text
== INPUT_LAST_TEXT
) {
1933 if (in
->history
->data
)
1934 def_text
= (char *) in
->history
->data
;
1936 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
1937 in
->widget
.options
|= W_IS_INPUT
;
1938 in
->completions
= NULL
;
1939 in
->completion_flags
= completion_flags
;
1940 in
->current_max_size
= initial_buffer_len
;
1941 in
->buffer
= g_new (char, initial_buffer_len
);
1943 in
->field_width
= width
;
1945 in
->term_first_shown
= 0;
1946 in
->disable_update
= 0;
1949 in
->is_password
= 0;
1951 strcpy (in
->buffer
, def_text
);
1952 in
->point
= str_length (in
->buffer
);
1957 /* Listbox widget */
1959 /* Should draw the scrollbar, but currently draws only
1960 * indications that there is more information
1962 static int listbox_cdiff (WLEntry
*s
, WLEntry
*e
);
1965 listbox_drawscroll (WListbox
*l
)
1969 int max_line
= l
->widget
.lines
- 1;
1971 /* Are we at the top? */
1972 widget_move (&l
->widget
, 0, l
->widget
.cols
);
1973 if (l
->list
== l
->top
)
1974 tty_print_one_vline ();
1976 tty_print_char ('^');
1978 /* Are we at the bottom? */
1979 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
1980 top
= listbox_cdiff (l
->list
, l
->top
);
1981 if ((top
+ l
->widget
.lines
== l
->count
) || l
->widget
.lines
>= l
->count
)
1982 tty_print_one_vline ();
1984 tty_print_char ('v');
1986 /* Now draw the nice relative pointer */
1988 line
= 1+ ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
1990 for (i
= 1; i
< max_line
; i
++){
1991 widget_move (&l
->widget
, i
, l
->widget
.cols
);
1993 tty_print_one_vline ();
1995 tty_print_char ('*');
2000 listbox_draw (WListbox
*l
, gboolean focused
)
2002 const Dlg_head
*h
= l
->widget
.parent
;
2003 const int normalc
= DLG_NORMALC (h
);
2004 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2011 for (e
= l
->top
, i
= 0; i
< l
->widget
.lines
; i
++) {
2012 /* Display the entry */
2013 if (e
== l
->current
&& sel_line
== -1) {
2015 tty_setcolor (selc
);
2017 tty_setcolor (normalc
);
2019 widget_move (&l
->widget
, i
, 1);
2021 if ((i
> 0 && e
== l
->list
) || !l
->list
)
2027 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2029 l
->cursor_y
= sel_line
;
2031 if (l
->scrollbar
&& l
->count
> l
->widget
.lines
) {
2032 tty_setcolor (normalc
);
2033 listbox_drawscroll (l
);
2037 /* Returns the number of items between s and e,
2038 must be on the same linked list */
2040 listbox_cdiff (WLEntry
*s
, WLEntry
*e
)
2044 for (count
= 0; s
!= e
; count
++)
2050 listbox_check_hotkey (WListbox
*l
, int key
)
2062 /* If we didn't find anything, return */
2063 if (i
&& e
== l
->list
)
2066 if (e
->hotkey
== key
)
2074 /* Selects the last entry and scrolls the list to the bottom */
2076 listbox_select_last (WListbox
*l
)
2079 l
->current
= l
->top
= l
->list
->prev
;
2080 for (i
= min (l
->widget
.lines
, l
->count
) - 1; i
; i
--)
2081 l
->top
= l
->top
->prev
;
2082 l
->pos
= l
->count
- 1;
2085 /* Selects the first entry and scrolls the list to the top */
2087 listbox_select_first (WListbox
*l
)
2089 l
->current
= l
->top
= l
->list
;
2094 listbox_remove_list (WListbox
*l
)
2103 while (l
->count
--) {
2109 l
->pos
= l
->count
= 0;
2110 l
->list
= l
->top
= l
->current
= 0;
2114 * bor 30.10.96: added force flag to remove *last* entry as well
2115 * bor 30.10.96: corrected selection bug if last entry was removed
2119 listbox_remove_current (WListbox
*l
, int force
)
2123 /* Ok, note: this won't allow for emtpy lists */
2124 if (!force
&& (!l
->count
|| l
->count
== 1))
2131 l
->current
->next
->prev
= l
->current
->prev
;
2132 l
->current
->prev
->next
= l
->current
->next
;
2133 if (p
->next
== l
->list
) {
2134 l
->current
= p
->prev
;
2138 l
->current
= p
->next
;
2141 l
->list
= l
->top
= p
->next
;
2144 l
->list
= l
->top
= l
->current
= 0;
2151 /* Makes *e the selected entry (sets current and pos) */
2153 listbox_select_entry (WListbox
*l
, WLEntry
*dest
)
2162 for (pos
= 0, e
= l
->list
; pos
< l
->count
; e
= e
->next
, pos
++){
2170 while (listbox_cdiff (l
->top
, l
->current
) >= l
->widget
.lines
)
2171 l
->top
= l
->top
->next
;
2173 l
->top
= l
->current
;
2179 /* If we are unable to find it, set decent values */
2180 l
->current
= l
->top
= l
->list
;
2184 /* Selects from base the pos element */
2186 listbox_select_pos (WListbox
*l
, WLEntry
*base
, int pos
)
2188 WLEntry
*last
= l
->list
->prev
;
2201 listbox_back (WListbox
*l
)
2204 listbox_select_entry (l
, l
->current
->prev
);
2206 listbox_select_last (l
);
2209 /* Return MSG_HANDLED if we want a redraw */
2211 listbox_key (WListbox
*l
, int key
)
2215 cb_ret_t j
= MSG_NOT_HANDLED
;
2217 /* focus on listbox item N by '0'..'9' keys */
2218 if (key
>= '0' && key
<= '9') {
2219 int oldpos
= l
->pos
;
2220 listbox_select_by_number(l
, key
- '0');
2222 /* need scroll to item? */
2223 if (abs(oldpos
- l
->pos
) > l
->widget
.lines
)
2224 l
->top
= l
->current
;
2230 return MSG_NOT_HANDLED
;
2236 listbox_select_first (l
);
2242 listbox_select_last (l
);
2257 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2258 && (l
->current
!= l
->list
->prev
)); i
++) {
2266 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2267 && (l
->current
!= l
->list
)); i
++) {
2273 return MSG_NOT_HANDLED
;
2277 listbox_destroy (WListbox
*l
)
2279 WLEntry
*n
, *p
= l
->list
;
2282 for (i
= 0; i
< l
->count
; i
++){
2291 listbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2293 WListbox
*l
= (WListbox
*) w
;
2294 Dlg_head
*h
= l
->widget
.parent
;
2303 e
= listbox_check_hotkey (l
, parm
);
2307 listbox_select_entry (l
, e
);
2309 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2312 action
= (*l
->cback
) (l
);
2314 action
= LISTBOX_DONE
;
2316 if (action
== LISTBOX_DONE
) {
2317 h
->ret_value
= B_ENTER
;
2322 return MSG_NOT_HANDLED
;
2325 ret_code
= listbox_key (l
, parm
);
2326 if (ret_code
!= MSG_NOT_HANDLED
) {
2327 listbox_draw (l
, TRUE
);
2328 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2333 widget_move (&l
->widget
, l
->cursor_y
, 0);
2334 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2338 case WIDGET_UNFOCUS
:
2340 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2343 case WIDGET_DESTROY
:
2344 listbox_destroy (l
);
2347 case WIDGET_RESIZED
:
2351 return default_proc (msg
, parm
);
2356 listbox_event (Gpm_Event
*event
, void *data
)
2361 Dlg_head
*h
= l
->widget
.parent
;
2364 if (event
->type
& GPM_DOWN
)
2365 dlg_select_widget (l
);
2367 if (l
->list
== NULL
)
2370 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2371 int ret
= MOU_REPEAT
;
2373 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2377 for (i
= -event
->y
; i
>= 0; i
--)
2379 else if (event
->y
> l
->widget
.lines
)
2380 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2382 else if (event
->buttons
& GPM_B_UP
) {
2385 } else if (event
->buttons
& GPM_B_DOWN
) {
2389 listbox_select_entry (l
,
2390 listbox_select_pos (l
, l
->top
,
2393 /* We need to refresh ourselves since the dialog manager doesn't */
2394 /* know about this event */
2395 listbox_draw (l
, TRUE
);
2400 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
)) {
2403 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2404 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2407 dlg_select_widget (l
);
2408 listbox_select_entry (l
,
2409 listbox_select_pos (l
, l
->top
,
2413 action
= (*l
->cback
) (l
);
2415 action
= LISTBOX_DONE
;
2417 if (action
== LISTBOX_DONE
) {
2418 h
->ret_value
= B_ENTER
;
2427 listbox_new (int y
, int x
, int height
, int width
, lcback callback
)
2429 WListbox
*l
= g_new (WListbox
, 1);
2434 init_widget (&l
->widget
, y
, x
, height
, width
,
2435 listbox_callback
, listbox_event
);
2437 l
->list
= l
->top
= l
->current
= 0;
2440 l
->cback
= callback
;
2441 l
->allow_duplicates
= 1;
2442 l
->scrollbar
= !tty_is_slow ();
2443 widget_want_hotkey (l
->widget
, 1);
2448 /* Listbox item adding function. They still lack a lot of functionality */
2450 /* 1.11.96 bor: added pos argument to control placement of new entry */
2452 listbox_append_item (WListbox
*l
, WLEntry
*e
, enum append_pos pos
)
2460 } else if (pos
== LISTBOX_APPEND_AT_END
) {
2462 e
->prev
= l
->list
->prev
;
2463 l
->list
->prev
->next
= e
;
2465 } else if (pos
== LISTBOX_APPEND_BEFORE
){
2466 e
->next
= l
->current
;
2467 e
->prev
= l
->current
->prev
;
2468 l
->current
->prev
->next
= e
;
2469 l
->current
->prev
= e
;
2470 if (l
->list
== l
->current
) { /* move list one position down */
2474 } else if (pos
== LISTBOX_APPEND_AFTER
) {
2475 e
->prev
= l
->current
;
2476 e
->next
= l
->current
->next
;
2477 l
->current
->next
->prev
= e
;
2478 l
->current
->next
= e
;
2479 } else if (pos
== LISTBOX_APPEND_SORTED
) {
2480 WLEntry
*w
= l
->list
;
2482 while (w
->next
!= l
->list
&& strcmp (e
->text
, w
->text
) > 0)
2484 if (w
->next
== l
->list
) {
2500 listbox_add_item (WListbox
*l
, enum append_pos pos
, int hotkey
,
2501 const char *text
, void *data
)
2508 if (!l
->allow_duplicates
)
2509 if (listbox_search_text (l
, text
))
2512 entry
= g_new (WLEntry
, 1);
2513 entry
->text
= g_strdup (text
);
2515 entry
->hotkey
= hotkey
;
2517 listbox_append_item (l
, entry
, pos
);
2522 /* Selects the nth entry in the listbox */
2524 listbox_select_by_number (WListbox
*l
, int n
)
2526 if (l
->list
!= NULL
)
2527 listbox_select_entry (l
, listbox_select_pos (l
, l
->list
, n
));
2531 listbox_search_text (WListbox
*l
, const char *text
)
2540 if(!strcmp (e
->text
, text
))
2543 } while (e
!=l
->list
);
2548 /* Returns the current string text as well as the associated extra data */
2550 listbox_get_current (WListbox
*l
, char **string
, char **extra
)
2556 if (string
&& l
->current
)
2557 *string
= l
->current
->text
;
2558 if (extra
&& l
->current
)
2559 *extra
= l
->current
->data
;
2562 /* returns TRUE if a function has been called, FALSE otherwise. */
2564 buttonbar_call (WButtonBar
*bb
, int i
)
2566 switch (bb
->labels
[i
].tag
) {
2570 bb
->labels
[i
].u
.fn_void ();
2573 bb
->labels
[i
].u
.fn_ptr (bb
->labels
[i
].data
);
2579 /* calculate width of one button, width is never lesser than 7 */
2581 buttonbat_get_button_width ()
2583 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2584 return (result
>= 7) ? result
: 7;
2589 buttonbar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2591 WButtonBar
*bb
= (WButtonBar
*) w
;
2597 return MSG_NOT_HANDLED
;
2600 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2601 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2603 return MSG_NOT_HANDLED
;
2607 widget_move (&bb
->widget
, 0, 0);
2608 tty_setcolor (DEFAULT_COLOR
);
2609 bb
->btn_width
= buttonbat_get_button_width ();
2610 tty_printf ("%-*s", bb
->widget
.cols
, "");
2612 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++) {
2613 widget_move (&bb
->widget
, 0, i
* bb
->btn_width
);
2614 tty_setcolor (DEFAULT_COLOR
);
2615 tty_printf ("%2d", i
+ 1);
2616 tty_setcolor (SELECTED_COLOR
);
2617 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2618 tty_print_string (str_fit_to_term (text
, bb
->btn_width
- 2, J_CENTER_LEFT
));
2623 case WIDGET_DESTROY
:
2624 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2625 g_free (bb
->labels
[i
].text
);
2629 return default_proc (msg
, parm
);
2634 buttonbar_event (Gpm_Event
*event
, void *data
)
2636 WButtonBar
*bb
= data
;
2639 if (!(event
->type
& GPM_UP
))
2643 button
= (event
->x
- 1) / bb
->btn_width
;
2644 if (button
< BUTTONBAR_LABELS_NUM
)
2645 buttonbar_call (bb
, button
);
2650 buttonbar_new (int visible
)
2655 bb
= g_new0 (WButtonBar
, 1);
2657 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
,
2658 buttonbar_callback
, buttonbar_event
);
2659 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2660 bb
->visible
= visible
;
2661 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++){
2662 bb
->labels
[i
].text
= NULL
;
2663 bb
->labels
[i
].tag
= BBFUNC_NONE
;
2665 widget_want_hotkey (bb
->widget
, 1);
2666 widget_want_cursor (bb
->widget
, 0);
2667 bb
->btn_width
= buttonbat_get_button_width ();
2673 set_label_text (WButtonBar
* bb
, int index
, const char *text
)
2675 g_free (bb
->labels
[index
- 1].text
);
2677 bb
->labels
[index
- 1].text
= g_strdup (text
);
2680 /* Find ButtonBar widget in the dialog */
2682 find_buttonbar (Dlg_head
*h
)
2686 bb
= (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2691 buttonbar_clear_label (Dlg_head
*h
, int idx
)
2693 WButtonBar
*bb
= find_buttonbar (h
);
2698 set_label_text (bb
, idx
, "");
2699 bb
->labels
[idx
- 1].tag
= BBFUNC_NONE
;
2703 buttonbar_set_label_data (Dlg_head
*h
, int idx
, const char *text
,
2704 buttonbarfn cback
, void *data
)
2706 WButtonBar
*bb
= find_buttonbar (h
);
2711 assert (cback
!= (buttonbarfn
) 0);
2712 set_label_text (bb
, idx
, text
);
2713 bb
->labels
[idx
- 1].tag
= BBFUNC_PTR
;
2714 bb
->labels
[idx
- 1].u
.fn_ptr
= cback
;
2715 bb
->labels
[idx
- 1].data
= data
;
2719 buttonbar_set_label (Dlg_head
*h
, int idx
, const char *text
, voidfn cback
)
2721 WButtonBar
*bb
= find_buttonbar (h
);
2726 assert (cback
!= (voidfn
) 0);
2727 set_label_text (bb
, idx
, text
);
2728 bb
->labels
[idx
- 1].tag
= BBFUNC_VOID
;
2729 bb
->labels
[idx
- 1].u
.fn_void
= cback
;
2733 buttonbar_set_visible (WButtonBar
*bb
, gboolean visible
)
2735 bb
->visible
= visible
;
2739 buttonbar_redraw (Dlg_head
*h
)
2741 WButtonBar
*bb
= find_buttonbar (h
);
2746 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
2750 groupbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2752 WGroupbox
*g
= (WGroupbox
*) w
;
2759 return MSG_NOT_HANDLED
;
2762 tty_setcolor (COLOR_NORMAL
);
2763 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2764 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
,
2767 tty_setcolor (COLOR_HOT_NORMAL
);
2768 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2769 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
2770 tty_print_string (g
->title
);
2773 case WIDGET_DESTROY
:
2778 return default_proc (msg
, parm
);
2783 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
2785 WGroupbox
*g
= g_new (WGroupbox
, 1);
2787 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
2789 g
->widget
.options
&= ~W_WANT_CURSOR
;
2790 widget_want_hotkey (g
->widget
, 0);
2792 /* Strip existing spaces, add one space before and after the title */
2795 t
= g_strstrip (g_strdup (title
));
2796 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);