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 */
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
) /* this is how to disable */
1009 if (!input_name
|| !*input_name
)
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
++) {
1020 char key_name
[BUF_TINY
];
1021 g_snprintf (key_name
, sizeof (key_name
), "%lu", (unsigned long)i
);
1022 this_entry
= mc_config_get_string (cfg
, input_name
, key_name
, "");
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
)
1051 if (!num_history_items_recorded
) /* this is how to disable */
1054 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1056 if ((i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
)) != -1)
1059 /* Make sure the history is only readable by the user */
1060 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
) {
1065 /* go to end of list */
1066 h
= g_list_last (h
);
1068 /* go back 60 places */
1069 for (i
= 0; i
< num_history_items_recorded
- 1 && h
->prev
; i
++)
1070 h
= g_list_previous (h
);
1072 cfg
= mc_config_init(profile
);
1075 mc_config_del_group(cfg
,input_name
);
1077 /* dump histories into profile */
1078 for (i
= 0; h
; h
= g_list_next (h
)) {
1079 char *text
= (char *) h
->data
;
1081 /* We shouldn't have null entries, but let's be sure */
1083 char key_name
[BUF_TINY
];
1084 g_snprintf (key_name
, sizeof (key_name
), "%d", i
++);
1085 mc_config_set_string(cfg
,input_name
, key_name
, text
);
1089 mc_config_save_file (cfg
, NULL
);
1090 mc_config_deinit(cfg
);
1094 /* }}} history saving and loading */
1097 /* {{{ history display */
1102 return _(" History ");
1106 listbox_fwd (WListbox
*l
)
1108 if (l
->current
!= l
->list
->prev
)
1109 listbox_select_entry (l
, l
->current
->next
);
1111 listbox_select_first (l
);
1121 dlg_hist_reposition (Dlg_head
*dlg_head
)
1123 dlg_hist_data
*data
;
1124 int x
= 0, y
, he
, wi
;
1127 if ((dlg_head
== NULL
)
1128 || (dlg_head
->data
== NULL
))
1129 return MSG_NOT_HANDLED
;
1131 data
= (dlg_hist_data
*) dlg_head
->data
;
1133 y
= data
->widget
->y
;
1134 he
= data
->count
+ 2;
1136 if (he
<= y
|| y
> (LINES
- 6)) {
1137 he
= min (he
, y
- 1);
1141 he
= min (he
, LINES
- y
);
1144 if (data
->widget
->x
> 2)
1145 x
= data
->widget
->x
- 2;
1147 wi
= data
->maxlen
+ 4;
1149 if ((wi
+ x
) > COLS
) {
1150 wi
= min (wi
, COLS
);
1154 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1160 dlg_hist_callback (Dlg_head
*h
, Widget
*sender
,
1161 dlg_msg_t msg
, int parm
, void *data
)
1165 return dlg_hist_reposition (h
);
1168 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1173 show_hist (GList
*history
, Widget
*widget
)
1176 size_t maxlen
, i
, count
= 0;
1178 Dlg_head
*query_dlg
;
1179 WListbox
*query_list
;
1180 dlg_hist_data hist_data
;
1182 if (history
== NULL
)
1185 maxlen
= str_term_width1 (i18n_htitle ());
1187 z
= g_list_first (history
);
1190 i
= str_term_width1 ((char *) hi
->data
);
1191 maxlen
= max (maxlen
, i
);
1193 hi
= g_list_next (hi
);
1196 hist_data
.maxlen
= maxlen
;
1197 hist_data
.widget
= widget
;
1198 hist_data
.count
= count
;
1201 create_dlg (0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1202 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1203 query_dlg
->data
= &hist_data
;
1205 query_list
= listbox_new (1, 1, 2, 2, NULL
);
1207 /* this call makes list stick to all sides of dialog, effectively make
1208 it be resized with dialog */
1209 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1211 /* to avoid diplicating of (calculating sizes in two places)
1212 code, call dlg_hist_callback function here, to set dialog and
1214 The main idea - create 4x4 dialog and add 2x2 list in
1215 center of it, and let dialog function resize it to needed
1217 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1219 if (query_dlg
->y
< widget
->y
) {
1223 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1224 0, (char *) hi
->data
, NULL
);
1225 hi
= g_list_next (hi
);
1227 listbox_select_last (query_list
);
1229 /* traverse backwards */
1230 hi
= g_list_last (history
);
1232 listbox_add_item (query_list
, LISTBOX_APPEND_AT_END
,
1233 0, (char *) hi
->data
, NULL
);
1234 hi
= g_list_previous (hi
);
1238 if (run_dlg (query_dlg
) != B_CANCEL
) {
1239 listbox_get_current (query_list
, &q
, NULL
);
1243 destroy_dlg (query_dlg
);
1248 do_show_hist (WInput
*in
)
1251 r
= show_hist (in
->history
, &in
->widget
);
1253 assign_text (in
, r
);
1258 /* }}} history display */
1261 input_destroy (WInput
*in
)
1264 fprintf (stderr
, "Internal error: null Input *\n");
1271 if (!in
->is_password
) /* don't save passwords ;-) */
1272 history_put (in
->history_name
, in
->history
);
1274 in
->history
= g_list_first (in
->history
);
1275 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1276 g_list_free (in
->history
);
1279 g_free (in
->buffer
);
1280 free_completions (in
);
1281 g_free (in
->history_name
);
1285 input_disable_update (WInput
*in
)
1287 in
->disable_update
++;
1291 input_enable_update (WInput
*in
)
1293 in
->disable_update
--;
1294 update_input (in
, 0);
1299 push_history (WInput
*in
, const char *text
)
1301 /* input widget where urls with passwords are entered without any
1303 const char *password_input_fields
[] = {
1304 N_(" Link to a remote machine "),
1305 N_(" FTP to machine "),
1306 N_(" SMB link to machine ")
1308 const size_t ELEMENTS
= (sizeof (password_input_fields
) /
1309 sizeof (password_input_fields
[0]));
1319 for (i
= 0; i
< ELEMENTS
; i
++)
1320 password_input_fields
[i
] = _(password_input_fields
[i
]);
1323 t
= g_strstrip (g_strdup (text
));
1326 t
= g_strdup (empty
? "" : text
);
1328 if (in
->history_name
!= NULL
) {
1329 const char *p
= in
->history_name
+ 3;
1331 for (i
= 0; i
< ELEMENTS
; i
++)
1332 if (strcmp (p
, password_input_fields
[i
]) == 0)
1335 strip_password (t
, i
>= ELEMENTS
);
1338 in
->history
= list_append_unique (in
->history
, t
);
1342 /* Cleans the input line and adds the current text to the history */
1344 new_input (WInput
*in
)
1346 push_history (in
, in
->buffer
);
1348 in
->buffer
[0] = '\0';
1352 free_completions (in
);
1353 update_input (in
, 0);
1357 move_buffer_backward (WInput
*in
, int start
, int end
)
1360 int str_len
= str_length (in
->buffer
);
1361 if (start
>= str_len
|| end
> str_len
+ 1) return;
1363 pos
= str_offset_to_pos (in
->buffer
, start
);
1364 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1366 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1367 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1371 insert_char (WInput
*in
, int c_code
)
1377 return MSG_NOT_HANDLED
;
1379 if (in
->charpoint
>= MB_LEN_MAX
)
1382 in
->charbuf
[in
->charpoint
] = c_code
;
1385 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1388 in
->charpoint
= 0; /* broken multibyte char, skip */
1393 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
){
1394 /* Expand the buffer */
1395 size_t new_length
= in
->current_max_size
+
1396 in
->field_width
+ in
->charpoint
;
1397 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1400 in
->current_max_size
= new_length
;
1404 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
) {
1405 /* bytes from begin */
1406 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1408 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1410 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1411 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] =
1412 in
->buffer
[ins_point
+ i
- 1];
1414 memcpy(in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1423 beginning_of_line (WInput
*in
)
1430 end_of_line (WInput
*in
)
1432 in
->point
= str_length (in
->buffer
);
1437 backward_char (WInput
*in
)
1439 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1441 if (in
->point
> 0) {
1442 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1448 forward_char (WInput
*in
)
1450 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1451 if (act
[0] != '\0') {
1452 in
->point
+= str_cnext_noncomb_char (&act
);
1458 forward_word (WInput
* in
)
1460 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1462 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
))) {
1463 str_cnext_char (&p
);
1466 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
)) {
1467 str_cnext_char (&p
);
1473 backward_word (WInput
*in
)
1479 p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1480 (p
!= in
->buffer
) && (p
[0] == '\0');
1481 str_cprev_char (&p
), in
->point
--
1484 while (p
!= in
->buffer
) {
1486 str_cprev_char (&p
);
1487 if (!str_isspace (p
) && !str_ispunct (p
)) {
1493 while (p
!= in
->buffer
) {
1494 str_cprev_char (&p
);
1495 if (str_isspace (p
) || str_ispunct (p
))
1503 key_left (WInput
*in
)
1509 key_ctrl_left (WInput
*in
)
1515 key_right (WInput
*in
)
1521 key_ctrl_right (WInput
*in
)
1526 backward_delete (WInput
*in
)
1528 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1534 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1535 move_buffer_backward(in
, start
, in
->point
);
1542 delete_char (WInput
*in
)
1544 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1545 int end
= in
->point
;
1547 end
+= str_cnext_noncomb_char (&act
);
1549 move_buffer_backward(in
, in
->point
, end
);
1555 copy_region (WInput
*in
, int x_first
, int x_last
)
1557 int first
= min (x_first
, x_last
);
1558 int last
= max (x_first
, x_last
);
1560 if (last
== first
) {
1561 /* Copy selected files to clipboard */
1562 panel_save_curent_file_to_clip_file ();
1566 g_free (kill_buffer
);
1568 first
= str_offset_to_pos (in
->buffer
, first
);
1569 last
= str_offset_to_pos (in
->buffer
, last
);
1571 kill_buffer
= g_strndup(in
->buffer
+ first
, last
- first
);
1572 save_text_to_clip_file (kill_buffer
);
1576 delete_region (WInput
*in
, int x_first
, int x_last
)
1578 int first
= min (x_first
, x_last
);
1579 int last
= max (x_first
, x_last
);
1583 if (in
->mark
> first
)
1585 last
= str_offset_to_pos (in
->buffer
, last
);
1586 first
= str_offset_to_pos (in
->buffer
, first
);
1587 len
= strlen (&in
->buffer
[last
]) + 1;
1588 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1594 kill_word (WInput
*in
)
1596 int old_point
= in
->point
;
1600 new_point
= in
->point
;
1601 in
->point
= old_point
;
1603 copy_region (in
, old_point
, new_point
);
1604 delete_region (in
, old_point
, new_point
);
1611 back_kill_word (WInput
*in
)
1613 int old_point
= in
->point
;
1617 new_point
= in
->point
;
1618 in
->point
= old_point
;
1620 copy_region (in
, old_point
, new_point
);
1621 delete_region (in
, old_point
, new_point
);
1626 set_mark (WInput
*in
)
1628 in
->mark
= in
->point
;
1632 kill_save (WInput
*in
)
1634 copy_region (in
, in
->mark
, in
->point
);
1638 kill_region (WInput
*in
)
1641 delete_region (in
, in
->point
, in
->mark
);
1645 clear_region (WInput
*in
)
1647 delete_region (in
, in
->point
, in
->mark
);
1658 for (p
= kill_buffer
; *p
; p
++)
1659 insert_char (in
, *p
);
1664 kill_line (WInput
*in
)
1666 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1667 g_free (kill_buffer
);
1668 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1669 in
->buffer
[chp
] = '\0';
1674 ins_from_clip (WInput
*in
)
1678 if (load_text_from_clip_file (&p
)) {
1681 for (pp
= p
; *pp
!= '\0'; pp
++)
1682 insert_char (in
, *pp
);
1689 assign_text (WInput
*in
, const char *text
)
1691 free_completions (in
);
1692 g_free (in
->buffer
);
1693 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1694 in
->current_max_size
= strlen (in
->buffer
) + 1;
1695 in
->point
= str_length (in
->buffer
);
1702 hist_prev (WInput
*in
)
1707 if (in
->need_push
) {
1708 push_history (in
, in
->buffer
);
1709 in
->history
= g_list_previous (in
->history
);
1710 } else if (in
->history
->prev
)
1711 in
->history
= g_list_previous (in
->history
);
1714 assign_text (in
, (char *) in
->history
->data
);
1719 hist_next (WInput
*in
)
1721 if (in
->need_push
) {
1722 push_history (in
, in
->buffer
);
1723 assign_text (in
, "");
1730 if (!in
->history
->next
) {
1731 assign_text (in
, "");
1735 in
->history
= g_list_next (in
->history
);
1736 assign_text (in
, (char *) in
->history
->data
);
1741 port_region_marked_for_delete (WInput
*in
)
1743 in
->buffer
[0] = '\0';
1750 input_execute_cmd (WInput
*in
, unsigned long command
)
1752 cb_ret_t res
= MSG_HANDLED
;
1756 beginning_of_line (in
);
1761 case CK_InputMoveLeft
:
1764 case CK_InputWordLeft
:
1767 case CK_InputMoveRight
:
1770 case CK_InputWordRight
:
1771 key_ctrl_right (in
);
1773 case CK_InputBackwardChar
:
1776 case CK_InputBackwardWord
:
1779 case CK_InputForwardChar
:
1782 case CK_InputForwardWord
:
1785 case CK_InputBackwardDelete
:
1786 backward_delete (in
);
1788 case CK_InputDeleteChar
:
1791 case CK_InputKillWord
:
1794 case CK_InputBackwardKillWord
:
1795 back_kill_word (in
);
1797 case CK_InputSetMark
:
1800 case CK_InputKillRegion
:
1803 case CK_InputClearLine
:
1806 case CK_InputKillSave
:
1815 case CK_InputKillLine
:
1818 case CK_InputHistoryPrev
:
1821 case CK_InputHistoryNext
:
1824 case CK_InputHistoryShow
:
1827 case CK_InputComplete
:
1831 res
= MSG_NOT_HANDLED
;
1837 /* This function is a test for a special input key used in complete.c */
1838 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1839 and 2 if it is a complete key */
1841 is_in_input_map (WInput
*in
, int key
)
1844 for (i
= 0; input_map
[i
].key
!= 0; i
++)
1845 if (key
== input_map
[i
].key
) {
1846 input_execute_cmd (in
, input_map
[i
].command
);
1847 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
1853 handle_char (WInput
*in
, int key
)
1858 v
= MSG_NOT_HANDLED
;
1861 free_completions (in
);
1862 v
= insert_char (in
, key
);
1863 update_input (in
, 1);
1867 for (i
= 0; input_map
[i
].key
; i
++) {
1868 if (key
== input_map
[i
].key
) {
1869 if (input_map
[i
].command
!= CK_InputComplete
)
1870 free_completions (in
);
1871 input_execute_cmd (in
, input_map
[i
].command
);
1872 update_input (in
, 1);
1877 if (input_map
[i
].command
== 0) {
1879 return MSG_NOT_HANDLED
;
1881 port_region_marked_for_delete (in
);
1882 free_completions (in
);
1883 v
= insert_char (in
, key
);
1885 update_input (in
, 1);
1889 /* Inserts text in input line */
1891 stuff (WInput
*in
, const char *text
, int insert_extra_space
)
1893 input_disable_update (in
);
1894 while (*text
!= '\0')
1895 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
1896 if (insert_extra_space
)
1897 handle_char (in
, ' ');
1898 input_enable_update (in
);
1899 update_input (in
, 1);
1903 input_set_point (WInput
*in
, int pos
)
1905 int max_pos
= str_length (in
->buffer
);
1909 if (pos
!= in
->point
)
1910 free_completions (in
);
1913 update_input (in
, 1);
1917 input_callback (Widget
*w
, widget_msg_t msg
, int parm
)
1919 WInput
*in
= (WInput
*) w
;
1924 if (parm
== XCTRL ('q')) {
1926 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
1931 /* Keys we want others to handle */
1932 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
1933 || parm
== KEY_F (10) || parm
== '\n')
1934 return MSG_NOT_HANDLED
;
1936 /* When pasting multiline text, insert literal Enter */
1937 if ((parm
& ~KEY_M_MASK
) == '\n') {
1939 v
= handle_char (in
, '\n');
1944 return handle_char (in
, parm
);
1946 case WIDGET_COMMAND
:
1947 return input_execute_cmd (in
, parm
);
1950 case WIDGET_UNFOCUS
:
1952 update_input (in
, 0);
1956 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
1957 - in
->term_first_shown
);
1960 case WIDGET_DESTROY
:
1965 return default_proc (msg
, parm
);
1970 input_event (Gpm_Event
* event
, void *data
)
1974 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
1975 dlg_select_widget (in
);
1977 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
1978 && should_show_history_button (in
)) {
1981 in
->point
= str_length (in
->buffer
);
1982 if (event
->x
+ in
->term_first_shown
- 1 <
1983 str_term_width1 (in
->buffer
))
1985 in
->point
= str_column_to_pos (in
->buffer
, event
->x
1986 + in
->term_first_shown
- 1);
1989 update_input (in
, 1);
1995 input_new (int y
, int x
, int color
, int width
, const char *def_text
,
1996 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
1998 WInput
*in
= g_new (WInput
, 1);
1999 int initial_buffer_len
;
2001 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2005 in
->history_name
= 0;
2008 in
->history_name
= g_strdup (histname
);
2009 in
->history
= history_get (histname
);
2013 if (def_text
== NULL
)
2016 if (def_text
== INPUT_LAST_TEXT
) {
2019 if (in
->history
->data
)
2020 def_text
= (char *) in
->history
->data
;
2022 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2023 in
->widget
.options
|= W_IS_INPUT
;
2024 in
->completions
= NULL
;
2025 in
->completion_flags
= completion_flags
;
2026 in
->current_max_size
= initial_buffer_len
;
2027 in
->buffer
= g_new (char, initial_buffer_len
);
2029 in
->field_width
= width
;
2031 in
->term_first_shown
= 0;
2032 in
->disable_update
= 0;
2035 in
->is_password
= 0;
2037 strcpy (in
->buffer
, def_text
);
2038 in
->point
= str_length (in
->buffer
);
2043 /* Listbox widget */
2045 /* Should draw the scrollbar, but currently draws only
2046 * indications that there is more information
2048 static int listbox_cdiff (WLEntry
*s
, WLEntry
*e
);
2051 listbox_drawscroll (WListbox
*l
)
2055 int max_line
= l
->widget
.lines
- 1;
2057 /* Are we at the top? */
2058 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2059 if (l
->list
== l
->top
)
2060 tty_print_one_vline ();
2062 tty_print_char ('^');
2064 /* Are we at the bottom? */
2065 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2066 top
= listbox_cdiff (l
->list
, l
->top
);
2067 if ((top
+ l
->widget
.lines
== l
->count
) || l
->widget
.lines
>= l
->count
)
2068 tty_print_one_vline ();
2070 tty_print_char ('v');
2072 /* Now draw the nice relative pointer */
2074 line
= 1+ ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2076 for (i
= 1; i
< max_line
; i
++){
2077 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2079 tty_print_one_vline ();
2081 tty_print_char ('*');
2086 listbox_draw (WListbox
*l
, gboolean focused
)
2088 const Dlg_head
*h
= l
->widget
.parent
;
2089 const int normalc
= DLG_NORMALC (h
);
2090 int selc
= focused
? DLG_HOT_FOCUSC (h
) : DLG_FOCUSC (h
);
2097 for (e
= l
->top
, i
= 0; i
< l
->widget
.lines
; i
++) {
2098 /* Display the entry */
2099 if (e
== l
->current
&& sel_line
== -1) {
2101 tty_setcolor (selc
);
2103 tty_setcolor (normalc
);
2105 widget_move (&l
->widget
, i
, 1);
2107 if ((i
> 0 && e
== l
->list
) || !l
->list
)
2113 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2115 l
->cursor_y
= sel_line
;
2117 if (l
->scrollbar
&& l
->count
> l
->widget
.lines
) {
2118 tty_setcolor (normalc
);
2119 listbox_drawscroll (l
);
2123 /* Returns the number of items between s and e,
2124 must be on the same linked list */
2126 listbox_cdiff (WLEntry
*s
, WLEntry
*e
)
2130 for (count
= 0; s
!= e
; count
++)
2136 listbox_check_hotkey (WListbox
*l
, int key
)
2148 /* If we didn't find anything, return */
2149 if (i
&& e
== l
->list
)
2152 if (e
->hotkey
== key
)
2160 /* Selects the last entry and scrolls the list to the bottom */
2162 listbox_select_last (WListbox
*l
)
2165 l
->current
= l
->top
= l
->list
->prev
;
2166 for (i
= min (l
->widget
.lines
, l
->count
) - 1; i
; i
--)
2167 l
->top
= l
->top
->prev
;
2168 l
->pos
= l
->count
- 1;
2171 /* Selects the first entry and scrolls the list to the top */
2173 listbox_select_first (WListbox
*l
)
2175 l
->current
= l
->top
= l
->list
;
2180 listbox_remove_list (WListbox
*l
)
2189 while (l
->count
--) {
2195 l
->pos
= l
->count
= 0;
2196 l
->list
= l
->top
= l
->current
= 0;
2200 * bor 30.10.96: added force flag to remove *last* entry as well
2201 * bor 30.10.96: corrected selection bug if last entry was removed
2205 listbox_remove_current (WListbox
*l
, int force
)
2209 /* Ok, note: this won't allow for emtpy lists */
2210 if (!force
&& (!l
->count
|| l
->count
== 1))
2217 l
->current
->next
->prev
= l
->current
->prev
;
2218 l
->current
->prev
->next
= l
->current
->next
;
2219 if (p
->next
== l
->list
) {
2220 l
->current
= p
->prev
;
2224 l
->current
= p
->next
;
2227 l
->list
= l
->top
= p
->next
;
2230 l
->list
= l
->top
= l
->current
= 0;
2237 /* Makes *e the selected entry (sets current and pos) */
2239 listbox_select_entry (WListbox
*l
, WLEntry
*dest
)
2248 for (pos
= 0, e
= l
->list
; pos
< l
->count
; e
= e
->next
, pos
++){
2256 while (listbox_cdiff (l
->top
, l
->current
) >= l
->widget
.lines
)
2257 l
->top
= l
->top
->next
;
2259 l
->top
= l
->current
;
2265 /* If we are unable to find it, set decent values */
2266 l
->current
= l
->top
= l
->list
;
2270 /* Selects from base the pos element */
2272 listbox_select_pos (WListbox
*l
, WLEntry
*base
, int pos
)
2274 WLEntry
*last
= l
->list
->prev
;
2287 listbox_back (WListbox
*l
)
2290 listbox_select_entry (l
, l
->current
->prev
);
2292 listbox_select_last (l
);
2295 /* Return MSG_HANDLED if we want a redraw */
2297 listbox_key (WListbox
*l
, int key
)
2301 cb_ret_t j
= MSG_NOT_HANDLED
;
2303 /* focus on listbox item N by '0'..'9' keys */
2304 if (key
>= '0' && key
<= '9') {
2305 int oldpos
= l
->pos
;
2306 listbox_select_by_number(l
, key
- '0');
2308 /* need scroll to item? */
2309 if (abs(oldpos
- l
->pos
) > l
->widget
.lines
)
2310 l
->top
= l
->current
;
2316 return MSG_NOT_HANDLED
;
2322 listbox_select_first (l
);
2328 listbox_select_last (l
);
2343 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2344 && (l
->current
!= l
->list
->prev
)); i
++) {
2352 for (i
= 0; ((i
< l
->widget
.lines
- 1)
2353 && (l
->current
!= l
->list
)); i
++) {
2359 return MSG_NOT_HANDLED
;
2363 listbox_destroy (WListbox
*l
)
2365 WLEntry
*n
, *p
= l
->list
;
2368 for (i
= 0; i
< l
->count
; i
++){
2377 listbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2379 WListbox
*l
= (WListbox
*) w
;
2380 Dlg_head
*h
= l
->widget
.parent
;
2389 e
= listbox_check_hotkey (l
, parm
);
2393 listbox_select_entry (l
, e
);
2394 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2397 action
= (*l
->cback
) (l
);
2399 action
= LISTBOX_DONE
;
2401 if (action
== LISTBOX_DONE
) {
2402 h
->ret_value
= B_ENTER
;
2407 return MSG_NOT_HANDLED
;
2410 ret_code
= listbox_key (l
, parm
);
2411 if (ret_code
!= MSG_NOT_HANDLED
) {
2412 listbox_draw (l
, TRUE
);
2413 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2418 widget_move (&l
->widget
, l
->cursor_y
, 0);
2419 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2423 case WIDGET_UNFOCUS
:
2425 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2428 case WIDGET_DESTROY
:
2429 listbox_destroy (l
);
2432 case WIDGET_RESIZED
:
2436 return default_proc (msg
, parm
);
2441 listbox_event (Gpm_Event
*event
, void *data
)
2446 Dlg_head
*h
= l
->widget
.parent
;
2449 if (event
->type
& GPM_DOWN
)
2450 dlg_select_widget (l
);
2452 if (l
->list
== NULL
)
2455 if (event
->type
& (GPM_DOWN
| GPM_DRAG
)) {
2456 int ret
= MOU_REPEAT
;
2458 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2462 for (i
= -event
->y
; i
>= 0; i
--)
2464 else if (event
->y
> l
->widget
.lines
)
2465 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2467 else if (event
->buttons
& GPM_B_UP
) {
2470 } else if (event
->buttons
& GPM_B_DOWN
) {
2474 listbox_select_entry (l
,
2475 listbox_select_pos (l
, l
->top
,
2478 /* We need to refresh ourselves since the dialog manager doesn't */
2479 /* know about this event */
2480 listbox_draw (l
, TRUE
);
2485 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
)) {
2488 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2489 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2492 dlg_select_widget (l
);
2493 listbox_select_entry (l
,
2494 listbox_select_pos (l
, l
->top
,
2498 action
= (*l
->cback
) (l
);
2500 action
= LISTBOX_DONE
;
2502 if (action
== LISTBOX_DONE
) {
2503 h
->ret_value
= B_ENTER
;
2512 listbox_new (int y
, int x
, int height
, int width
, lcback callback
)
2514 WListbox
*l
= g_new (WListbox
, 1);
2519 init_widget (&l
->widget
, y
, x
, height
, width
,
2520 listbox_callback
, listbox_event
);
2522 l
->list
= l
->top
= l
->current
= 0;
2525 l
->cback
= callback
;
2526 l
->allow_duplicates
= 1;
2527 l
->scrollbar
= !tty_is_slow ();
2528 widget_want_hotkey (l
->widget
, 1);
2533 /* Listbox item adding function. They still lack a lot of functionality */
2535 /* 1.11.96 bor: added pos argument to control placement of new entry */
2537 listbox_append_item (WListbox
*l
, WLEntry
*e
, enum append_pos pos
)
2545 } else if (pos
== LISTBOX_APPEND_AT_END
) {
2547 e
->prev
= l
->list
->prev
;
2548 l
->list
->prev
->next
= e
;
2550 } else if (pos
== LISTBOX_APPEND_BEFORE
){
2551 e
->next
= l
->current
;
2552 e
->prev
= l
->current
->prev
;
2553 l
->current
->prev
->next
= e
;
2554 l
->current
->prev
= e
;
2555 if (l
->list
== l
->current
) { /* move list one position down */
2559 } else if (pos
== LISTBOX_APPEND_AFTER
) {
2560 e
->prev
= l
->current
;
2561 e
->next
= l
->current
->next
;
2562 l
->current
->next
->prev
= e
;
2563 l
->current
->next
= e
;
2564 } else if (pos
== LISTBOX_APPEND_SORTED
) {
2565 WLEntry
*w
= l
->list
;
2567 while (w
->next
!= l
->list
&& strcmp (e
->text
, w
->text
) > 0)
2569 if (w
->next
== l
->list
) {
2585 listbox_add_item (WListbox
*l
, enum append_pos pos
, int hotkey
,
2586 const char *text
, void *data
)
2593 if (!l
->allow_duplicates
)
2594 if (listbox_search_text (l
, text
))
2597 entry
= g_new (WLEntry
, 1);
2598 entry
->text
= g_strdup (text
);
2600 entry
->hotkey
= hotkey
;
2602 listbox_append_item (l
, entry
, pos
);
2607 /* Selects the nth entry in the listbox */
2609 listbox_select_by_number (WListbox
*l
, int n
)
2611 if (l
->list
!= NULL
)
2612 listbox_select_entry (l
, listbox_select_pos (l
, l
->list
, n
));
2616 listbox_search_text (WListbox
*l
, const char *text
)
2625 if(!strcmp (e
->text
, text
))
2628 } while (e
!=l
->list
);
2633 /* Returns the current string text as well as the associated extra data */
2635 listbox_get_current (WListbox
*l
, char **string
, char **extra
)
2641 if (string
&& l
->current
)
2642 *string
= l
->current
->text
;
2643 if (extra
&& l
->current
)
2644 *extra
= l
->current
->data
;
2647 /* returns TRUE if a function has been called, FALSE otherwise. */
2649 buttonbar_call (WButtonBar
*bb
, int i
)
2651 cb_ret_t ret
= MSG_NOT_HANDLED
;
2654 ret
= bb
->widget
.parent
->callback (bb
->widget
.parent
,
2655 (Widget
*) bb
, DLG_ACTION
,
2656 bb
->labels
[i
].command
,
2657 bb
->labels
[i
].receiver
);
2661 /* calculate width of one button, width is never lesser than 7 */
2663 buttonbat_get_button_width (void)
2665 int result
= COLS
/ BUTTONBAR_LABELS_NUM
;
2666 return (result
>= 7) ? result
: 7;
2670 buttonbar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2672 WButtonBar
*bb
= (WButtonBar
*) w
;
2678 return MSG_NOT_HANDLED
;
2681 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2682 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
2684 return MSG_NOT_HANDLED
;
2689 int count_free_positions
;
2691 widget_move (&bb
->widget
, 0, 0);
2692 tty_setcolor (DEFAULT_COLOR
);
2693 bb
->btn_width
= buttonbat_get_button_width ();
2694 tty_printf ("%-*s", bb
->widget
.cols
, "");
2695 count_free_positions
= COLS
- bb
->btn_width
* BUTTONBAR_LABELS_NUM
;
2697 for (i
= 0; i
< COLS
/ bb
->btn_width
&& i
< BUTTONBAR_LABELS_NUM
; i
++) {
2698 widget_move (&bb
->widget
, 0, (i
* bb
->btn_width
) + offset
);
2699 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
2700 tty_printf ("%2d", i
+ 1);
2701 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
2702 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
2703 tty_print_string (str_fit_to_term (
2705 bb
->btn_width
- 2 + (int)(offset
< count_free_positions
),
2708 if (count_free_positions
!= 0 && offset
< count_free_positions
)
2714 case WIDGET_DESTROY
:
2715 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
2716 g_free (bb
->labels
[i
].text
);
2720 return default_proc (msg
, parm
);
2725 buttonbar_event (Gpm_Event
*event
, void *data
)
2727 WButtonBar
*bb
= data
;
2730 if (!(event
->type
& GPM_UP
))
2734 button
= (event
->x
- 1) * BUTTONBAR_LABELS_NUM
/ COLS
;
2735 if (button
< BUTTONBAR_LABELS_NUM
)
2736 buttonbar_call (bb
, button
);
2741 buttonbar_new (gboolean visible
)
2745 bb
= g_new0 (WButtonBar
, 1);
2747 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
,
2748 buttonbar_callback
, buttonbar_event
);
2749 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
2750 bb
->visible
= visible
;
2751 widget_want_hotkey (bb
->widget
, 1);
2752 widget_want_cursor (bb
->widget
, 0);
2753 bb
->btn_width
= buttonbat_get_button_width ();
2759 set_label_text (WButtonBar
*bb
, int lc_index
, const char *text
)
2761 g_free (bb
->labels
[lc_index
- 1].text
);
2762 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
2765 /* Find ButtonBar widget in the dialog */
2767 find_buttonbar (const Dlg_head
*h
)
2769 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
2773 buttonbar_set_label (WButtonBar
*bb
, int idx
, const char *text
,
2774 const struct global_keymap_t
*keymap
, const Widget
*receiver
)
2776 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
)) {
2777 unsigned long command
= CK_Ignore_Key
;
2780 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
2782 if ((text
== NULL
) || (text
[0] == '\0'))
2783 set_label_text (bb
, idx
, "");
2785 set_label_text (bb
, idx
, text
);
2787 bb
->labels
[idx
- 1].command
= command
;
2788 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
2793 buttonbar_set_visible (WButtonBar
*bb
, gboolean visible
)
2795 bb
->visible
= visible
;
2799 buttonbar_redraw (WButtonBar
*bb
)
2802 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
2806 groupbox_callback (Widget
*w
, widget_msg_t msg
, int parm
)
2808 WGroupbox
*g
= (WGroupbox
*) w
;
2815 return MSG_NOT_HANDLED
;
2818 tty_setcolor (COLOR_NORMAL
);
2819 draw_box (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2820 g
->widget
.x
- g
->widget
.parent
->x
, g
->widget
.lines
,
2823 tty_setcolor (COLOR_HOT_NORMAL
);
2824 dlg_move (g
->widget
.parent
, g
->widget
.y
- g
->widget
.parent
->y
,
2825 g
->widget
.x
- g
->widget
.parent
->x
+ 1);
2826 tty_print_string (g
->title
);
2829 case WIDGET_DESTROY
:
2834 return default_proc (msg
, parm
);
2839 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
2841 WGroupbox
*g
= g_new (WGroupbox
, 1);
2843 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
2845 g
->widget
.options
&= ~W_WANT_CURSOR
;
2846 widget_want_hotkey (g
->widget
, 0);
2848 /* Strip existing spaces, add one space before and after the title */
2851 t
= g_strstrip (g_strdup (title
));
2852 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);