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_keymap_t */
61 const global_keymap_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
;
83 /* search for '&', that is not on the of text */
84 cp
= strchr (text
, '&');
85 if (cp
!= NULL
&& cp
[1] != '\0') {
86 result
.start
= g_strndup (text
, cp
- text
);
90 p
= str_cget_next_char (cp
);
91 result
.hotkey
= g_strndup (cp
, p
- cp
);
94 result
.end
= g_strdup (cp
);
96 result
.start
= g_strdup (text
);
104 release_hotkey (const struct hotkey_t hotkey
)
106 g_free (hotkey
.start
);
107 g_free (hotkey
.hotkey
);
112 hotkey_width (const struct hotkey_t hotkey
)
116 result
= str_term_width1 (hotkey
.start
);
117 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
118 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
123 draw_hotkey (Widget
*w
, const struct hotkey_t hotkey
, gboolean focused
)
125 widget_selectcolor (w
, focused
, FALSE
);
126 tty_print_string (hotkey
.start
);
128 if (hotkey
.hotkey
!= NULL
) {
129 widget_selectcolor (w
, focused
, TRUE
);
130 tty_print_string (hotkey
.hotkey
);
131 widget_selectcolor (w
, focused
, FALSE
);
134 if (hotkey
.end
!= NULL
)
135 tty_print_string (hotkey
.end
);
139 /* Default callback for widgets */
141 default_proc (widget_msg_t msg
, int parm
)
156 return MSG_NOT_HANDLED
;
160 static int button_event (Gpm_Event
*event
, void *);
165 button_callback (Widget
*w
, widget_msg_t msg
, int parm
)
167 WButton
*b
= (WButton
*) w
;
170 Dlg_head
*h
= b
->widget
.parent
;
175 * Don't let the default button steal Enter from the current
176 * button. This is a workaround for the flawed event model
177 * when hotkeys are sent to all widgets before the key is
178 * handled by the current widget.
180 if (parm
== '\n' && h
->current
== &b
->widget
) {
181 button_callback (w
, WIDGET_KEY
, ' ');
185 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
) {
186 button_callback (w
, WIDGET_KEY
, ' ');
190 if (b
->text
.hotkey
!= NULL
) {
191 if (g_ascii_tolower ((gchar
)b
->text
.hotkey
[0]) ==
192 g_ascii_tolower ((gchar
)parm
)) {
193 button_callback (w
, WIDGET_KEY
, ' ');
197 return MSG_NOT_HANDLED
;
200 if (parm
!= ' ' && parm
!= '\n')
201 return MSG_NOT_HANDLED
;
204 stop
= (*b
->callback
) (b
->action
);
205 if (!b
->callback
|| stop
) {
206 h
->ret_value
= b
->action
;
227 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
233 if (msg
== WIDGET_UNFOCUS
)
235 else if (msg
== WIDGET_FOCUS
)
238 widget_selectcolor (w
, b
->selected
, FALSE
);
239 widget_move (w
, 0, 0);
243 tty_print_string ("[< ");
246 tty_print_string ("[ ");
249 tty_print_string ("[");
256 draw_hotkey (w
, b
->text
, b
->selected
);
260 tty_print_string (" >]");
263 tty_print_string (" ]");
266 tty_print_string ("]");
272 release_hotkey (b
->text
);
276 return default_proc (msg
, parm
);
281 button_event (Gpm_Event
*event
, void *data
)
285 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
286 Dlg_head
*h
=b
->widget
.parent
;
287 dlg_select_widget (b
);
288 if (event
->type
& GPM_UP
){
289 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
290 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
298 button_get_len (const WButton
*b
)
300 int ret
= hotkey_width (b
->text
);
319 button_new (int y
, int x
, int action
, int flags
, const char *text
,
322 WButton
*b
= g_new (WButton
, 1);
326 b
->text
= parse_hotkey (text
);
328 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
),
329 button_callback
, button_event
);
332 b
->callback
= callback
;
333 widget_want_hotkey (b
->widget
, 1);
334 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
340 button_get_text (const WButton
*b
)
342 if (b
->text
.hotkey
!= NULL
)
343 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
,
346 return g_strdup (b
->text
.start
);
350 button_set_text (WButton
*b
, const char *text
)
352 release_hotkey (b
->text
);
353 b
->text
= parse_hotkey (text
);
354 b
->widget
.cols
= button_get_len (b
);
355 dlg_redraw (b
->widget
.parent
);
359 /* Radio button widget */
360 static int radio_event (Gpm_Event
*event
, void *);
363 radio_callback (Widget
*w
, widget_msg_t msg
, int parm
)
365 WRadio
*r
= (WRadio
*) w
;
367 Dlg_head
*h
= r
->widget
.parent
;
372 int i
, lp
= g_ascii_tolower ((gchar
)parm
);
374 for (i
= 0; i
< r
->count
; i
++) {
375 if (r
->texts
[i
].hotkey
!= NULL
) {
376 int c
= g_ascii_tolower ((gchar
)r
->texts
[i
].hotkey
[0]);
383 radio_callback (w
, WIDGET_KEY
, ' ');
388 return MSG_NOT_HANDLED
;
394 (*h
->callback
) (h
, DLG_ACTION
, 0);
395 radio_callback (w
, WIDGET_FOCUS
, ' ');
404 return MSG_NOT_HANDLED
;
408 if (r
->count
- 1 > r
->pos
) {
413 return MSG_NOT_HANDLED
;
416 (*h
->callback
) (h
, DLG_ACTION
, 0);
417 radio_callback (w
, WIDGET_FOCUS
, ' ');
418 widget_move (&r
->widget
, r
->pos
, 1);
424 for (i
= 0; i
< r
->count
; i
++) {
425 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
426 widget_selectcolor (w
, focused
, FALSE
);
427 widget_move (&r
->widget
, i
, 0);
428 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
429 draw_hotkey (w
, r
->texts
[i
], focused
);
434 for (i
= 0; i
< r
->count
; i
++) {
435 release_hotkey (r
->texts
[i
]);
441 return default_proc (msg
, parm
);
446 radio_event (Gpm_Event
*event
, void *data
)
451 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
452 Dlg_head
*h
= r
->widget
.parent
;
454 r
->pos
= event
->y
- 1;
455 dlg_select_widget (r
);
456 if (event
->type
& GPM_UP
){
457 radio_callback (w
, WIDGET_KEY
, ' ');
458 radio_callback (w
, WIDGET_FOCUS
, 0);
459 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
467 radio_new (int y
, int x
, int count
, const char **texts
)
469 WRadio
*result
= g_new (WRadio
, 1);
472 /* Compute the longest string */
473 result
->texts
= g_new (struct hotkey_t
, count
);
476 for (i
= 0; i
< count
; i
++){
477 result
->texts
[i
] = parse_hotkey (texts
[i
]);
478 m
= hotkey_width (result
->texts
[i
]);
483 init_widget (&result
->widget
, y
, x
, count
, max
, radio_callback
, radio_event
);
487 result
->count
= count
;
488 widget_want_hotkey (result
->widget
, 1);
494 /* Checkbutton widget */
496 static int check_event (Gpm_Event
*event
, void *);
499 check_callback (Widget
*w
, widget_msg_t msg
, int parm
)
501 WCheck
*c
= (WCheck
*) w
;
502 Dlg_head
*h
= c
->widget
.parent
;
506 if (c
->text
.hotkey
!= NULL
) {
507 if (g_ascii_tolower ((gchar
)c
->text
.hotkey
[0]) ==
508 g_ascii_tolower ((gchar
)parm
)) {
510 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
514 return MSG_NOT_HANDLED
;
518 return MSG_NOT_HANDLED
;
520 c
->state
^= C_CHANGE
;
521 (*h
->callback
) (h
, DLG_ACTION
, 0);
522 check_callback (w
, WIDGET_FOCUS
, ' ');
526 widget_move (&c
->widget
, 0, 1);
532 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
533 widget_move (&c
->widget
, 0, 0);
534 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
535 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
539 release_hotkey (c
->text
);
543 return default_proc (msg
, parm
);
548 check_event (Gpm_Event
*event
, void *data
)
553 if (event
->type
& (GPM_DOWN
|GPM_UP
)){
554 Dlg_head
*h
= c
->widget
.parent
;
556 dlg_select_widget (c
);
557 if (event
->type
& GPM_UP
){
558 check_callback (w
, WIDGET_KEY
, ' ');
559 check_callback (w
, WIDGET_FOCUS
, 0);
560 (*h
->callback
) (h
, DLG_POST_KEY
, ' ');
568 check_new (int y
, int x
, int state
, const char *text
)
570 WCheck
*c
= g_new (WCheck
, 1);
572 c
->text
= parse_hotkey (text
);
574 init_widget (&c
->widget
, y
, x
, 1, hotkey_width (c
->text
),
575 check_callback
, check_event
);
576 c
->state
= state
? C_BOOL
: 0;
577 widget_want_hotkey (c
->widget
, 1);
586 label_callback (Widget
*w
, widget_msg_t msg
, int parm
)
588 WLabel
*l
= (WLabel
*) w
;
589 Dlg_head
*h
= l
->widget
.parent
;
595 /* We don't want to get the focus */
597 return MSG_NOT_HANDLED
;
601 char *p
= l
->text
, *q
, c
= 0;
608 tty_setcolor (DEFAULT_COLOR
);
610 tty_setcolor (DLG_NORMALC (h
));
613 q
= strchr (p
, '\n');
619 widget_move (&l
->widget
, y
, 0);
620 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
636 return default_proc (msg
, parm
);
641 label_set_text (WLabel
*label
, const char *text
)
643 int newcols
= label
->widget
.cols
;
646 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
647 return; /* Flickering is not nice */
649 g_free (label
->text
);
652 label
->text
= g_strdup (text
);
653 if (label
->auto_adjust_cols
) {
654 str_msg_term_size (text
, &newlines
, &newcols
);
655 if (newcols
> label
->widget
.cols
)
656 label
->widget
.cols
= newcols
;
657 if (newlines
> label
->widget
.lines
)
658 label
->widget
.lines
= newlines
;
660 } else label
->text
= NULL
;
662 if (label
->widget
.parent
)
663 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
665 if (newcols
< label
->widget
.cols
)
666 label
->widget
.cols
= newcols
;
670 label_new (int y
, int x
, const char *text
)
677 str_msg_term_size (text
, &lines
, &cols
);
679 l
= g_new (WLabel
, 1);
680 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
681 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
682 l
->auto_adjust_cols
= 1;
684 widget_want_cursor (l
->widget
, 0);
689 /* Gauge widget (progress indicator) */
690 /* Currently width is hardcoded here for text mode */
694 gauge_callback (Widget
*w
, widget_msg_t msg
, int parm
)
696 WGauge
*g
= (WGauge
*) w
;
697 Dlg_head
*h
= g
->widget
.parent
;
699 if (msg
== WIDGET_INIT
)
702 /* We don't want to get the focus */
703 if (msg
== WIDGET_FOCUS
)
704 return MSG_NOT_HANDLED
;
706 if (msg
== WIDGET_DRAW
){
707 widget_move (&g
->widget
, 0, 0);
708 tty_setcolor (DLG_NORMALC (h
));
710 tty_printf ("%*s", gauge_len
, "");
712 int percentage
, columns
;
713 long total
= g
->max
, done
= g
->current
;
715 if (total
<= 0 || done
< 0) {
721 while (total
> 65535) {
725 percentage
= (200 * done
/ total
+ 1) / 2;
726 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
727 tty_print_char ('[');
728 tty_setcolor (GAUGE_COLOR
);
729 tty_printf ("%*s", (int) columns
, "");
730 tty_setcolor (DLG_NORMALC (h
));
731 tty_printf ("%*s] %3d%%", (int)(gauge_len
- 7 - columns
), "", (int) percentage
);
736 return default_proc (msg
, parm
);
740 gauge_set_value (WGauge
*g
, int max
, int current
)
742 if (g
->current
== current
&& g
->max
== max
)
743 return; /* Do not flicker */
745 max
= 1; /* I do not like division by zero :) */
747 g
->current
= current
;
749 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
753 gauge_show (WGauge
*g
, int shown
)
755 if (g
->shown
== shown
)
758 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
762 gauge_new (int y
, int x
, int shown
, int max
, int current
)
764 WGauge
*g
= g_new (WGauge
, 1);
766 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
769 max
= 1; /* I do not like division by zero :) */
771 g
->current
= current
;
772 widget_want_cursor (g
->widget
, 0);
779 /* {{{ history button */
781 #define LARGE_HISTORY_BUTTON 1
783 #ifdef LARGE_HISTORY_BUTTON
784 # define HISTORY_BUTTON_WIDTH 3
786 # define HISTORY_BUTTON_WIDTH 1
789 #define should_show_history_button(in) \
790 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
792 static void draw_history_button (WInput
* in
)
795 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
796 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
797 #ifdef LARGE_HISTORY_BUTTON
800 h
= in
->widget
.parent
;
801 tty_setcolor (NORMAL_COLOR
);
802 tty_print_string ("[ ]");
803 /* Too distracting: tty_setcolor (MARKED_COLOR); */
804 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
808 tty_setcolor (MARKED_COLOR
);
813 /* }}} history button */
816 /* Input widgets now have a global kill ring */
817 /* Pointer to killed data */
818 static char *kill_buffer
= 0;
821 update_input (WInput
*in
, int clear_first
)
825 int buf_len
= str_length (in
->buffer
);
829 if (should_show_history_button (in
))
830 has_history
= HISTORY_BUTTON_WIDTH
;
832 if (in
->disable_update
)
835 pw
= str_term_width2 (in
->buffer
, in
->point
);
837 /* Make the point visible */
838 if ((pw
< in
->term_first_shown
) ||
839 (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
)) {
841 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
842 if (in
->term_first_shown
< 0)
843 in
->term_first_shown
= 0;
846 /* Adjust the mark */
847 if (in
->mark
> buf_len
)
851 draw_history_button (in
);
853 tty_setcolor (in
->color
);
855 widget_move (&in
->widget
, 0, 0);
857 if (!in
->is_password
) {
858 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
859 in
->field_width
- has_history
));
862 for (i
= -in
->term_first_shown
; i
< in
->field_width
- has_history
; i
++){
864 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
866 if (cp
[0] != '\0') str_cnext_char (&cp
);
875 winput_set_origin (WInput
*in
, int x
, int field_width
)
878 in
->field_width
= in
->widget
.cols
= field_width
;
879 update_input (in
, 0);
882 /* {{{ history saving and loading */
884 int num_history_items_recorded
= 60;
887 This loads and saves the history of an input line to and from the
888 widget. It is called with the widgets history name on creation of the
889 widget, and returns the GList list. It stores histories in the file
890 ~/.mc/history in using the profile code.
892 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
893 function) then input_new assigns the default text to be the last text
894 entered, or "" if not found.
898 history_get (const char *input_name
)
908 if (!num_history_items_recorded
) /* this is how to disable */
910 if (!input_name
|| !*input_name
)
913 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
914 cfg
= mc_config_init (profile
);
916 /* get number of keys */
917 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
920 for (i
= 0; i
< keys_num
; i
++) {
921 char key_name
[BUF_TINY
];
922 g_snprintf (key_name
, sizeof (key_name
), "%lu", (unsigned long)i
);
923 this_entry
= mc_config_get_string (cfg
, input_name
, key_name
, "");
925 if (this_entry
&& *this_entry
)
926 hist
= list_append_unique (hist
, this_entry
);
929 mc_config_deinit (cfg
);
932 /* return pointer to the last entry in the list */
933 return g_list_last (hist
);
937 history_put (const char *input_name
, GList
*h
)
952 if (!num_history_items_recorded
) /* this is how to disable */
955 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
957 if ((i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) != -1)
960 /* Make sure the history is only readable by the user */
961 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
) {
966 /* go to end of list */
969 /* go back 60 places */
970 for (i
= 0; i
< num_history_items_recorded
- 1 && h
->prev
; i
++)
971 h
= g_list_previous (h
);
973 cfg
= mc_config_init(profile
);
976 mc_config_del_group(cfg
,input_name
);
978 /* dump histories into profile */
979 for (i
= 0; h
; h
= g_list_next (h
)) {
982 text
= (char *) h
->data
;
984 /* We shouldn't have null entries, but let's be sure */
986 char key_name
[BUF_TINY
];
987 g_snprintf (key_name
, sizeof (key_name
), "%d", i
++);
988 mc_config_set_string(cfg
,input_name
, key_name
, text
);
992 mc_config_save_file (cfg
, NULL
);
993 mc_config_deinit(cfg
);
997 /* }}} history saving and loading */
1000 /* {{{ history display */
1005 return _(" History ");
1009 listbox_fwd (WListbox
*l
)
1011 if (l
->current
!= l
->list
->prev
)
1012 listbox_select_entry (l
, l
->current
->next
);
1014 listbox_select_first (l
);
1024 dlg_hist_reposition (Dlg_head
*dlg_head
)
1026 dlg_hist_data
*data
;
1027 int x
= 0, y
, he
, wi
;
1030 if ((dlg_head
== NULL
)
1031 || (dlg_head
->data
== NULL
))
1032 return MSG_NOT_HANDLED
;
1034 data
= (dlg_hist_data
*) dlg_head
->data
;
1036 y
= data
->widget
->y
;
1037 he
= data
->count
+ 2;
1039 if (he
<= y
|| y
> (LINES
- 6)) {
1040 he
= min (he
, y
- 1);
1044 he
= min (he
, LINES
- y
);
1047 if (data
->widget
->x
> 2)
1048 x
= data
->widget
->x
- 2;
1050 wi
= data
->maxlen
+ 4;
1052 if ((wi
+ x
) > COLS
) {
1053 wi
= min (wi
, COLS
);
1057 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1063 dlg_hist_callback (Dlg_head
*h
, dlg_msg_t msg
, int parm
)
1067 return dlg_hist_reposition (h
);
1070 return default_dlg_callback (h
, msg
, parm
);
1075 show_hist (GList
*history
, Widget
*widget
)
1078 size_t maxlen
, i
, count
= 0;
1080 Dlg_head
*query_dlg
;
1081 WListbox
*query_list
;
1082 dlg_hist_data hist_data
;
1084 if (history
== NULL
)
1087 maxlen
= str_term_width1 (i18n_htitle ());
1089 z
= g_list_first (history
);
1092 i
= str_term_width1 ((char *) hi
->data
);
1093 maxlen
= max (maxlen
, i
);
1095 hi
= g_list_next (hi
);
1098 hist_data
.maxlen
= maxlen
;
1099 hist_data
.widget
= widget
;
1100 hist_data
.count
= count
;
1103 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1104 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1105 query_dlg
->data
= &hist_data
;
1107 query_list
= listbox_new (1, 1, 2, 2, NULL
);
1109 /* this call makes list stick to all sides of dialog, effectively make
1110 it be resized with dialog */
1111 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1113 /* to avoid diplicating of (calculating sizes in two places)
1114 code, call dlg_hist_callback function here, to set dialog and
1116 The main idea - create 4x4 dialog and add 2x2 list in
1117 center of it, and let dialog function resize it to needed
1119 dlg_hist_callback (query_dlg
, DLG_RESIZE
, 0);
1121 if (query_dlg
->y
< widget
->y
) {
1125 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1126 0, (char *) hi
->data
, NULL
);
1127 hi
= g_list_next (hi
);
1129 listbox_select_last (query_list
);
1131 /* traverse backwards */
1132 hi
= g_list_last (history
);
1134 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1135 0, (char *) hi
->data
, NULL
);
1136 hi
= g_list_previous (hi
);
1140 if (run_dlg (query_dlg
) != B_CANCEL
) {
1141 listbox_get_current (query_list
, &q
, NULL
);
1145 destroy_dlg (query_dlg
);
1150 do_show_hist (WInput
*in
)
1153 r
= show_hist (in
->history
, &in
->widget
);
1155 assign_text (in
, r
);
1160 /* }}} history display */
1163 input_destroy (WInput
*in
)
1166 fprintf (stderr
, "Internal error: null Input *\n");
1173 if (!in
->is_password
) /* don't save passwords ;-) */
1174 history_put (in
->history_name
, in
->history
);
1176 in
->history
= g_list_first (in
->history
);
1177 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1178 g_list_free (in
->history
);
1181 g_free (in
->buffer
);
1182 free_completions (in
);
1183 g_free (in
->history_name
);
1187 input_disable_update (WInput
*in
)
1189 in
->disable_update
++;
1193 input_enable_update (WInput
*in
)
1195 in
->disable_update
--;
1196 update_input (in
, 0);
1199 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1202 push_history (WInput
*in
, const char *text
)
1205 /* input widget where urls with passwords are entered without any
1207 static const char *password_input_fields
[] = {
1208 N_(" Link to a remote machine "),
1209 N_(" FTP to machine "),
1210 N_(" SMB link to machine ")
1218 for (i
= 0; i
< ELEMENTS (password_input_fields
); i
++)
1219 password_input_fields
[i
] = _(password_input_fields
[i
]);
1222 for (p
= text
; *p
== ' ' || *p
== '\t'; p
++);
1227 /* Avoid duplicated entries */
1228 in
->history
= g_list_last (in
->history
);
1229 if (!strcmp ((char *) in
->history
->data
, text
))
1233 t
= g_strdup (text
);
1235 if (in
->history_name
) {
1236 p
= in
->history_name
+ 3;
1237 for (i
= 0; i
< ELEMENTS (password_input_fields
); i
++)
1238 if (strcmp (p
, password_input_fields
[i
]) == 0)
1240 if (i
< ELEMENTS (password_input_fields
))
1241 strip_password (t
, 0);
1243 strip_password (t
, 1);
1246 in
->history
= list_append_unique (in
->history
, t
);
1254 /* Cleans the input line and adds the current text to the history */
1256 new_input (WInput
*in
)
1259 push_history (in
, in
->buffer
);
1261 in
->buffer
[0] = '\0';
1265 free_completions (in
);
1266 update_input (in
, 0);
1270 move_buffer_backward (WInput
*in
, int start
, int end
)
1273 int str_len
= str_length (in
->buffer
);
1274 if (start
>= str_len
|| end
> str_len
+ 1) return;
1276 pos
= str_offset_to_pos (in
->buffer
, start
);
1277 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1279 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1280 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1284 insert_char (WInput
*in
, int c_code
)
1290 return MSG_NOT_HANDLED
;
1292 if (in
->charpoint
>= MB_LEN_MAX
)
1295 in
->charbuf
[in
->charpoint
] = c_code
;
1298 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1301 in
->charpoint
= 0; /* broken multibyte char, skip */
1306 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
){
1307 /* Expand the buffer */
1308 size_t new_length
= in
->current_max_size
+
1309 in
->field_width
+ in
->charpoint
;
1310 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1313 in
->current_max_size
= new_length
;
1317 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
) {
1318 /* bytes from begin */
1319 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1321 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1323 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1324 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] =
1325 in
->buffer
[ins_point
+ i
- 1];
1327 memcpy(in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1336 beginning_of_line (WInput
*in
)
1343 end_of_line (WInput
*in
)
1345 in
->point
= str_length (in
->buffer
);
1350 backward_char (WInput
*in
)
1352 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1354 if (in
->point
> 0) {
1355 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1361 forward_char (WInput
*in
)
1363 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1364 if (act
[0] != '\0') {
1365 in
->point
+= str_cnext_noncomb_char (&act
);
1371 forward_word (WInput
* in
)
1373 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1375 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
))) {
1376 str_cnext_char (&p
);
1379 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
)) {
1380 str_cnext_char (&p
);
1386 backward_word (WInput
*in
)
1392 p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1393 (p
!= in
->buffer
) && (p
[0] == '\0');
1397 while (p
!= in
->buffer
) {
1399 str_cprev_char (&p
);
1400 if (!str_isspace (p
) && !str_ispunct (p
)) {
1406 while (p
!= in
->buffer
) {
1407 str_cprev_char (&p
);
1408 if (str_isspace (p
) || str_ispunct (p
))
1416 key_left (WInput
*in
)
1422 key_ctrl_left (WInput
*in
)
1428 key_right (WInput
*in
)
1434 key_ctrl_right (WInput
*in
)
1439 backward_delete (WInput
*in
)
1441 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1447 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1448 move_buffer_backward(in
, start
, in
->point
);
1455 delete_char (WInput
*in
)
1457 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1458 int end
= in
->point
;
1460 end
+= str_cnext_noncomb_char (&act
);
1462 move_buffer_backward(in
, in
->point
, end
);
1468 copy_region (WInput
*in
, int x_first
, int x_last
)
1470 int first
= min (x_first
, x_last
);
1471 int last
= max (x_first
, x_last
);
1476 g_free (kill_buffer
);
1478 first
= str_offset_to_pos (in
->buffer
, first
);
1479 last
= str_offset_to_pos (in
->buffer
, last
);
1481 kill_buffer
= g_strndup(in
->buffer
+ first
, last
- first
);
1485 delete_region (WInput
*in
, int x_first
, int x_last
)
1487 int first
= min (x_first
, x_last
);
1488 int last
= max (x_first
, x_last
);
1493 last
= str_offset_to_pos (in
->buffer
, last
);
1494 first
= str_offset_to_pos (in
->buffer
, first
);
1495 len
= strlen (&in
->buffer
[last
]) + 1;
1496 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1502 kill_word (WInput
*in
)
1504 int old_point
= in
->point
;
1508 new_point
= in
->point
;
1509 in
->point
= old_point
;
1511 copy_region (in
, old_point
, new_point
);
1512 delete_region (in
, old_point
, new_point
);
1519 back_kill_word (WInput
*in
)
1521 int old_point
= in
->point
;
1525 new_point
= in
->point
;
1526 in
->point
= old_point
;
1528 copy_region (in
, old_point
, new_point
);
1529 delete_region (in
, old_point
, new_point
);
1534 set_mark (WInput
*in
)
1536 in
->mark
= in
->point
;
1540 kill_save (WInput
*in
)
1542 copy_region (in
, in
->mark
, in
->point
);
1546 kill_region (WInput
*in
)
1549 delete_region (in
, in
->point
, in
->mark
);
1560 for (p
= kill_buffer
; *p
; p
++)
1561 insert_char (in
, *p
);
1566 kill_line (WInput
*in
)
1568 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1569 g_free (kill_buffer
);
1570 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1571 in
->buffer
[chp
] = '\0';
1576 assign_text (WInput
*in
, const char *text
)
1578 free_completions (in
);
1579 g_free (in
->buffer
);
1580 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1581 in
->current_max_size
= strlen (in
->buffer
) + 1;
1582 in
->point
= str_length (in
->buffer
);
1589 hist_prev (WInput
*in
)
1594 if (in
->need_push
) {
1595 switch (push_history (in
, in
->buffer
)) {
1597 in
->history
= g_list_previous (in
->history
);
1600 if (in
->history
->prev
)
1601 in
->history
= g_list_previous (in
->history
);
1606 } else if (in
->history
->prev
)
1607 in
->history
= g_list_previous (in
->history
);
1610 assign_text (in
, (char *) in
->history
->data
);
1615 hist_next (WInput
*in
)
1617 if (in
->need_push
) {
1618 switch (push_history (in
, in
->buffer
)) {
1620 assign_text (in
, "");
1630 if (!in
->history
->next
) {
1631 assign_text (in
, "");
1635 in
->history
= g_list_next (in
->history
);
1636 assign_text (in
, (char *) in
->history
->data
);
1641 port_region_marked_for_delete (WInput
*in
)
1643 in
->buffer
[0] = '\0';
1650 input_execute_cmd (WInput
*in
, int command
)
1652 cb_ret_t res
= MSG_HANDLED
;
1656 beginning_of_line (in
);
1661 case CK_InputMoveLeft
:
1664 case CK_InputWordLeft
:
1667 case CK_InputMoveRight
:
1670 case CK_InputWordRight
:
1671 key_ctrl_right (in
);
1673 case CK_InputBackwardChar
:
1676 case CK_InputBackwardWord
:
1679 case CK_InputForwardChar
:
1682 case CK_InputForwardWord
:
1685 case CK_InputBackwardDelete
:
1686 backward_delete (in
);
1688 case CK_InputDeleteChar
:
1691 case CK_InputKillWord
:
1694 case CK_InputBackwardKillWord
:
1695 back_kill_word (in
);
1697 case CK_InputSetMark
:
1700 case CK_InputKillRegion
:
1703 case CK_InputKillSave
:
1709 case CK_InputKillLine
:
1712 case CK_InputHistoryPrev
:
1715 case CK_InputHistoryNext
:
1718 case CK_InputHistoryShow
:
1721 case CK_InputComplete
:
1725 res
= MSG_NOT_HANDLED
;
1731 /* This function is a test for a special input key used in complete.c */
1732 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1733 and 2 if it is a complete key */
1735 is_in_input_map (WInput
*in
, int key
)
1739 for (i
= 0; input_map
[i
].key
; i
++) {
1740 if (key
== input_map
[i
].key
) {
1741 input_execute_cmd (in
, input_map
[i
].command
);
1742 if (input_map
[i
].command
== CK_InputComplete
)
1752 handle_char (WInput
*in
, int key
)
1757 v
= MSG_NOT_HANDLED
;
1760 free_completions (in
);
1761 v
= insert_char (in
, key
);
1762 update_input (in
, 1);
1767 for (i
= 0; input_map
[i
].key
; i
++) {
1768 if (key
== input_map
[i
].key
) {
1769 if (input_map
[i
].command
!= CK_InputComplete
)
1770 free_completions (in
);
1771 input_execute_cmd (in
, input_map
[i
].command
);
1772 update_input (in
, 1);
1777 if (input_map
[i
].command
== 0) {
1779 return MSG_NOT_HANDLED
;
1781 port_region_marked_for_delete (in
);
1782 free_completions (in
);
1783 v
= insert_char (in
, key
);
1785 update_input (in
, 1);
1789 /* Inserts text in input line */
1791 stuff (WInput
*in
, const char *text
, int insert_extra_space
)
1793 input_disable_update (in
);
1795 handle_char (in
, *text
++);
1796 if (insert_extra_space
)
1797 handle_char (in
, ' ');
1798 input_enable_update (in
);
1799 update_input (in
, 1);
1803 input_set_point (WInput
*in
, int pos
)
1805 int max_pos
= str_length (in
->buffer
);
1809 if (pos
!= in
->point
)
1810 free_completions (in
);
1813 update_input (in
, 1);
1817 input_callback (Widget
*w
, widget_msg_t msg
, int parm
)
1819 WInput
*in
= (WInput
*) w
;
1824 if (parm
== XCTRL ('q')) {
1826 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
1831 /* Keys we want others to handle */
1832 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
1833 || parm
== KEY_F (10) || parm
== XCTRL ('g') || parm
== '\n')
1834 return MSG_NOT_HANDLED
;
1836 /* When pasting multiline text, insert literal Enter */
1837 if ((parm
& ~KEY_M_MASK
) == '\n') {
1839 v
= handle_char (in
, '\n');
1844 return handle_char (in
, parm
);
1846 case WIDGET_COMMAND
:
1847 return input_execute_cmd (in
, parm
);
1850 case WIDGET_UNFOCUS
:
1852 update_input (in
, 0);
1856 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
1857 - in
->term_first_shown
);
1860 case WIDGET_DESTROY
:
1865 return default_proc (msg
, parm
);
1870 input_event (Gpm_Event
* event
, void *data
)
1874 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
1875 dlg_select_widget (in
);
1877 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
1878 && should_show_history_button (in
)) {
1881 in
->point
= str_length (in
->buffer
);
1882 if (event
->x
+ in
->term_first_shown
- 1 <
1883 str_term_width1 (in
->buffer
))
1885 in
->point
= str_column_to_pos (in
->buffer
, event
->x
1886 + in
->term_first_shown
- 1);
1889 update_input (in
, 1);
1895 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
1896 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
1898 WInput
*in
= g_new (WInput
, 1);
1899 int initial_buffer_len
;
1901 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
1905 in
->history_name
= 0;
1908 in
->history_name
= g_strdup (histname
);
1909 in
->history
= history_get (histname
);
1913 if (def_text
== NULL
)
1916 if (def_text
== INPUT_LAST_TEXT
) {
1919 if (in
->history
->data
)
1920 def_text
= (char *) in
->history
->data
;
1922 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
1923 in
->widget
.options
|= W_IS_INPUT
;
1924 in
->completions
= NULL
;
1925 in
->completion_flags
= completion_flags
;
1926 in
->current_max_size
= initial_buffer_len
;
1927 in
->buffer
= g_new (char, initial_buffer_len
);
1929 in
->field_width
= width
;
1931 in
->term_first_shown
= 0;
1932 in
->disable_update
= 0;
1935 in
->is_password
= 0;
1937 strcpy (in
->buffer
, def_text
);
1938 in
->point
= str_length (in
->buffer
);
1943 /* Listbox widget */
1945 /* Should draw the scrollbar, but currently draws only
1946 * indications that there is more information
1948 static int listbox_cdiff (WLEntry
*s
, WLEntry
*e
);
1951 listbox_drawscroll (WListbox
*l
)
1955 int max_line
= l
->widget
.lines
- 1;
1957 /* Are we at the top? */
1958 widget_move (&l
->widget
, 0, l
->widget
.cols
);
1959 if (l
->list
== l
->top
)
1960 tty_print_one_vline ();
1962 tty_print_char ('^');
1964 /* Are we at the bottom? */
1965 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
1966 top
= listbox_cdiff (l
->list
, l
->top
);
1967 if ((top
+ l
->widget
.lines
== l
->count
) || l
->widget
.lines
>= l
->count
)
1968 tty_print_one_vline ();
1970 tty_print_char ('v');
1972 /* Now draw the nice relative pointer */
1974 line
= 1+ ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
1976 for (i
= 1; i
< max_line
; i
++){
1977 widget_move (&l
->widget
, i
, l
->widget
.cols
);
1979 tty_print_one_vline ();
1981 tty_print_char ('*');
1986 listbox_draw (WListbox
*l
, gboolean focused
)
1988 const Dlg_head
*h
= l
->widget
.parent
;
1989 const int normalc
= DLG_NORMALC (h
);
1990 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
1997 for (e
= l
->top
, i
= 0; i
< l
->widget
.lines
; i
++) {
1998 /* Display the entry */
1999 if (e
== l
->current
&& sel_line
== -1) {
2001 tty_setcolor (selc
);
2003 tty_setcolor (normalc
);
2005 widget_move (&l
->widget
, i
, 1);
2007 if ((i
> 0 && e
== l
->list
) || !l
->list
)
2013 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2015 l
->cursor_y
= sel_line
;
2017 if (l
->scrollbar
&& l
->count
> l
->widget
.lines
) {
2018 tty_setcolor (normalc
);
2019 listbox_drawscroll (l
);
2023 /* Returns the number of items between s and e,
2024 must be on the same linked list */
2026 listbox_cdiff (WLEntry
*s
, WLEntry
*e
)
2030 for (count
= 0; s
!= e
; count
++)
2036 listbox_check_hotkey (WListbox
*l
, int key
)
2048 /* If we didn't find anything, return */
2049 if (i
&& e
== l
->list
)
2052 if (e
->hotkey
== key
)
2060 /* Selects the last entry and scrolls the list to the bottom */
2062 listbox_select_last (WListbox
*l
)
2065 l
->current
= l
->top
= l
->list
->prev
;
2066 for (i
= min (l
->widget
.lines
, l
->count
) - 1; i
; i
--)
2067 l
->top
= l
->top
->prev
;
2068 l
->pos
= l
->count
- 1;
2071 /* Selects the first entry and scrolls the list to the top */
2073 listbox_select_first (WListbox
*l
)
2075 l
->current
= l
->top
= l
->list
;
2080 listbox_remove_list (WListbox
*l
)
2089 while (l
->count
--) {
2095 l
->pos
= l
->count
= 0;
2096 l
->list
= l
->top
= l
->current
= 0;
2100 * bor 30.10.96: added force flag to remove *last* entry as well
2101 * bor 30.10.96: corrected selection bug if last entry was removed
2105 listbox_remove_current (WListbox
*l
, int force
)
2109 /* Ok, note: this won't allow for emtpy lists */
2110 if (!force
&& (!l
->count
|| l
->count
== 1))
2117 l
->current
->next
->prev
= l
->current
->prev
;
2118 l
->current
->prev
->next
= l
->current
->next
;
2119 if (p
->next
== l
->list
) {
2120 l
->current
= p
->prev
;
2124 l
->current
= p
->next
;
2127 l
->list
= l
->top
= p
->next
;
2130 l
->list
= l
->top
= l
->current
= 0;
2137 /* Makes *e the selected entry (sets current and pos) */
2139 listbox_select_entry (WListbox
*l
, WLEntry
*dest
)
2148 for (pos
= 0, e
= l
->list
; pos
< l
->count
; e
= e
->next
, pos
++){
2156 while (listbox_cdiff (l
->top
, l
->current
) >= l
->widget
.lines
)
2157 l
->top
= l
->top
->next
;
2159 l
->top
= l
->current
;
2165 /* If we are unable to find it, set decent values */
2166 l
->current
= l
->top
= l
->list
;
2170 /* Selects from base the pos element */
2172 listbox_select_pos (WListbox
*l
, WLEntry
*base
, int pos
)
2174 WLEntry
*last
= l
->list
->prev
;
2187 listbox_back (WListbox
*l
)
2190 listbox_select_entry (l
, l
->current
->prev
);
2192 listbox_select_last (l
);
2195 /* Return MSG_HANDLED if we want a redraw */
2197 listbox_key (WListbox
*l
, int key
)
2201 cb_ret_t j
= MSG_NOT_HANDLED
;
2203 /* focus on listbox item N by '0'..'9' keys */
2204 if (key
>= '0' && key
<= '9') {
2205 int oldpos
= l
->pos
;
2206 listbox_select_by_number(l
, key
- '0');
2208 /* need scroll to item? */
2209 if (abs(oldpos
- l
->pos
) > l
->widget
.lines
)
2210 l
->top
= l
->current
;
2216 return MSG_NOT_HANDLED
;
2222 listbox_select_first (l
);
2228 listbox_select_last (l
);
2243 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2244 && (l
->current
!= l
->list
->prev
)); i
++) {
2252 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2253 && (l
->current
!= l
->list
)); i
++) {
2259 return MSG_NOT_HANDLED
;
2263 listbox_destroy (WListbox
*l
)
2265 WLEntry
*n
, *p
= l
->list
;
2268 for (i
= 0; i
< l
->count
; i
++){
2277 listbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2279 WListbox
*l
= (WListbox
*) w
;
2280 Dlg_head
*h
= l
->widget
.parent
;
2289 e
= listbox_check_hotkey (l
, parm
);
2293 listbox_select_entry (l
, e
);
2295 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2298 action
= (*l
->cback
) (l
);
2300 action
= LISTBOX_DONE
;
2302 if (action
== LISTBOX_DONE
) {
2303 h
->ret_value
= B_ENTER
;
2308 return MSG_NOT_HANDLED
;
2311 ret_code
= listbox_key (l
, parm
);
2312 if (ret_code
!= MSG_NOT_HANDLED
) {
2313 listbox_draw (l
, TRUE
);
2314 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2319 widget_move (&l
->widget
, l
->cursor_y
, 0);
2320 (*h
->callback
) (h
, DLG_ACTION
, l
->pos
);
2324 case WIDGET_UNFOCUS
:
2326 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2329 case WIDGET_DESTROY
:
2330 listbox_destroy (l
);
2333 case WIDGET_RESIZED
:
2337 return default_proc (msg
, parm
);
2342 listbox_event (Gpm_Event
*event
, void *data
)
2347 Dlg_head
*h
= l
->widget
.parent
;
2350 if (event
->type
& GPM_DOWN
)
2351 dlg_select_widget (l
);
2353 if (l
->list
== NULL
)
2356 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2357 int ret
= MOU_REPEAT
;
2359 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2363 for (i
= -event
->y
; i
>= 0; i
--)
2365 else if (event
->y
> l
->widget
.lines
)
2366 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2368 else if (event
->buttons
& GPM_B_UP
) {
2371 } else if (event
->buttons
& GPM_B_DOWN
) {
2375 listbox_select_entry (l
,
2376 listbox_select_pos (l
, l
->top
,
2379 /* We need to refresh ourselves since the dialog manager doesn't */
2380 /* know about this event */
2381 listbox_draw (l
, TRUE
);
2386 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
)) {
2389 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2390 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2393 dlg_select_widget (l
);
2394 listbox_select_entry (l
,
2395 listbox_select_pos (l
, l
->top
,
2399 action
= (*l
->cback
) (l
);
2401 action
= LISTBOX_DONE
;
2403 if (action
== LISTBOX_DONE
) {
2404 h
->ret_value
= B_ENTER
;
2413 listbox_new (int y
, int x
, int height
, int width
, lcback callback
)
2415 WListbox
*l
= g_new (WListbox
, 1);
2420 init_widget (&l
->widget
, y
, x
, height
, width
,
2421 listbox_callback
, listbox_event
);
2423 l
->list
= l
->top
= l
->current
= 0;
2426 l
->cback
= callback
;
2427 l
->allow_duplicates
= 1;
2428 l
->scrollbar
= !tty_is_slow ();
2429 widget_want_hotkey (l
->widget
, 1);
2434 /* Listbox item adding function. They still lack a lot of functionality */
2436 /* 1.11.96 bor: added pos argument to control placement of new entry */
2438 listbox_append_item (WListbox
*l
, WLEntry
*e
, enum append_pos pos
)
2446 } else if (pos
== LISTBOX_APPEND_AT_END
) {
2448 e
->prev
= l
->list
->prev
;
2449 l
->list
->prev
->next
= e
;
2451 } else if (pos
== LISTBOX_APPEND_BEFORE
){
2452 e
->next
= l
->current
;
2453 e
->prev
= l
->current
->prev
;
2454 l
->current
->prev
->next
= e
;
2455 l
->current
->prev
= e
;
2456 if (l
->list
== l
->current
) { /* move list one position down */
2460 } else if (pos
== LISTBOX_APPEND_AFTER
) {
2461 e
->prev
= l
->current
;
2462 e
->next
= l
->current
->next
;
2463 l
->current
->next
->prev
= e
;
2464 l
->current
->next
= e
;
2465 } else if (pos
== LISTBOX_APPEND_SORTED
) {
2466 WLEntry
*w
= l
->list
;
2468 while (w
->next
!= l
->list
&& strcmp (e
->text
, w
->text
) > 0)
2470 if (w
->next
== l
->list
) {
2486 listbox_add_item (WListbox
*l
, enum append_pos pos
, int hotkey
,
2487 const char *text
, void *data
)
2494 if (!l
->allow_duplicates
)
2495 if (listbox_search_text (l
, text
))
2498 entry
= g_new (WLEntry
, 1);
2499 entry
->text
= g_strdup (text
);
2501 entry
->hotkey
= hotkey
;
2503 listbox_append_item (l
, entry
, pos
);
2508 /* Selects the nth entry in the listbox */
2510 listbox_select_by_number (WListbox
*l
, int n
)
2512 if (l
->list
!= NULL
)
2513 listbox_select_entry (l
, listbox_select_pos (l
, l
->list
, n
));
2517 listbox_search_text (WListbox
*l
, const char *text
)
2526 if(!strcmp (e
->text
, text
))
2529 } while (e
!=l
->list
);
2534 /* Returns the current string text as well as the associated extra data */
2536 listbox_get_current (WListbox
*l
, char **string
, char **extra
)
2542 if (string
&& l
->current
)
2543 *string
= l
->current
->text
;
2544 if (extra
&& l
->current
)
2545 *extra
= l
->current
->data
;
2548 /* returns TRUE if a function has been called, FALSE otherwise. */
2550 buttonbar_call (WButtonBar
*bb
, int i
)
2552 switch (bb
->labels
[i
].tag
) {
2556 bb
->labels
[i
].u
.fn_void ();
2559 bb
->labels
[i
].u
.fn_ptr (bb
->labels
[i
].data
);
2565 /* calculate width of one button, width is never lesser than 7 */
2567 buttonbat_get_button_width ()
2569 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2570 return (result
>= 7) ? result
: 7;
2575 buttonbar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2577 WButtonBar
*bb
= (WButtonBar
*) w
;
2583 return MSG_NOT_HANDLED
;
2586 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2587 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2589 return MSG_NOT_HANDLED
;
2594 int count_free_positions
;
2596 widget_move (&bb
->widget
, 0, 0);
2597 tty_setcolor (DEFAULT_COLOR
);
2598 bb
->btn_width
= buttonbat_get_button_width ();
2599 tty_printf ("%-*s", bb
->widget
.cols
, "");
2600 count_free_positions
= COLS
- bb
->btn_width
*BUTTONBAR_LABELS_NUM
;
2602 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++) {
2603 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2604 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2605 tty_printf ("%2d", i
+ 1);
2606 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2607 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2608 tty_print_string (str_fit_to_term (
2610 bb
->btn_width
- 2 + (int)(offset
< count_free_positions
),
2613 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2619 case WIDGET_DESTROY
:
2620 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2621 g_free (bb
->labels
[i
].text
);
2625 return default_proc (msg
, parm
);
2630 buttonbar_event (Gpm_Event
*event
, void *data
)
2632 WButtonBar
*bb
= data
;
2635 if (!(event
->type
& GPM_UP
))
2639 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ (COLS
);
2640 if (button
< BUTTONBAR_LABELS_NUM
)
2641 buttonbar_call (bb
, button
);
2646 buttonbar_new (int visible
)
2651 bb
= g_new0 (WButtonBar
, 1);
2653 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
,
2654 buttonbar_callback
, buttonbar_event
);
2655 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2656 bb
->visible
= visible
;
2657 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++){
2658 bb
->labels
[i
].text
= NULL
;
2659 bb
->labels
[i
].tag
= BBFUNC_NONE
;
2661 widget_want_hotkey (bb
->widget
, 1);
2662 widget_want_cursor (bb
->widget
, 0);
2663 bb
->btn_width
= buttonbat_get_button_width ();
2669 set_label_text (WButtonBar
* bb
, int index
, const char *text
)
2671 g_free (bb
->labels
[index
- 1].text
);
2673 bb
->labels
[index
- 1].text
= g_strdup (text
);
2676 /* Find ButtonBar widget in the dialog */
2678 find_buttonbar (Dlg_head
*h
)
2682 bb
= (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2687 buttonbar_clear_label (Dlg_head
*h
, int idx
)
2689 WButtonBar
*bb
= find_buttonbar (h
);
2694 set_label_text (bb
, idx
, "");
2695 bb
->labels
[idx
- 1].tag
= BBFUNC_NONE
;
2699 buttonbar_set_label_data (Dlg_head
*h
, int idx
, const char *text
,
2700 buttonbarfn cback
, void *data
)
2702 WButtonBar
*bb
= find_buttonbar (h
);
2707 assert (cback
!= (buttonbarfn
) 0);
2708 set_label_text (bb
, idx
, text
);
2709 bb
->labels
[idx
- 1].tag
= BBFUNC_PTR
;
2710 bb
->labels
[idx
- 1].u
.fn_ptr
= cback
;
2711 bb
->labels
[idx
- 1].data
= data
;
2715 buttonbar_set_label (Dlg_head
*h
, int idx
, const char *text
, voidfn cback
)
2717 WButtonBar
*bb
= find_buttonbar (h
);
2722 assert (cback
!= (voidfn
) 0);
2723 set_label_text (bb
, idx
, text
);
2724 bb
->labels
[idx
- 1].tag
= BBFUNC_VOID
;
2725 bb
->labels
[idx
- 1].u
.fn_void
= cback
;
2729 buttonbar_set_visible (WButtonBar
*bb
, gboolean visible
)
2731 bb
->visible
= visible
;
2735 buttonbar_redraw (Dlg_head
*h
)
2737 WButtonBar
*bb
= find_buttonbar (h
);
2742 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
2746 groupbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2748 WGroupbox
*g
= (WGroupbox
*) w
;
2755 return MSG_NOT_HANDLED
;
2758 tty_setcolor (COLOR_NORMAL
);
2759 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2760 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
,
2763 tty_setcolor (COLOR_HOT_NORMAL
);
2764 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2765 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
2766 tty_print_string (g
->title
);
2769 case WIDGET_DESTROY
:
2774 return default_proc (msg
, parm
);
2779 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
2781 WGroupbox
*g
= g_new (WGroupbox
, 1);
2783 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
2785 g
->widget
.options
&= ~W_WANT_CURSOR
;
2786 widget_want_hotkey (g
->widget
, 0);
2788 /* Strip existing spaces, add one space before and after the title */
2791 t
= g_strstrip (g_strdup (title
));
2792 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);