1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
11 2009, 2010 Andrew Borodin
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 * \brief Source: widgets
42 #include <sys/types.h>
44 #include "lib/global.h"
46 #include "lib/tty/tty.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
50 #include "lib/mcconfig.h" /* for history loading and saving */
51 #include "lib/vfs/mc-vfs/vfs.h"
52 #include "lib/fileloc.h"
53 #include "lib/strutil.h"
59 #include "cmddef.h" /* CK_ cmd name const */
60 #include "keybind.h" /* global_keymap_t */
61 #include "panel.h" /* current_panel */
62 #include "main.h" /* confirm_history_cleanup */
63 #include "setup.h" /* num_history_items_recorded */
64 #include "clipboard.h" /* copy_file_to_ext_clip, paste_to_file_from_ext_clip */
67 widget_selectcolor (Widget
* w
, gboolean focused
, gboolean hotkey
)
69 Dlg_head
*h
= w
->owner
;
72 if ((w
->options
& W_DISABLED
) != 0)
73 color
= DISABLED_COLOR
;
77 color
= h
->color
[DLG_COLOR_HOT_FOCUS
];
79 color
= h
->color
[DLG_COLOR_HOT_NORMAL
];
84 color
= h
->color
[DLG_COLOR_FOCUS
];
86 color
= h
->color
[DLG_COLOR_NORMAL
];
93 parse_hotkey (const char *text
)
95 struct hotkey_t result
;
98 /* search for '&', that is not on the of text */
99 cp
= strchr (text
, '&');
100 if (cp
!= NULL
&& cp
[1] != '\0')
102 result
.start
= g_strndup (text
, cp
- text
);
106 p
= str_cget_next_char (cp
);
107 result
.hotkey
= g_strndup (cp
, p
- cp
);
110 result
.end
= g_strdup (cp
);
114 result
.start
= g_strdup (text
);
115 result
.hotkey
= NULL
;
123 release_hotkey (const struct hotkey_t hotkey
)
125 g_free (hotkey
.start
);
126 g_free (hotkey
.hotkey
);
131 hotkey_width (const struct hotkey_t hotkey
)
135 result
= str_term_width1 (hotkey
.start
);
136 result
+= (hotkey
.hotkey
!= NULL
) ? str_term_width1 (hotkey
.hotkey
) : 0;
137 result
+= (hotkey
.end
!= NULL
) ? str_term_width1 (hotkey
.end
) : 0;
142 draw_hotkey (Widget
* w
, const struct hotkey_t hotkey
, gboolean focused
)
144 widget_selectcolor (w
, focused
, FALSE
);
145 tty_print_string (hotkey
.start
);
147 if (hotkey
.hotkey
!= NULL
)
149 widget_selectcolor (w
, focused
, TRUE
);
150 tty_print_string (hotkey
.hotkey
);
151 widget_selectcolor (w
, focused
, FALSE
);
154 if (hotkey
.end
!= NULL
)
155 tty_print_string (hotkey
.end
);
159 /* Default callback for widgets */
161 default_proc (widget_msg_t msg
, int parm
)
177 return MSG_NOT_HANDLED
;
181 static int button_event (Gpm_Event
* event
, void *);
186 button_callback (Widget
* w
, widget_msg_t msg
, int parm
)
188 WButton
*b
= (WButton
*) w
;
191 Dlg_head
*h
= b
->widget
.owner
;
197 * Don't let the default button steal Enter from the current
198 * button. This is a workaround for the flawed event model
199 * when hotkeys are sent to all widgets before the key is
200 * handled by the current widget.
202 if (parm
== '\n' && (Widget
*) h
->current
->data
== &b
->widget
)
204 button_callback (w
, WIDGET_KEY
, ' ');
208 if (parm
== '\n' && b
->flags
== DEFPUSH_BUTTON
)
210 button_callback (w
, WIDGET_KEY
, ' ');
214 if (b
->text
.hotkey
!= NULL
)
216 if (g_ascii_tolower ((gchar
) b
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
218 button_callback (w
, WIDGET_KEY
, ' ');
222 return MSG_NOT_HANDLED
;
225 if (parm
!= ' ' && parm
!= '\n')
226 return MSG_NOT_HANDLED
;
229 stop
= (*b
->callback
) (b
, b
->action
);
230 if (!b
->callback
|| stop
)
232 h
->ret_value
= b
->action
;
254 widget_move (&b
->widget
, 0, b
->hotpos
+ off
);
260 if (msg
== WIDGET_UNFOCUS
)
262 else if (msg
== WIDGET_FOCUS
)
265 widget_selectcolor (w
, b
->selected
, FALSE
);
266 widget_move (w
, 0, 0);
271 tty_print_string ("[< ");
274 tty_print_string ("[ ");
277 tty_print_string ("[");
284 draw_hotkey (w
, b
->text
, b
->selected
);
289 tty_print_string (" >]");
292 tty_print_string (" ]");
295 tty_print_string ("]");
301 release_hotkey (b
->text
);
305 return default_proc (msg
, parm
);
310 button_event (Gpm_Event
* event
, void *data
)
314 if (event
->type
& (GPM_DOWN
| GPM_UP
))
316 Dlg_head
*h
= b
->widget
.owner
;
317 dlg_select_widget (b
);
318 if (event
->type
& GPM_UP
)
320 button_callback ((Widget
*) data
, WIDGET_KEY
, ' ');
321 h
->callback (h
, &b
->widget
, DLG_POST_KEY
, ' ', NULL
);
329 button_get_len (const WButton
* b
)
331 int ret
= hotkey_width (b
->text
);
351 button_new (int y
, int x
, int action
, int flags
, const char *text
, bcback callback
)
353 WButton
*b
= g_new (WButton
, 1);
357 b
->text
= parse_hotkey (text
);
359 init_widget (&b
->widget
, y
, x
, 1, button_get_len (b
), button_callback
, button_event
);
362 b
->callback
= callback
;
363 widget_want_hotkey (b
->widget
, 1);
364 b
->hotpos
= (b
->text
.hotkey
!= NULL
) ? str_term_width1 (b
->text
.start
) : -1;
370 button_get_text (const WButton
* b
)
372 if (b
->text
.hotkey
!= NULL
)
373 return g_strconcat (b
->text
.start
, "&", b
->text
.hotkey
, b
->text
.end
, (char *) NULL
);
375 return g_strdup (b
->text
.start
);
379 button_set_text (WButton
* b
, const char *text
)
381 release_hotkey (b
->text
);
382 b
->text
= parse_hotkey (text
);
383 b
->widget
.cols
= button_get_len (b
);
384 dlg_redraw (b
->widget
.owner
);
388 /* Radio button widget */
389 static int radio_event (Gpm_Event
* event
, void *);
392 radio_callback (Widget
* w
, widget_msg_t msg
, int parm
)
394 WRadio
*r
= (WRadio
*) w
;
396 Dlg_head
*h
= r
->widget
.owner
;
402 int lp
= g_ascii_tolower ((gchar
) parm
);
404 for (i
= 0; i
< r
->count
; i
++)
406 if (r
->texts
[i
].hotkey
!= NULL
)
408 int c
= g_ascii_tolower ((gchar
) r
->texts
[i
].hotkey
[0]);
415 radio_callback (w
, WIDGET_KEY
, ' ');
420 return MSG_NOT_HANDLED
;
427 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
428 radio_callback (w
, WIDGET_FOCUS
, ' ');
438 return MSG_NOT_HANDLED
;
442 if (r
->count
- 1 > r
->pos
)
448 return MSG_NOT_HANDLED
;
451 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
452 radio_callback (w
, WIDGET_FOCUS
, ' ');
453 widget_move (&r
->widget
, r
->pos
, 1);
459 for (i
= 0; i
< r
->count
; i
++)
461 const gboolean focused
= (i
== r
->pos
&& msg
== WIDGET_FOCUS
);
462 widget_selectcolor (w
, focused
, FALSE
);
463 widget_move (&r
->widget
, i
, 0);
464 tty_draw_hline (r
->widget
.y
+ i
, r
->widget
.x
, ' ', r
->widget
.cols
);
465 tty_print_string ((r
->sel
== i
) ? "(*) " : "( ) ");
466 draw_hotkey (w
, r
->texts
[i
], focused
);
471 for (i
= 0; i
< r
->count
; i
++)
473 release_hotkey (r
->texts
[i
]);
479 return default_proc (msg
, parm
);
484 radio_event (Gpm_Event
* event
, void *data
)
489 if (event
->type
& (GPM_DOWN
| GPM_UP
))
491 Dlg_head
*h
= r
->widget
.owner
;
493 r
->pos
= event
->y
- 1;
494 dlg_select_widget (r
);
495 if (event
->type
& GPM_UP
)
497 radio_callback (w
, WIDGET_KEY
, ' ');
498 radio_callback (w
, WIDGET_FOCUS
, 0);
499 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
507 radio_new (int y
, int x
, int count
, const char **texts
)
509 WRadio
*result
= g_new (WRadio
, 1);
512 /* Compute the longest string */
513 result
->texts
= g_new (struct hotkey_t
, count
);
516 for (i
= 0; i
< count
; i
++)
518 result
->texts
[i
] = parse_hotkey (texts
[i
]);
519 m
= hotkey_width (result
->texts
[i
]);
524 init_widget (&result
->widget
, y
, x
, count
, 4 + max
, radio_callback
, radio_event
);
525 /* 4 is width of "(*) " */
529 result
->count
= count
;
530 widget_want_hotkey (result
->widget
, 1);
536 /* Checkbutton widget */
538 static int check_event (Gpm_Event
* event
, void *);
541 check_callback (Widget
* w
, widget_msg_t msg
, int parm
)
543 WCheck
*c
= (WCheck
*) w
;
544 Dlg_head
*h
= c
->widget
.owner
;
549 if (c
->text
.hotkey
!= NULL
)
551 if (g_ascii_tolower ((gchar
) c
->text
.hotkey
[0]) == g_ascii_tolower ((gchar
) parm
))
554 check_callback (w
, WIDGET_KEY
, ' '); /* make action */
558 return MSG_NOT_HANDLED
;
562 return MSG_NOT_HANDLED
;
564 c
->state
^= C_CHANGE
;
565 h
->callback (h
, w
, DLG_ACTION
, 0, NULL
);
566 check_callback (w
, WIDGET_FOCUS
, ' ');
570 widget_move (&c
->widget
, 0, 1);
576 widget_selectcolor (w
, msg
== WIDGET_FOCUS
, FALSE
);
577 widget_move (&c
->widget
, 0, 0);
578 tty_print_string ((c
->state
& C_BOOL
) ? "[x] " : "[ ] ");
579 draw_hotkey (w
, c
->text
, msg
== WIDGET_FOCUS
);
583 release_hotkey (c
->text
);
587 return default_proc (msg
, parm
);
592 check_event (Gpm_Event
* event
, void *data
)
597 if (event
->type
& (GPM_DOWN
| GPM_UP
))
599 Dlg_head
*h
= c
->widget
.owner
;
601 dlg_select_widget (c
);
602 if (event
->type
& GPM_UP
)
604 check_callback (w
, WIDGET_KEY
, ' ');
605 check_callback (w
, WIDGET_FOCUS
, 0);
606 h
->callback (h
, w
, DLG_POST_KEY
, ' ', NULL
);
614 check_new (int y
, int x
, int state
, const char *text
)
616 WCheck
*c
= g_new (WCheck
, 1);
618 c
->text
= parse_hotkey (text
);
620 init_widget (&c
->widget
, y
, x
, 1, 4 + hotkey_width (c
->text
), check_callback
, check_event
);
621 /* 4 is width of "[X] " */
622 c
->state
= state
? C_BOOL
: 0;
623 widget_want_hotkey (c
->widget
, 1);
629 save_text_to_clip_file (const char *text
)
636 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
637 file
= mc_open (fname
, O_CREAT
| O_WRONLY
| O_TRUNC
,
638 S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
| O_BINARY
);
644 str_len
= strlen (text
);
645 ret
= mc_write (file
, (char *) text
, str_len
);
647 return ret
== (ssize_t
) str_len
;
651 load_text_from_clip_file (char **text
)
656 gboolean first
= TRUE
;
658 fname
= g_build_filename (home_dir
, EDIT_CLIP_FILE
, NULL
);
659 f
= fopen (fname
, "r");
667 while (fgets (buf
, sizeof (buf
), f
))
674 if (buf
[len
- 1] == '\n')
680 *text
= g_strdup (buf
);
684 /* remove \n on EOL */
687 tmp
= g_strconcat (*text
, " ", buf
, (char *) NULL
);
696 return (*text
!= NULL
);
700 panel_save_curent_file_to_clip_file (void)
704 if (current_panel
->marked
== 0)
705 res
= save_text_to_clip_file (selection (current_panel
)->fname
);
709 gboolean first
= TRUE
;
712 for (i
= 0; i
< current_panel
->count
; i
++)
713 if (current_panel
->dir
.list
[i
].f
.marked
!= 0)
714 { /* Skip the unmarked ones */
717 flist
= g_strdup (current_panel
->dir
.list
[i
].fname
);
722 /* Add empty lines after the file */
726 g_strconcat (flist
, "\n", current_panel
->dir
.list
[i
].fname
, (char *) NULL
);
734 res
= save_text_to_clip_file (flist
);
744 label_callback (Widget
* w
, widget_msg_t msg
, int parm
)
746 WLabel
*l
= (WLabel
*) w
;
747 Dlg_head
*h
= l
->widget
.owner
;
754 /* We don't want to get the focus */
756 return MSG_NOT_HANDLED
;
760 char *p
= l
->text
, *q
, c
= 0;
762 gboolean disabled
= (w
->options
& W_DISABLED
) != 0;
768 tty_setcolor (disabled
? DISABLED_COLOR
: DEFAULT_COLOR
);
770 tty_setcolor (disabled
? DISABLED_COLOR
: h
->color
[DLG_COLOR_NORMAL
]);
774 q
= strchr (p
, '\n');
781 widget_move (&l
->widget
, y
, 0);
782 tty_print_string (str_fit_to_term (p
, l
->widget
.cols
, J_LEFT
));
798 return default_proc (msg
, parm
);
803 label_set_text (WLabel
* label
, const char *text
)
805 int newcols
= label
->widget
.cols
;
808 if (label
->text
&& text
&& !strcmp (label
->text
, text
))
809 return; /* Flickering is not nice */
811 g_free (label
->text
);
815 label
->text
= g_strdup (text
);
816 if (label
->auto_adjust_cols
)
818 str_msg_term_size (text
, &newlines
, &newcols
);
819 if (newcols
> label
->widget
.cols
)
820 label
->widget
.cols
= newcols
;
821 if (newlines
> label
->widget
.lines
)
822 label
->widget
.lines
= newlines
;
828 if (label
->widget
.owner
)
829 label_callback ((Widget
*) label
, WIDGET_DRAW
, 0);
831 if (newcols
< label
->widget
.cols
)
832 label
->widget
.cols
= newcols
;
836 label_new (int y
, int x
, const char *text
)
843 str_msg_term_size (text
, &lines
, &cols
);
845 l
= g_new (WLabel
, 1);
846 init_widget (&l
->widget
, y
, x
, lines
, cols
, label_callback
, NULL
);
847 l
->text
= (text
!= NULL
) ? g_strdup (text
) : NULL
;
848 l
->auto_adjust_cols
= 1;
850 widget_want_cursor (l
->widget
, 0);
855 hline_callback (Widget
* w
, widget_msg_t msg
, int parm
)
857 WHLine
*l
= (WHLine
*) w
;
858 Dlg_head
*h
= l
->widget
.owner
;
864 if (l
->auto_adjust_cols
)
866 if (((w
->owner
->flags
& DLG_COMPACT
) != 0))
869 w
->cols
= w
->owner
->cols
;
873 w
->x
= w
->owner
->x
+ 1;
874 w
->cols
= w
->owner
->cols
- 2;
879 /* We don't want to get the focus */
880 return MSG_NOT_HANDLED
;
884 tty_setcolor (DEFAULT_COLOR
);
886 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
888 tty_draw_hline (w
->y
, w
->x
+ 1, ACS_HLINE
, w
->cols
- 2);
890 if (l
->auto_adjust_cols
)
892 widget_move (w
, 0, 0);
893 tty_print_alt_char (ACS_LTEE
, FALSE
);
894 widget_move (w
, 0, w
->cols
- 1);
895 tty_print_alt_char (ACS_RTEE
, FALSE
);
900 return default_proc (msg
, parm
);
906 hline_new (int y
, int x
, int width
)
912 l
= g_new (WHLine
, 1);
913 init_widget (&l
->widget
, y
, x
, lines
, cols
, hline_callback
, NULL
);
914 l
->auto_adjust_cols
= (width
< 0);
915 l
->transparent
= FALSE
;
916 widget_want_cursor (l
->widget
, 0);
920 /* Gauge widget (progress indicator) */
921 /* Currently width is hardcoded here for text mode */
925 gauge_callback (Widget
* w
, widget_msg_t msg
, int parm
)
927 WGauge
*g
= (WGauge
*) w
;
928 Dlg_head
*h
= g
->widget
.owner
;
930 if (msg
== WIDGET_INIT
)
933 /* We don't want to get the focus */
934 if (msg
== WIDGET_FOCUS
)
935 return MSG_NOT_HANDLED
;
937 if (msg
== WIDGET_DRAW
)
939 widget_move (&g
->widget
, 0, 0);
940 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
942 tty_printf ("%*s", gauge_len
, "");
945 int percentage
, columns
;
946 long total
= g
->max
, done
= g
->current
;
948 if (total
<= 0 || done
< 0)
955 while (total
> 65535)
960 percentage
= (200 * done
/ total
+ 1) / 2;
961 columns
= (2 * (gauge_len
- 7) * done
/ total
+ 1) / 2;
962 tty_print_char ('[');
963 if (g
->from_left_to_right
)
965 tty_setcolor (GAUGE_COLOR
);
966 tty_printf ("%*s", (int) columns
, "");
967 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
968 tty_printf ("%*s] %3d%%", (int) (gauge_len
- 7 - columns
), "", (int) percentage
);
972 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
973 tty_printf ("%*s", gauge_len
- columns
- 7, "");
974 tty_setcolor (GAUGE_COLOR
);
975 tty_printf ("%*s", columns
, "");
976 tty_setcolor (h
->color
[DLG_COLOR_NORMAL
]);
977 tty_printf ("] %3d%%", 100 * columns
/ (gauge_len
- 7), percentage
);
983 return default_proc (msg
, parm
);
987 gauge_set_value (WGauge
* g
, int max
, int current
)
989 if (g
->current
== current
&& g
->max
== max
)
990 return; /* Do not flicker */
992 max
= 1; /* I do not like division by zero :) */
994 g
->current
= current
;
996 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
1000 gauge_show (WGauge
* g
, int shown
)
1002 if (g
->shown
== shown
)
1005 gauge_callback ((Widget
*) g
, WIDGET_DRAW
, 0);
1009 gauge_new (int y
, int x
, int shown
, int max
, int current
)
1011 WGauge
*g
= g_new (WGauge
, 1);
1013 init_widget (&g
->widget
, y
, x
, 1, gauge_len
, gauge_callback
, NULL
);
1016 max
= 1; /* I do not like division by zero :) */
1018 g
->current
= current
;
1019 g
->from_left_to_right
= TRUE
;
1020 widget_want_cursor (g
->widget
, 0);
1027 /* {{{ history button */
1029 #define LARGE_HISTORY_BUTTON 1
1031 #ifdef LARGE_HISTORY_BUTTON
1032 #define HISTORY_BUTTON_WIDTH 3
1034 #define HISTORY_BUTTON_WIDTH 1
1037 #define should_show_history_button(in) \
1038 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1041 draw_history_button (WInput
* in
)
1044 gboolean disabled
= (((Widget
*) in
)->options
& W_DISABLED
) != 0;
1046 c
= in
->history
->next
? (in
->history
->prev
? '|' : 'v') : '^';
1047 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
);
1048 tty_setcolor (disabled
? DISABLED_COLOR
: in
->color
[WINPUTC_HISTORY
]);
1049 #ifdef LARGE_HISTORY_BUTTON
1052 h
= in
->widget
.owner
;
1053 tty_print_string ("[ ]");
1054 widget_move (&in
->widget
, 0, in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1);
1060 /* }}} history button */
1063 /* Input widgets now have a global kill ring */
1064 /* Pointer to killed data */
1065 static char *kill_buffer
= NULL
;
1068 input_set_markers (WInput
* in
, long m1
)
1074 input_mark_cmd (WInput
* in
, gboolean mark
)
1078 in
->highlight
= FALSE
;
1079 input_set_markers (in
, 0);
1083 in
->highlight
= TRUE
;
1084 input_set_markers (in
, in
->point
);
1089 input_eval_marks (WInput
* in
, long *start_mark
, long *end_mark
)
1093 *start_mark
= min (in
->mark
, in
->point
);
1094 *end_mark
= max (in
->mark
, in
->point
);
1099 *start_mark
= *end_mark
= 0;
1105 delete_region (WInput
* in
, int x_first
, int x_last
)
1107 int first
= min (x_first
, x_last
);
1108 int last
= max (x_first
, x_last
);
1111 input_mark_cmd (in
, FALSE
);
1113 last
= str_offset_to_pos (in
->buffer
, last
);
1114 first
= str_offset_to_pos (in
->buffer
, first
);
1115 len
= strlen (&in
->buffer
[last
]) + 1;
1116 memmove (&in
->buffer
[first
], &in
->buffer
[last
], len
);
1122 update_input (WInput
* in
, int clear_first
)
1124 int has_history
= 0;
1126 int buf_len
= str_length (in
->buffer
);
1130 if (should_show_history_button (in
))
1131 has_history
= HISTORY_BUTTON_WIDTH
;
1133 if (in
->disable_update
)
1136 pw
= str_term_width2 (in
->buffer
, in
->point
);
1138 /* Make the point visible */
1139 if ((pw
< in
->term_first_shown
) || (pw
>= in
->term_first_shown
+ in
->field_width
- has_history
))
1142 in
->term_first_shown
= pw
- (in
->field_width
/ 3);
1143 if (in
->term_first_shown
< 0)
1144 in
->term_first_shown
= 0;
1147 /* Adjust the mark */
1148 if (in
->mark
> buf_len
)
1152 draw_history_button (in
);
1154 if ((((Widget
*) in
)->options
& W_DISABLED
) != 0)
1155 tty_setcolor (DISABLED_COLOR
);
1157 tty_setcolor (in
->color
[WINPUTC_UNCHANGED
]);
1159 tty_setcolor (in
->color
[WINPUTC_MAIN
]);
1161 widget_move (&in
->widget
, 0, 0);
1163 if (!in
->is_password
)
1167 tty_print_string (str_term_substring (in
->buffer
, in
->term_first_shown
,
1168 in
->field_width
- has_history
));
1173 if (input_eval_marks (in
, &m1
, &m2
))
1175 tty_setcolor (in
->color
[WINPUTC_MAIN
]);
1176 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
,
1177 in
->field_width
- has_history
);
1178 tty_print_string (cp
);
1179 tty_setcolor (in
->color
[WINPUTC_MARK
]);
1180 if (m1
< in
->term_first_shown
)
1182 widget_move (&in
->widget
, 0, 0);
1183 tty_print_string (str_term_substring
1184 (in
->buffer
, in
->term_first_shown
,
1185 m2
- in
->term_first_shown
));
1190 widget_move (&in
->widget
, 0, m1
- in
->term_first_shown
);
1193 (in
->field_width
- has_history
) - (str_term_width2 (in
->buffer
, m1
) -
1194 in
->term_first_shown
));
1195 tty_print_string (str_term_substring (in
->buffer
, m1
, sel_width
));
1202 cp
= str_term_substring (in
->buffer
, in
->term_first_shown
, in
->field_width
- has_history
);
1203 for (i
= 0; i
< in
->field_width
- has_history
; i
++)
1207 tty_setcolor (in
->color
[WINPUTC_MAIN
]);
1208 tty_print_char ((cp
[0] != '\0') ? '*' : ' ');
1211 str_cnext_char (&cp
);
1220 winput_set_origin (WInput
* in
, int x
, int field_width
)
1223 in
->field_width
= in
->widget
.cols
= field_width
;
1224 update_input (in
, 0);
1227 /* {{{ history saving and loading */
1229 int num_history_items_recorded
= 60;
1232 This loads and saves the history of an input line to and from the
1233 widget. It is called with the widgets history name on creation of the
1234 widget, and returns the GList list. It stores histories in the file
1235 ~/.mc/history in using the profile code.
1237 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1238 function) then input_new assigns the default text to be the last text
1239 entered, or "" if not found.
1243 history_get (const char *input_name
)
1250 size_t keys_num
= 0;
1253 if (num_history_items_recorded
== 0) /* this is how to disable */
1255 if ((input_name
== NULL
) || (*input_name
== '\0'))
1258 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1259 cfg
= mc_config_init (profile
);
1261 /* get number of keys */
1262 keys
= mc_config_get_keys (cfg
, input_name
, &keys_num
);
1265 for (i
= 0; i
< keys_num
; i
++)
1268 g_snprintf (key
, sizeof (key
), "%lu", (unsigned long) i
);
1269 this_entry
= mc_config_get_string (cfg
, input_name
, key
, "");
1271 if (this_entry
!= NULL
)
1272 hist
= list_append_unique (hist
, this_entry
);
1275 mc_config_deinit (cfg
);
1278 /* return pointer to the last entry in the list */
1279 return g_list_last (hist
);
1283 history_put (const char *input_name
, GList
* h
)
1289 if (num_history_items_recorded
== 0) /* this is how to disable */
1291 if ((input_name
== NULL
) || (*input_name
== '\0'))
1296 profile
= g_build_filename (home_dir
, MC_USERCONF_DIR
, MC_HISTORY_FILE
, NULL
);
1298 i
= open (profile
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
1302 /* Make sure the history is only readable by the user */
1303 if (chmod (profile
, S_IRUSR
| S_IWUSR
) == -1 && errno
!= ENOENT
)
1309 /* go to end of list */
1310 h
= g_list_last (h
);
1312 /* go back 60 places */
1313 for (i
= 0; (i
< num_history_items_recorded
- 1) && (h
->prev
!= NULL
); i
++)
1314 h
= g_list_previous (h
);
1316 cfg
= mc_config_init (profile
);
1318 if (input_name
!= NULL
)
1319 mc_config_del_group (cfg
, input_name
);
1321 /* dump history into profile */
1322 for (i
= 0; h
!= NULL
; h
= g_list_next (h
))
1324 char *text
= (char *) h
->data
;
1326 /* We shouldn't have null entries, but let's be sure */
1330 g_snprintf (key
, sizeof (key
), "%d", i
++);
1331 mc_config_set_string (cfg
, input_name
, key
, text
);
1335 mc_config_save_file (cfg
, NULL
);
1336 mc_config_deinit (cfg
);
1340 /* }}} history saving and loading */
1343 /* {{{ history display */
1348 return _("History");
1359 dlg_hist_reposition (Dlg_head
* dlg_head
)
1361 dlg_hist_data
*data
;
1362 int x
= 0, y
, he
, wi
;
1365 if ((dlg_head
== NULL
) || (dlg_head
->data
== NULL
))
1366 return MSG_NOT_HANDLED
;
1368 data
= (dlg_hist_data
*) dlg_head
->data
;
1370 y
= data
->widget
->y
;
1371 he
= data
->count
+ 2;
1373 if (he
<= y
|| y
> (LINES
- 6))
1375 he
= min (he
, y
- 1);
1381 he
= min (he
, LINES
- y
);
1384 if (data
->widget
->x
> 2)
1385 x
= data
->widget
->x
- 2;
1387 wi
= data
->maxlen
+ 4;
1389 if ((wi
+ x
) > COLS
)
1391 wi
= min (wi
, COLS
);
1395 dlg_set_position (dlg_head
, y
, x
, y
+ he
, x
+ wi
);
1401 dlg_hist_callback (Dlg_head
* h
, Widget
* sender
, dlg_msg_t msg
, int parm
, void *data
)
1406 return dlg_hist_reposition (h
);
1409 return default_dlg_callback (h
, sender
, msg
, parm
, data
);
1414 show_hist (GList
** history
, Widget
* widget
)
1416 GList
*z
, *hlist
= NULL
, *hi
;
1417 size_t maxlen
, i
, count
= 0;
1419 Dlg_head
*query_dlg
;
1420 WListbox
*query_list
;
1421 dlg_hist_data hist_data
;
1423 if (*history
== NULL
)
1426 maxlen
= str_term_width1 (i18n_htitle ()) + 2;
1428 for (z
= *history
; z
!= NULL
; z
= g_list_previous (z
))
1432 i
= str_term_width1 ((char *) z
->data
);
1433 maxlen
= max (maxlen
, i
);
1436 entry
= g_new0 (WLEntry
, 1);
1437 /* history is being reverted here */
1438 entry
->text
= g_strdup ((char *) z
->data
);
1439 hlist
= g_list_prepend (hlist
, entry
);
1442 hist_data
.widget
= widget
;
1443 hist_data
.count
= count
;
1444 hist_data
.maxlen
= maxlen
;
1447 create_dlg (TRUE
, 0, 0, 4, 4, dialog_colors
, dlg_hist_callback
,
1448 "[History-query]", i18n_htitle (), DLG_COMPACT
);
1449 query_dlg
->data
= &hist_data
;
1451 query_list
= listbox_new (1, 1, 2, 2, TRUE
, NULL
);
1453 /* this call makes list stick to all sides of dialog, effectively make
1454 it be resized with dialog */
1455 add_widget_autopos (query_dlg
, query_list
, WPOS_KEEP_ALL
);
1457 /* to avoid diplicating of (calculating sizes in two places)
1458 code, call dlg_hist_callback function here, to set dialog and
1460 The main idea - create 4x4 dialog and add 2x2 list in
1461 center of it, and let dialog function resize it to needed
1463 dlg_hist_callback (query_dlg
, NULL
, DLG_RESIZE
, 0, NULL
);
1465 if (query_dlg
->y
< widget
->y
)
1467 /* draw list entries from bottom upto top */
1468 listbox_set_list (query_list
, hlist
);
1469 listbox_select_last (query_list
);
1473 /* draw list entries from top downto bottom */
1474 /* revert history direction */
1475 hlist
= g_list_reverse (hlist
);
1476 listbox_set_list (query_list
, hlist
);
1479 if (run_dlg (query_dlg
) != B_CANCEL
)
1483 listbox_get_current (query_list
, &q
, NULL
);
1488 /* get modified history from dialog */
1490 for (hi
= query_list
->list
; hi
!= NULL
; hi
= g_list_next (hi
))
1494 entry
= (WLEntry
*) hi
->data
;
1495 /* history is being reverted here again */
1496 z
= g_list_prepend (z
, entry
->text
);
1500 /* restore history direction */
1501 if (query_dlg
->y
< widget
->y
)
1502 z
= g_list_reverse (z
);
1504 destroy_dlg (query_dlg
);
1506 g_list_foreach (*history
, (GFunc
) g_free
, NULL
);
1507 g_list_free (*history
);
1508 *history
= g_list_last (z
);
1514 do_show_hist (WInput
* in
)
1518 r
= show_hist (&in
->history
, &in
->widget
);
1521 assign_text (in
, r
);
1526 /* }}} history display */
1529 input_destroy (WInput
* in
)
1533 fprintf (stderr
, "Internal error: null Input *\n");
1534 exit (EXIT_FAILURE
);
1539 if (in
->history
!= NULL
)
1541 if (!in
->is_password
&& (((Widget
*) in
)->owner
->ret_value
!= B_CANCEL
))
1542 history_put (in
->history_name
, in
->history
);
1544 in
->history
= g_list_first (in
->history
);
1545 g_list_foreach (in
->history
, (GFunc
) g_free
, NULL
);
1546 g_list_free (in
->history
);
1549 g_free (in
->buffer
);
1550 free_completions (in
);
1551 g_free (in
->history_name
);
1553 g_free (kill_buffer
);
1558 input_disable_update (WInput
* in
)
1560 in
->disable_update
++;
1564 input_enable_update (WInput
* in
)
1566 in
->disable_update
--;
1567 update_input (in
, 0);
1572 push_history (WInput
* in
, const char *text
)
1574 /* input widget where urls with passwords are entered without any
1576 const char *password_input_fields
[] = {
1577 " Link to a remote machine ",
1579 " SMB link to machine "
1581 const size_t ELEMENTS
= (sizeof (password_input_fields
) / sizeof (password_input_fields
[0]));
1591 for (i
= 0; i
< ELEMENTS
; i
++)
1592 password_input_fields
[i
] = _(password_input_fields
[i
]);
1595 t
= g_strstrip (g_strdup (text
));
1598 t
= g_strdup (empty
? "" : text
);
1600 if (in
->history_name
!= NULL
)
1602 /* FIXME: It is the strange code. Rewrite is needed. */
1604 const char *p
= in
->history_name
+ 3;
1606 for (i
= 0; i
< ELEMENTS
; i
++)
1607 if (strcmp (p
, password_input_fields
[i
]) == 0)
1610 strip_password (t
, i
>= ELEMENTS
);
1613 in
->history
= list_append_unique (in
->history
, t
);
1617 /* Cleans the input line and adds the current text to the history */
1619 new_input (WInput
* in
)
1621 push_history (in
, in
->buffer
);
1623 in
->buffer
[0] = '\0';
1627 in
->highlight
= FALSE
;
1628 free_completions (in
);
1629 update_input (in
, 0);
1633 move_buffer_backward (WInput
* in
, int start
, int end
)
1636 int str_len
= str_length (in
->buffer
);
1637 if (start
>= str_len
|| end
> str_len
+ 1)
1640 pos
= str_offset_to_pos (in
->buffer
, start
);
1641 len
= str_offset_to_pos (in
->buffer
, end
) - pos
;
1643 for (i
= pos
; in
->buffer
[i
+ len
- 1]; i
++)
1644 in
->buffer
[i
] = in
->buffer
[i
+ len
];
1648 insert_char (WInput
* in
, int c_code
)
1656 if (input_eval_marks (in
, &m1
, &m2
))
1657 delete_region (in
, m1
, m2
);
1660 return MSG_NOT_HANDLED
;
1662 if (in
->charpoint
>= MB_LEN_MAX
)
1665 in
->charbuf
[in
->charpoint
] = c_code
;
1668 res
= str_is_valid_char (in
->charbuf
, in
->charpoint
);
1672 in
->charpoint
= 0; /* broken multibyte char, skip */
1677 if (strlen (in
->buffer
) + 1 + in
->charpoint
>= in
->current_max_size
)
1679 /* Expand the buffer */
1680 size_t new_length
= in
->current_max_size
+ in
->field_width
+ in
->charpoint
;
1681 char *narea
= g_try_renew (char, in
->buffer
, new_length
);
1685 in
->current_max_size
= new_length
;
1689 if (strlen (in
->buffer
) + in
->charpoint
< in
->current_max_size
)
1691 /* bytes from begin */
1692 size_t ins_point
= str_offset_to_pos (in
->buffer
, in
->point
);
1694 size_t rest_bytes
= strlen (in
->buffer
+ ins_point
);
1696 for (i
= rest_bytes
+ 1; i
> 0; i
--)
1697 in
->buffer
[ins_point
+ i
+ in
->charpoint
- 1] = in
->buffer
[ins_point
+ i
- 1];
1699 memcpy (in
->buffer
+ ins_point
, in
->charbuf
, in
->charpoint
);
1708 beginning_of_line (WInput
* in
)
1715 end_of_line (WInput
* in
)
1717 in
->point
= str_length (in
->buffer
);
1722 backward_char (WInput
* in
)
1724 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1728 in
->point
-= str_cprev_noncomb_char (&act
, in
->buffer
);
1734 forward_char (WInput
* in
)
1736 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1739 in
->point
+= str_cnext_noncomb_char (&act
);
1745 forward_word (WInput
* in
)
1747 const char *p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1749 while (p
[0] != '\0' && (str_isspace (p
) || str_ispunct (p
)))
1751 str_cnext_char (&p
);
1754 while (p
[0] != '\0' && !str_isspace (p
) && !str_ispunct (p
))
1756 str_cnext_char (&p
);
1762 backward_word (WInput
* in
)
1767 for (p
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1768 (p
!= in
->buffer
) && (p
[0] == '\0'); str_cprev_char (&p
), in
->point
--);
1770 while (p
!= in
->buffer
)
1773 str_cprev_char (&p
);
1774 if (!str_isspace (p
) && !str_ispunct (p
))
1781 while (p
!= in
->buffer
)
1783 str_cprev_char (&p
);
1784 if (str_isspace (p
) || str_ispunct (p
))
1792 key_left (WInput
* in
)
1798 key_ctrl_left (WInput
* in
)
1804 key_right (WInput
* in
)
1810 key_ctrl_right (WInput
* in
)
1816 backward_delete (WInput
* in
)
1818 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1824 start
= in
->point
- str_cprev_noncomb_char (&act
, in
->buffer
);
1825 move_buffer_backward (in
, start
, in
->point
);
1832 delete_char (WInput
* in
)
1834 const char *act
= in
->buffer
+ str_offset_to_pos (in
->buffer
, in
->point
);
1835 int end
= in
->point
;
1837 end
+= str_cnext_noncomb_char (&act
);
1839 move_buffer_backward (in
, in
->point
, end
);
1845 copy_region (WInput
* in
, int x_first
, int x_last
)
1847 int first
= min (x_first
, x_last
);
1848 int last
= max (x_first
, x_last
);
1852 /* Copy selected files to clipboard */
1853 panel_save_curent_file_to_clip_file ();
1854 /* try use external clipboard utility */
1855 copy_file_to_ext_clip ();
1859 g_free (kill_buffer
);
1861 first
= str_offset_to_pos (in
->buffer
, first
);
1862 last
= str_offset_to_pos (in
->buffer
, last
);
1864 kill_buffer
= g_strndup (in
->buffer
+ first
, last
- first
);
1866 save_text_to_clip_file (kill_buffer
);
1867 /* try use external clipboard utility */
1868 copy_file_to_ext_clip ();
1872 kill_word (WInput
* in
)
1874 int old_point
= in
->point
;
1878 new_point
= in
->point
;
1879 in
->point
= old_point
;
1881 copy_region (in
, old_point
, new_point
);
1882 delete_region (in
, old_point
, new_point
);
1889 back_kill_word (WInput
* in
)
1891 int old_point
= in
->point
;
1895 new_point
= in
->point
;
1896 in
->point
= old_point
;
1898 copy_region (in
, old_point
, new_point
);
1899 delete_region (in
, old_point
, new_point
);
1904 set_mark (WInput
* in
)
1906 input_mark_cmd (in
, TRUE
);
1910 kill_save (WInput
* in
)
1912 copy_region (in
, in
->mark
, in
->point
);
1916 kill_region (WInput
* in
)
1919 delete_region (in
, in
->point
, in
->mark
);
1923 clear_region (WInput
* in
)
1925 delete_region (in
, in
->point
, in
->mark
);
1931 if (kill_buffer
!= NULL
)
1936 for (p
= kill_buffer
; *p
!= '\0'; p
++)
1937 insert_char (in
, *p
);
1943 kill_line (WInput
* in
)
1945 int chp
= str_offset_to_pos (in
->buffer
, in
->point
);
1946 g_free (kill_buffer
);
1947 kill_buffer
= g_strdup (&in
->buffer
[chp
]);
1948 in
->buffer
[chp
] = '\0';
1953 ins_from_clip (WInput
* in
)
1957 /* try use external clipboard utility */
1958 paste_to_file_from_ext_clip ();
1960 if (load_text_from_clip_file (&p
))
1964 for (pp
= p
; *pp
!= '\0'; pp
++)
1965 insert_char (in
, *pp
);
1972 assign_text (WInput
* in
, const char *text
)
1974 free_completions (in
);
1975 g_free (in
->buffer
);
1976 in
->buffer
= g_strdup (text
); /* was in->buffer->text */
1977 in
->current_max_size
= strlen (in
->buffer
) + 1;
1978 in
->point
= str_length (in
->buffer
);
1985 hist_prev (WInput
* in
)
1993 push_history (in
, in
->buffer
);
1995 prev
= g_list_previous (in
->history
);
1999 assign_text (in
, (char *) prev
->data
);
2005 hist_next (WInput
* in
)
2009 push_history (in
, in
->buffer
);
2010 assign_text (in
, "");
2017 if (!in
->history
->next
)
2019 assign_text (in
, "");
2023 in
->history
= g_list_next (in
->history
);
2024 assign_text (in
, (char *) in
->history
->data
);
2029 port_region_marked_for_delete (WInput
* in
)
2031 in
->buffer
[0] = '\0';
2038 input_execute_cmd (WInput
* in
, unsigned long command
)
2040 cb_ret_t res
= MSG_HANDLED
;
2042 /* a highlight command like shift-arrow */
2043 if (command
== CK_InputLeftHighlight
||
2044 command
== CK_InputRightHighlight
||
2045 command
== CK_InputWordLeftHighlight
||
2046 command
== CK_InputWordRightHighlight
||
2047 command
== CK_InputBolHighlight
|| command
== CK_InputEolHighlight
)
2051 input_mark_cmd (in
, FALSE
); /* clear */
2052 input_mark_cmd (in
, TRUE
); /* marking on */
2058 case CK_InputForwardWord
:
2059 case CK_InputBackwardWord
:
2060 case CK_InputForwardChar
:
2061 case CK_InputBackwardChar
:
2063 input_mark_cmd (in
, FALSE
);
2069 case CK_InputBolHighlight
:
2070 beginning_of_line (in
);
2073 case CK_InputEolHighlight
:
2076 case CK_InputMoveLeft
:
2077 case CK_InputLeftHighlight
:
2080 case CK_InputWordLeft
:
2081 case CK_InputWordLeftHighlight
:
2084 case CK_InputMoveRight
:
2085 case CK_InputRightHighlight
:
2088 case CK_InputWordRight
:
2089 case CK_InputWordRightHighlight
:
2090 key_ctrl_right (in
);
2092 case CK_InputBackwardChar
:
2095 case CK_InputBackwardWord
:
2098 case CK_InputForwardChar
:
2101 case CK_InputForwardWord
:
2104 case CK_InputBackwardDelete
:
2108 if (input_eval_marks (in
, &m1
, &m2
))
2109 delete_region (in
, m1
, m2
);
2113 backward_delete (in
);
2116 case CK_InputDeleteChar
:
2118 port_region_marked_for_delete (in
);
2119 else if (in
->highlight
)
2122 if (input_eval_marks (in
, &m1
, &m2
))
2123 delete_region (in
, m1
, m2
);
2128 case CK_InputKillWord
:
2131 case CK_InputBackwardKillWord
:
2132 back_kill_word (in
);
2134 case CK_InputSetMark
:
2137 case CK_InputKillRegion
:
2140 case CK_InputClearLine
:
2143 case CK_InputKillSave
:
2152 case CK_InputKillLine
:
2155 case CK_InputHistoryPrev
:
2158 case CK_InputHistoryNext
:
2161 case CK_InputHistoryShow
:
2164 case CK_InputComplete
:
2168 res
= MSG_NOT_HANDLED
;
2171 if (command
!= CK_InputLeftHighlight
&&
2172 command
!= CK_InputRightHighlight
&&
2173 command
!= CK_InputWordLeftHighlight
&&
2174 command
!= CK_InputWordRightHighlight
&&
2175 command
!= CK_InputBolHighlight
&& command
!= CK_InputEolHighlight
)
2177 in
->highlight
= FALSE
;
2183 /* This function is a test for a special input key used in complete.c */
2184 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2185 and 2 if it is a complete key */
2187 is_in_input_map (WInput
* in
, int key
)
2190 for (i
= 0; input_map
[i
].key
!= 0; i
++)
2191 if (key
== input_map
[i
].key
)
2193 input_execute_cmd (in
, input_map
[i
].command
);
2194 return (input_map
[i
].command
== CK_InputComplete
) ? 2 : 1;
2200 handle_char (WInput
* in
, int key
)
2205 v
= MSG_NOT_HANDLED
;
2209 free_completions (in
);
2210 v
= insert_char (in
, key
);
2211 update_input (in
, 1);
2215 for (i
= 0; input_map
[i
].key
; i
++)
2217 if (key
== input_map
[i
].key
)
2219 if (input_map
[i
].command
!= CK_InputComplete
)
2220 free_completions (in
);
2221 input_execute_cmd (in
, input_map
[i
].command
);
2222 update_input (in
, 1);
2227 if (input_map
[i
].command
== CK_Ignore_Key
)
2230 return MSG_NOT_HANDLED
;
2232 port_region_marked_for_delete (in
);
2233 free_completions (in
);
2234 v
= insert_char (in
, key
);
2236 update_input (in
, 1);
2240 /* Inserts text in input line */
2242 stuff (WInput
* in
, const char *text
, int insert_extra_space
)
2244 input_disable_update (in
);
2245 while (*text
!= '\0')
2246 handle_char (in
, (unsigned char) *text
++); /* unsigned extension char->int */
2247 if (insert_extra_space
)
2248 handle_char (in
, ' ');
2249 input_enable_update (in
);
2250 update_input (in
, 1);
2254 input_set_point (WInput
* in
, int pos
)
2256 int max_pos
= str_length (in
->buffer
);
2260 if (pos
!= in
->point
)
2261 free_completions (in
);
2264 update_input (in
, 1);
2268 input_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2270 WInput
*in
= (WInput
*) w
;
2276 if (parm
== XCTRL ('q'))
2279 v
= handle_char (in
, ascii_alpha_to_cntrl (tty_getch ()));
2284 /* Keys we want others to handle */
2285 if (parm
== KEY_UP
|| parm
== KEY_DOWN
|| parm
== ESC_CHAR
2286 || parm
== KEY_F (10) || parm
== '\n')
2287 return MSG_NOT_HANDLED
;
2289 /* When pasting multiline text, insert literal Enter */
2290 if ((parm
& ~KEY_M_MASK
) == '\n')
2293 v
= handle_char (in
, '\n');
2298 return handle_char (in
, parm
);
2300 case WIDGET_COMMAND
:
2301 return input_execute_cmd (in
, parm
);
2304 case WIDGET_UNFOCUS
:
2306 update_input (in
, 0);
2310 widget_move (&in
->widget
, 0, str_term_width2 (in
->buffer
, in
->point
)
2311 - in
->term_first_shown
);
2314 case WIDGET_DESTROY
:
2319 return default_proc (msg
, parm
);
2324 input_event (Gpm_Event
* event
, void *data
)
2328 if (event
->type
& GPM_DOWN
)
2331 input_mark_cmd (in
, FALSE
);
2333 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2335 dlg_select_widget (in
);
2337 if (event
->x
>= in
->field_width
- HISTORY_BUTTON_WIDTH
+ 1
2338 && should_show_history_button (in
))
2344 in
->point
= str_length (in
->buffer
);
2345 if (event
->x
+ in
->term_first_shown
- 1 < str_term_width1 (in
->buffer
))
2346 in
->point
= str_column_to_pos (in
->buffer
, event
->x
+ in
->term_first_shown
- 1);
2348 update_input (in
, 1);
2350 /* A lone up mustn't do anything */
2351 if (in
->highlight
&& event
->type
& (GPM_UP
| GPM_DRAG
))
2354 if (!(event
->type
& GPM_DRAG
))
2355 input_mark_cmd (in
, TRUE
);
2360 /** Get default colors for WInput widget.
2361 * @returns default colors
2364 input_get_default_colors (void)
2366 static input_colors_t standart_colors
;
2368 standart_colors
[WINPUTC_MAIN
] = INPUT_COLOR
;
2369 standart_colors
[WINPUTC_MARK
] = INPUT_MARK_COLOR
;
2370 standart_colors
[WINPUTC_UNCHANGED
] = INPUT_UNCHANGED_COLOR
;
2371 standart_colors
[WINPUTC_HISTORY
] = INPUT_HISTORY_COLOR
;
2373 return standart_colors
;
2376 /** Create new instance of WInput object.
2377 * @param y Y coordinate
2378 * @param x X coordinate
2379 * @param input_colors Array of used colors
2380 * @param width Widget width
2381 * @param def_text Default text filled in widget
2382 * @param histname Name of history
2383 * @param completion_flags Flags for specify type of completions
2384 * @returns WInput object
2387 input_new (int y
, int x
, int *input_colors
, int width
, const char *def_text
,
2388 const char *histname
, INPUT_COMPLETE_FLAGS completion_flags
)
2390 WInput
*in
= g_new (WInput
, 1);
2391 size_t initial_buffer_len
;
2393 init_widget (&in
->widget
, y
, x
, 1, width
, input_callback
, input_event
);
2396 in
->history_name
= NULL
;
2398 if ((histname
!= NULL
) && (*histname
!= '\0'))
2400 in
->history_name
= g_strdup (histname
);
2401 in
->history
= history_get (histname
);
2404 if (def_text
== NULL
)
2406 else if (def_text
== INPUT_LAST_TEXT
)
2408 if ((in
->history
!= NULL
) && (in
->history
->data
!= NULL
))
2409 def_text
= (char *) in
->history
->data
;
2414 initial_buffer_len
= 1 + max ((size_t) width
, strlen (def_text
));
2415 in
->widget
.options
|= W_IS_INPUT
;
2416 in
->completions
= NULL
;
2417 in
->completion_flags
= completion_flags
;
2418 in
->current_max_size
= initial_buffer_len
;
2419 in
->buffer
= g_new (char, initial_buffer_len
);
2421 memmove (in
->color
, input_colors
, sizeof (input_colors_t
));
2423 in
->field_width
= width
;
2425 in
->highlight
= FALSE
;
2426 in
->term_first_shown
= 0;
2427 in
->disable_update
= 0;
2430 in
->is_password
= 0;
2432 strcpy (in
->buffer
, def_text
);
2433 in
->point
= str_length (in
->buffer
);
2440 /* Listbox widget */
2442 /* Should draw the scrollbar, but currently draws only
2443 * indications that there is more information
2447 listbox_entry_free (void *data
)
2455 listbox_drawscroll (WListbox
* l
)
2457 const int max_line
= l
->widget
.lines
- 1;
2461 /* Are we at the top? */
2462 widget_move (&l
->widget
, 0, l
->widget
.cols
);
2464 tty_print_one_vline (TRUE
);
2466 tty_print_char ('^');
2468 /* Are we at the bottom? */
2469 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
2470 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
2471 tty_print_one_vline (TRUE
);
2473 tty_print_char ('v');
2475 /* Now draw the nice relative pointer */
2477 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
2479 for (i
= 1; i
< max_line
; i
++)
2481 widget_move (&l
->widget
, i
, l
->widget
.cols
);
2483 tty_print_one_vline (TRUE
);
2485 tty_print_char ('*');
2490 listbox_draw (WListbox
* l
, gboolean focused
)
2492 const Dlg_head
*h
= l
->widget
.owner
;
2493 const gboolean disabled
= (((Widget
*) l
)->options
& W_DISABLED
) != 0;
2494 const int normalc
= disabled
? DISABLED_COLOR
: h
->color
[DLG_COLOR_NORMAL
];
2495 int selc
= disabled
? DISABLED_COLOR
: focused
? h
->color
[DLG_COLOR_HOT_FOCUS
] : h
->color
[DLG_COLOR_FOCUS
];
2502 le
= g_list_nth (l
->list
, l
->top
);
2503 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2504 pos
= (le
== NULL
) ? 0 : l
->top
;
2506 for (i
= 0; i
< l
->widget
.lines
; i
++)
2510 /* Display the entry */
2511 if (pos
== l
->pos
&& sel_line
== -1)
2514 tty_setcolor (selc
);
2517 tty_setcolor (normalc
);
2519 widget_move (&l
->widget
, i
, 1);
2521 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
2525 WLEntry
*e
= (WLEntry
*) le
->data
;
2527 le
= g_list_next (le
);
2531 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
2534 l
->cursor_y
= sel_line
;
2536 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
2538 tty_setcolor (normalc
);
2539 listbox_drawscroll (l
);
2544 listbox_check_hotkey (WListbox
* l
, int key
)
2549 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
2551 WLEntry
*e
= (WLEntry
*) le
->data
;
2553 if (e
->hotkey
== key
)
2560 /* Selects the last entry and scrolls the list to the bottom */
2562 listbox_select_last (WListbox
* l
)
2564 l
->pos
= l
->count
- 1;
2565 l
->top
= (l
->count
> l
->widget
.lines
) ? (l
->count
- l
->widget
.lines
) : 0;
2568 /* Selects the first entry and scrolls the list to the top */
2570 listbox_select_first (WListbox
* l
)
2572 l
->pos
= l
->top
= 0;
2576 listbox_set_list (WListbox
* l
, GList
* list
)
2578 listbox_remove_list (l
);
2583 l
->top
= l
->pos
= 0;
2584 l
->count
= g_list_length (list
);
2589 listbox_remove_list (WListbox
* l
)
2591 if ((l
!= NULL
) && (l
->count
!= 0))
2593 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
2594 g_list_free (l
->list
);
2596 l
->count
= l
->pos
= l
->top
= 0;
2601 listbox_remove_current (WListbox
* l
)
2603 if ((l
!= NULL
) && (l
->count
!= 0))
2607 current
= g_list_nth (l
->list
, l
->pos
);
2608 l
->list
= g_list_remove_link (l
->list
, current
);
2609 listbox_entry_free ((WLEntry
*) current
->data
);
2610 g_list_free_1 (current
);
2614 l
->top
= l
->pos
= 0;
2615 else if (l
->pos
>= l
->count
)
2616 l
->pos
= l
->count
- 1;
2621 listbox_select_entry (WListbox
* l
, int dest
)
2625 gboolean top_seen
= FALSE
;
2631 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
2641 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
2642 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
2647 /* If we are unable to find it, set decent values */
2648 l
->pos
= l
->top
= 0;
2651 /* Selects from base the pos element */
2653 listbox_select_pos (WListbox
* l
, int base
, int pos
)
2655 int last
= l
->count
- 1;
2665 listbox_fwd (WListbox
* l
)
2667 if (l
->pos
+ 1 >= l
->count
)
2668 listbox_select_first (l
);
2670 listbox_select_entry (l
, l
->pos
+ 1);
2674 listbox_back (WListbox
* l
)
2677 listbox_select_last (l
);
2679 listbox_select_entry (l
, l
->pos
- 1);
2683 listbox_execute_cmd (WListbox
* l
, unsigned long command
)
2685 cb_ret_t ret
= MSG_HANDLED
;
2690 case CK_ListboxMoveUp
:
2693 case CK_ListboxMoveDown
:
2696 case CK_ListboxMoveHome
:
2697 listbox_select_first (l
);
2699 case CK_ListboxMoveEnd
:
2700 listbox_select_last (l
);
2702 case CK_ListboxMovePgUp
:
2703 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
2706 case CK_ListboxMovePgDn
:
2707 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
2710 case CK_ListboxDeleteItem
:
2713 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
2714 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
2716 listbox_remove_current (l
);
2717 if ((l
->top
> 0) && (is_last
|| is_more
))
2721 case CK_ListboxDeleteAll
:
2722 if (l
->deletable
&& confirm_history_cleanup
2723 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2724 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2725 _("Do you want clean this history?"),
2726 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
2727 listbox_remove_list (l
);
2730 ret
= MSG_NOT_HANDLED
;
2736 /* Return MSG_HANDLED if we want a redraw */
2738 listbox_key (WListbox
* l
, int key
)
2740 unsigned long command
;
2742 if (l
->list
== NULL
)
2743 return MSG_NOT_HANDLED
;
2745 /* focus on listbox item N by '0'..'9' keys */
2746 if (key
>= '0' && key
<= '9')
2748 int oldpos
= l
->pos
;
2749 listbox_select_entry (l
, key
- '0');
2751 /* need scroll to item? */
2752 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
2758 command
= lookup_keymap_command (listbox_map
, key
);
2759 if (command
== CK_Ignore_Key
)
2760 return MSG_NOT_HANDLED
;
2761 return listbox_execute_cmd (l
, command
);
2765 listbox_destroy (WListbox
* l
)
2767 listbox_remove_list (l
);
2771 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
2773 WListbox
*l
= (WListbox
*) w
;
2774 Dlg_head
*h
= l
->widget
.owner
;
2786 pos
= listbox_check_hotkey (l
, parm
);
2788 return MSG_NOT_HANDLED
;
2790 listbox_select_entry (l
, pos
);
2791 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2793 if (l
->cback
!= NULL
)
2794 action
= l
->cback (l
);
2796 action
= LISTBOX_DONE
;
2798 if (action
== LISTBOX_DONE
)
2800 h
->ret_value
= B_ENTER
;
2808 ret_code
= listbox_key (l
, parm
);
2809 if (ret_code
!= MSG_NOT_HANDLED
)
2811 listbox_draw (l
, TRUE
);
2812 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2816 case WIDGET_COMMAND
:
2817 return listbox_execute_cmd (l
, parm
);
2820 widget_move (&l
->widget
, l
->cursor_y
, 0);
2821 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
2825 case WIDGET_UNFOCUS
:
2827 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
2830 case WIDGET_DESTROY
:
2831 listbox_destroy (l
);
2834 case WIDGET_RESIZED
:
2838 return default_proc (msg
, parm
);
2843 listbox_event (Gpm_Event
* event
, void *data
)
2848 Dlg_head
*h
= l
->widget
.owner
;
2851 if (event
->type
& GPM_DOWN
)
2852 dlg_select_widget (l
);
2854 if (l
->list
== NULL
)
2857 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
2859 int ret
= MOU_REPEAT
;
2861 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
2865 for (i
= -event
->y
; i
>= 0; i
--)
2867 else if (event
->y
> l
->widget
.lines
)
2868 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
2870 else if (event
->buttons
& GPM_B_UP
)
2875 else if (event
->buttons
& GPM_B_DOWN
)
2881 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2883 /* We need to refresh ourselves since the dialog manager doesn't */
2884 /* know about this event */
2885 listbox_draw (l
, TRUE
);
2890 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
2894 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
2895 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
2898 dlg_select_widget (l
);
2899 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
2901 if (l
->cback
!= NULL
)
2902 action
= l
->cback (l
);
2904 action
= LISTBOX_DONE
;
2906 if (action
== LISTBOX_DONE
)
2908 h
->ret_value
= B_ENTER
;
2917 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback callback
)
2919 WListbox
*l
= g_new (WListbox
, 1);
2924 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
2927 l
->top
= l
->pos
= 0;
2929 l
->deletable
= deletable
;
2930 l
->cback
= callback
;
2931 l
->allow_duplicates
= TRUE
;
2932 l
->scrollbar
= !tty_is_slow ();
2933 widget_want_hotkey (l
->widget
, 1);
2934 widget_want_cursor (l
->widget
, 0);
2940 listbox_entry_cmp (const void *a
, const void *b
)
2942 const WLEntry
*ea
= (const WLEntry
*) a
;
2943 const WLEntry
*eb
= (const WLEntry
*) b
;
2945 return strcmp (ea
->text
, eb
->text
);
2948 /* Listbox item adding function */
2950 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
2954 case LISTBOX_APPEND_AT_END
:
2955 l
->list
= g_list_append (l
->list
, e
);
2958 case LISTBOX_APPEND_BEFORE
:
2959 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
2964 case LISTBOX_APPEND_AFTER
:
2965 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
2968 case LISTBOX_APPEND_SORTED
:
2969 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
2980 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
2987 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
2990 entry
= g_new (WLEntry
, 1);
2991 entry
->text
= g_strdup (text
);
2993 entry
->hotkey
= hotkey
;
2995 listbox_append_item (l
, entry
, pos
);
3001 listbox_search_text (WListbox
* l
, const char *text
)
3008 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
3010 WLEntry
*e
= (WLEntry
*) le
->data
;
3012 if (strcmp (e
->text
, text
) == 0)
3020 /* Returns the current string text as well as the associated extra data */
3022 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
3028 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
3033 *string
= ok
? e
->text
: NULL
;
3036 *extra
= ok
? e
->data
: NULL
;
3040 /* ButtonBar widget */
3042 /* returns TRUE if a function has been called, FALSE otherwise. */
3044 buttonbar_call (WButtonBar
* bb
, int i
)
3046 cb_ret_t ret
= MSG_NOT_HANDLED
;
3048 if ((bb
!= NULL
) && (bb
->labels
[i
].command
!= CK_Ignore_Key
))
3049 ret
= bb
->widget
.owner
->callback (bb
->widget
.owner
,
3050 (Widget
*) bb
, DLG_ACTION
,
3051 bb
->labels
[i
].command
, bb
->labels
[i
].receiver
);
3055 /* calculate positions of buttons; width is never less than 7 */
3057 buttonbar_init_button_positions (WButtonBar
* bb
)
3062 if (COLS
< BUTTONBAR_LABELS_NUM
* 7)
3064 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3066 if (pos
+ 7 <= COLS
)
3069 bb
->labels
[i
].end_coord
= pos
;
3074 /* Distribute the extra width in a way that the middle vertical line
3075 (between F5 and F6) aligns with the two panels. The extra width
3076 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3079 lc_div
= COLS
/ BUTTONBAR_LABELS_NUM
;
3080 mod
= COLS
% BUTTONBAR_LABELS_NUM
;
3082 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
/ 2; i
++)
3085 if (BUTTONBAR_LABELS_NUM
/ 2 - 1 - i
< mod
/ 2)
3088 bb
->labels
[i
].end_coord
= pos
;
3091 for (; i
< BUTTONBAR_LABELS_NUM
; i
++)
3094 if (BUTTONBAR_LABELS_NUM
- 1 - i
< (mod
+ 1) / 2)
3097 bb
->labels
[i
].end_coord
= pos
;
3102 /* return width of one button */
3104 buttonbar_get_button_width (const WButtonBar
* bb
, int i
)
3107 return bb
->labels
[0].end_coord
;
3108 return bb
->labels
[i
].end_coord
- bb
->labels
[i
- 1].end_coord
;
3112 buttonbar_get_button_by_x_coord (const WButtonBar
* bb
, int x
)
3116 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3117 if (bb
->labels
[i
].end_coord
> x
)
3124 buttonbar_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3126 WButtonBar
*bb
= (WButtonBar
*) w
;
3133 return MSG_NOT_HANDLED
;
3136 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3137 if (parm
== KEY_F (i
+ 1) && buttonbar_call (bb
, i
))
3139 return MSG_NOT_HANDLED
;
3144 buttonbar_init_button_positions (bb
);
3145 widget_move (&bb
->widget
, 0, 0);
3146 tty_setcolor (DEFAULT_COLOR
);
3147 tty_printf ("%-*s", bb
->widget
.cols
, "");
3148 widget_move (&bb
->widget
, 0, 0);
3150 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3154 width
= buttonbar_get_button_width (bb
, i
);
3158 tty_setcolor (BUTTONBAR_HOTKEY_COLOR
);
3159 tty_printf ("%2d", i
+ 1);
3161 tty_setcolor (BUTTONBAR_BUTTON_COLOR
);
3162 text
= (bb
->labels
[i
].text
!= NULL
) ? bb
->labels
[i
].text
: "";
3163 tty_print_string (str_fit_to_term (text
, width
- 2, J_LEFT_FIT
));
3168 case WIDGET_DESTROY
:
3169 for (i
= 0; i
< BUTTONBAR_LABELS_NUM
; i
++)
3170 g_free (bb
->labels
[i
].text
);
3174 return default_proc (msg
, parm
);
3179 buttonbar_event (Gpm_Event
* event
, void *data
)
3181 WButtonBar
*bb
= data
;
3184 if (!(event
->type
& GPM_UP
))
3188 button
= buttonbar_get_button_by_x_coord (bb
, event
->x
- 1);
3190 buttonbar_call (bb
, button
);
3195 buttonbar_new (gboolean visible
)
3199 bb
= g_new0 (WButtonBar
, 1);
3201 init_widget (&bb
->widget
, LINES
- 1, 0, 1, COLS
, buttonbar_callback
, buttonbar_event
);
3202 bb
->widget
.pos_flags
= WPOS_KEEP_HORZ
| WPOS_KEEP_BOTTOM
;
3203 bb
->visible
= visible
;
3204 widget_want_hotkey (bb
->widget
, 1);
3205 widget_want_cursor (bb
->widget
, 0);
3211 set_label_text (WButtonBar
* bb
, int lc_index
, const char *text
)
3213 g_free (bb
->labels
[lc_index
- 1].text
);
3214 bb
->labels
[lc_index
- 1].text
= g_strdup (text
);
3217 /* Find ButtonBar widget in the dialog */
3219 find_buttonbar (const Dlg_head
* h
)
3221 return (WButtonBar
*) find_widget_type (h
, buttonbar_callback
);
3225 buttonbar_set_label (WButtonBar
* bb
, int idx
, const char *text
,
3226 const struct global_keymap_t
*keymap
, const Widget
* receiver
)
3228 if ((bb
!= NULL
) && (idx
>= 1) && (idx
<= BUTTONBAR_LABELS_NUM
))
3230 unsigned long command
= CK_Ignore_Key
;
3233 command
= lookup_keymap_command (keymap
, KEY_F (idx
));
3235 if ((text
== NULL
) || (text
[0] == '\0'))
3236 set_label_text (bb
, idx
, "");
3238 set_label_text (bb
, idx
, text
);
3240 bb
->labels
[idx
- 1].command
= command
;
3241 bb
->labels
[idx
- 1].receiver
= (Widget
*) receiver
;
3246 buttonbar_set_visible (WButtonBar
* bb
, gboolean visible
)
3248 bb
->visible
= visible
;
3252 buttonbar_redraw (WButtonBar
* bb
)
3255 send_message ((Widget
*) bb
, WIDGET_DRAW
, 0);
3259 groupbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
3261 WGroupbox
*g
= (WGroupbox
*) w
;
3269 return MSG_NOT_HANDLED
;
3273 gboolean disabled
= (w
->options
& W_DISABLED
) != 0;
3274 tty_setcolor (disabled
? DISABLED_COLOR
: COLOR_NORMAL
);
3275 draw_box (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3276 g
->widget
.x
- g
->widget
.owner
->x
, g
->widget
.lines
, g
->widget
.cols
, TRUE
);
3278 if (g
->title
!= NULL
)
3280 tty_setcolor (disabled
? DISABLED_COLOR
: COLOR_TITLE
);
3281 dlg_move (g
->widget
.owner
, g
->widget
.y
- g
->widget
.owner
->y
,
3282 g
->widget
.x
- g
->widget
.owner
->x
+ 1);
3283 tty_print_string (g
->title
);
3288 case WIDGET_DESTROY
:
3293 return default_proc (msg
, parm
);
3298 groupbox_new (int y
, int x
, int height
, int width
, const char *title
)
3300 WGroupbox
*g
= g_new (WGroupbox
, 1);
3302 init_widget (&g
->widget
, y
, x
, height
, width
, groupbox_callback
, NULL
);
3304 g
->widget
.options
&= ~W_WANT_CURSOR
;
3305 widget_want_hotkey (g
->widget
, 0);
3307 /* Strip existing spaces, add one space before and after the title */
3311 t
= g_strstrip (g_strdup (title
));
3312 g
->title
= g_strconcat (" ", t
, " ", (char *) NULL
);