Changes into src directory:
[midnight-commander.git] / src / widget.c
blob094b2af91ca210e5cc8e520187f10b09e13bc4b0
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
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
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.
29 /** \file widget.c
30 * \brief Source: widgets
33 #include <config.h>
35 #include <assert.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <fcntl.h>
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 */
49 #include "lib/skin.h"
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"
55 #include "dialog.h"
56 #include "widget.h"
57 #include "wtools.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 */
64 const global_keymap_t *input_map;
66 static void
67 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
69 Dlg_head *h = w->parent;
71 tty_setcolor (hotkey
72 ? (focused
73 ? DLG_HOT_FOCUSC (h)
74 : DLG_HOT_NORMALC (h))
75 : (focused
76 ? DLG_FOCUSC (h)
77 : DLG_NORMALC (h)));
80 struct hotkey_t
81 parse_hotkey (const char *text)
83 struct hotkey_t result;
84 const char *cp, *p;
86 /* search for '&', that is not on the of text */
87 cp = strchr (text, '&');
88 if (cp != NULL && cp[1] != '\0')
90 result.start = g_strndup (text, cp - text);
92 /* skip '&' */
93 cp++;
94 p = str_cget_next_char (cp);
95 result.hotkey = g_strndup (cp, p - cp);
97 cp = p;
98 result.end = g_strdup (cp);
100 else
102 result.start = g_strdup (text);
103 result.hotkey = NULL;
104 result.end = NULL;
107 return result;
110 void
111 release_hotkey (const struct hotkey_t hotkey)
113 g_free (hotkey.start);
114 g_free (hotkey.hotkey);
115 g_free (hotkey.end);
119 hotkey_width (const struct hotkey_t hotkey)
121 int result;
123 result = str_term_width1 (hotkey.start);
124 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
125 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
126 return result;
129 static void
130 draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
132 widget_selectcolor (w, focused, FALSE);
133 tty_print_string (hotkey.start);
135 if (hotkey.hotkey != NULL)
137 widget_selectcolor (w, focused, TRUE);
138 tty_print_string (hotkey.hotkey);
139 widget_selectcolor (w, focused, FALSE);
142 if (hotkey.end != NULL)
143 tty_print_string (hotkey.end);
147 /* Default callback for widgets */
148 cb_ret_t
149 default_proc (widget_msg_t msg, int parm)
151 (void) parm;
153 switch (msg)
155 case WIDGET_INIT:
156 case WIDGET_FOCUS:
157 case WIDGET_UNFOCUS:
158 case WIDGET_DRAW:
159 case WIDGET_DESTROY:
160 case WIDGET_CURSOR:
161 case WIDGET_IDLE:
162 return MSG_HANDLED;
164 default:
165 return MSG_NOT_HANDLED;
169 static int button_event (Gpm_Event * event, void *);
171 int quote = 0;
173 static cb_ret_t
174 button_callback (Widget * w, widget_msg_t msg, int parm)
176 WButton *b = (WButton *) w;
177 int stop = 0;
178 int off = 0;
179 Dlg_head *h = b->widget.parent;
181 switch (msg)
183 case WIDGET_HOTKEY:
185 * Don't let the default button steal Enter from the current
186 * button. This is a workaround for the flawed event model
187 * when hotkeys are sent to all widgets before the key is
188 * handled by the current widget.
190 if (parm == '\n' && h->current == &b->widget)
192 button_callback (w, WIDGET_KEY, ' ');
193 return MSG_HANDLED;
196 if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
198 button_callback (w, WIDGET_KEY, ' ');
199 return MSG_HANDLED;
202 if (b->text.hotkey != NULL)
204 if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
206 button_callback (w, WIDGET_KEY, ' ');
207 return MSG_HANDLED;
210 return MSG_NOT_HANDLED;
212 case WIDGET_KEY:
213 if (parm != ' ' && parm != '\n')
214 return MSG_NOT_HANDLED;
216 if (b->callback)
217 stop = (*b->callback) (b->action);
218 if (!b->callback || stop)
220 h->ret_value = b->action;
221 dlg_stop (h);
223 return MSG_HANDLED;
225 case WIDGET_CURSOR:
226 switch (b->flags)
228 case DEFPUSH_BUTTON:
229 off = 3;
230 break;
231 case NORMAL_BUTTON:
232 off = 2;
233 break;
234 case NARROW_BUTTON:
235 off = 1;
236 break;
237 case HIDDEN_BUTTON:
238 default:
239 off = 0;
240 break;
242 widget_move (&b->widget, 0, b->hotpos + off);
243 return MSG_HANDLED;
245 case WIDGET_UNFOCUS:
246 case WIDGET_FOCUS:
247 case WIDGET_DRAW:
248 if (msg == WIDGET_UNFOCUS)
249 b->selected = 0;
250 else if (msg == WIDGET_FOCUS)
251 b->selected = 1;
253 widget_selectcolor (w, b->selected, FALSE);
254 widget_move (w, 0, 0);
256 switch (b->flags)
258 case DEFPUSH_BUTTON:
259 tty_print_string ("[< ");
260 break;
261 case NORMAL_BUTTON:
262 tty_print_string ("[ ");
263 break;
264 case NARROW_BUTTON:
265 tty_print_string ("[");
266 break;
267 case HIDDEN_BUTTON:
268 default:
269 return MSG_HANDLED;
272 draw_hotkey (w, b->text, b->selected);
274 switch (b->flags)
276 case DEFPUSH_BUTTON:
277 tty_print_string (" >]");
278 break;
279 case NORMAL_BUTTON:
280 tty_print_string (" ]");
281 break;
282 case NARROW_BUTTON:
283 tty_print_string ("]");
284 break;
286 return MSG_HANDLED;
288 case WIDGET_DESTROY:
289 release_hotkey (b->text);
290 return MSG_HANDLED;
292 default:
293 return default_proc (msg, parm);
297 static int
298 button_event (Gpm_Event * event, void *data)
300 WButton *b = data;
302 if (event->type & (GPM_DOWN | GPM_UP))
304 Dlg_head *h = b->widget.parent;
305 dlg_select_widget (b);
306 if (event->type & GPM_UP)
308 button_callback ((Widget *) data, WIDGET_KEY, ' ');
309 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
310 return MOU_NORMAL;
313 return MOU_NORMAL;
317 button_get_len (const WButton * b)
319 int ret = hotkey_width (b->text);
320 switch (b->flags)
322 case DEFPUSH_BUTTON:
323 ret += 6;
324 break;
325 case NORMAL_BUTTON:
326 ret += 4;
327 break;
328 case NARROW_BUTTON:
329 ret += 2;
330 break;
331 case HIDDEN_BUTTON:
332 default:
333 return 0;
335 return ret;
338 WButton *
339 button_new (int y, int x, int action, int flags, const char *text, bcback callback)
341 WButton *b = g_new (WButton, 1);
343 b->action = action;
344 b->flags = flags;
345 b->text = parse_hotkey (text);
347 init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
349 b->selected = 0;
350 b->callback = callback;
351 widget_want_hotkey (b->widget, 1);
352 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
354 return b;
357 const char *
358 button_get_text (const WButton * b)
360 if (b->text.hotkey != NULL)
361 return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
362 else
363 return g_strdup (b->text.start);
366 void
367 button_set_text (WButton * b, const char *text)
369 release_hotkey (b->text);
370 b->text = parse_hotkey (text);
371 b->widget.cols = button_get_len (b);
372 dlg_redraw (b->widget.parent);
376 /* Radio button widget */
377 static int radio_event (Gpm_Event * event, void *);
379 static cb_ret_t
380 radio_callback (Widget * w, widget_msg_t msg, int parm)
382 WRadio *r = (WRadio *) w;
383 int i;
384 Dlg_head *h = r->widget.parent;
386 switch (msg)
388 case WIDGET_HOTKEY:
390 int lp = g_ascii_tolower ((gchar) parm);
392 for (i = 0; i < r->count; i++)
394 if (r->texts[i].hotkey != NULL)
396 int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
398 if (c != lp)
399 continue;
400 r->pos = i;
402 /* Take action */
403 radio_callback (w, WIDGET_KEY, ' ');
404 return MSG_HANDLED;
408 return MSG_NOT_HANDLED;
410 case WIDGET_KEY:
411 switch (parm)
413 case ' ':
414 r->sel = r->pos;
415 h->callback (h, w, DLG_ACTION, 0, NULL);
416 radio_callback (w, WIDGET_FOCUS, ' ');
417 return MSG_HANDLED;
419 case KEY_UP:
420 case KEY_LEFT:
421 if (r->pos > 0)
423 r->pos--;
424 return MSG_HANDLED;
426 return MSG_NOT_HANDLED;
428 case KEY_DOWN:
429 case KEY_RIGHT:
430 if (r->count - 1 > r->pos)
432 r->pos++;
433 return MSG_HANDLED;
436 return MSG_NOT_HANDLED;
438 case WIDGET_CURSOR:
439 h->callback (h, w, DLG_ACTION, 0, NULL);
440 radio_callback (w, WIDGET_FOCUS, ' ');
441 widget_move (&r->widget, r->pos, 1);
442 return MSG_HANDLED;
444 case WIDGET_UNFOCUS:
445 case WIDGET_FOCUS:
446 case WIDGET_DRAW:
447 for (i = 0; i < r->count; i++)
449 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
450 widget_selectcolor (w, focused, FALSE);
451 widget_move (&r->widget, i, 0);
452 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
453 draw_hotkey (w, r->texts[i], focused);
455 return MSG_HANDLED;
457 case WIDGET_DESTROY:
458 for (i = 0; i < r->count; i++)
460 release_hotkey (r->texts[i]);
462 g_free (r->texts);
463 return MSG_HANDLED;
465 default:
466 return default_proc (msg, parm);
470 static int
471 radio_event (Gpm_Event * event, void *data)
473 WRadio *r = data;
474 Widget *w = data;
476 if (event->type & (GPM_DOWN | GPM_UP))
478 Dlg_head *h = r->widget.parent;
480 r->pos = event->y - 1;
481 dlg_select_widget (r);
482 if (event->type & GPM_UP)
484 radio_callback (w, WIDGET_KEY, ' ');
485 radio_callback (w, WIDGET_FOCUS, 0);
486 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
487 return MOU_NORMAL;
490 return MOU_NORMAL;
493 WRadio *
494 radio_new (int y, int x, int count, const char **texts)
496 WRadio *result = g_new (WRadio, 1);
497 int i, max, m;
499 /* Compute the longest string */
500 result->texts = g_new (struct hotkey_t, count);
502 max = 0;
503 for (i = 0; i < count; i++)
505 result->texts[i] = parse_hotkey (texts[i]);
506 m = hotkey_width (result->texts[i]);
507 if (m > max)
508 max = m;
511 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
512 result->state = 1;
513 result->pos = 0;
514 result->sel = 0;
515 result->count = count;
516 widget_want_hotkey (result->widget, 1);
518 return result;
522 /* Checkbutton widget */
524 static int check_event (Gpm_Event * event, void *);
526 static cb_ret_t
527 check_callback (Widget * w, widget_msg_t msg, int parm)
529 WCheck *c = (WCheck *) w;
530 Dlg_head *h = c->widget.parent;
532 switch (msg)
534 case WIDGET_HOTKEY:
535 if (c->text.hotkey != NULL)
537 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
540 check_callback (w, WIDGET_KEY, ' '); /* make action */
541 return MSG_HANDLED;
544 return MSG_NOT_HANDLED;
546 case WIDGET_KEY:
547 if (parm != ' ')
548 return MSG_NOT_HANDLED;
549 c->state ^= C_BOOL;
550 c->state ^= C_CHANGE;
551 h->callback (h, w, DLG_ACTION, 0, NULL);
552 check_callback (w, WIDGET_FOCUS, ' ');
553 return MSG_HANDLED;
555 case WIDGET_CURSOR:
556 widget_move (&c->widget, 0, 1);
557 return MSG_HANDLED;
559 case WIDGET_FOCUS:
560 case WIDGET_UNFOCUS:
561 case WIDGET_DRAW:
562 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
563 widget_move (&c->widget, 0, 0);
564 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
565 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
566 return MSG_HANDLED;
568 case WIDGET_DESTROY:
569 release_hotkey (c->text);
570 return MSG_HANDLED;
572 default:
573 return default_proc (msg, parm);
577 static int
578 check_event (Gpm_Event * event, void *data)
580 WCheck *c = data;
581 Widget *w = data;
583 if (event->type & (GPM_DOWN | GPM_UP))
585 Dlg_head *h = c->widget.parent;
587 dlg_select_widget (c);
588 if (event->type & GPM_UP)
590 check_callback (w, WIDGET_KEY, ' ');
591 check_callback (w, WIDGET_FOCUS, 0);
592 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
593 return MOU_NORMAL;
596 return MOU_NORMAL;
599 WCheck *
600 check_new (int y, int x, int state, const char *text)
602 WCheck *c = g_new (WCheck, 1);
604 c->text = parse_hotkey (text);
606 init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
607 c->state = state ? C_BOOL : 0;
608 widget_want_hotkey (c->widget, 1);
610 return c;
613 static gboolean
614 save_text_to_clip_file (const char *text)
616 int file;
617 char *fname = NULL;
618 ssize_t ret;
619 size_t str_len;
621 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
622 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
623 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
624 g_free (fname);
626 if (file == -1)
627 return FALSE;
629 str_len = strlen (text);
630 ret = mc_write (file, (char *) text, str_len);
631 mc_close (file);
632 return ret == (ssize_t) str_len;
635 static gboolean
636 load_text_from_clip_file (char **text)
638 char buf[BUF_LARGE];
639 FILE *f;
640 char *fname = NULL;
641 gboolean first = TRUE;
643 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
644 f = fopen (fname, "r");
645 g_free (fname);
647 if (f == NULL)
648 return FALSE;
650 *text = NULL;
652 while (fgets (buf, sizeof (buf), f))
654 size_t len;
656 len = strlen (buf);
657 if (len > 0)
659 if (buf[len - 1] == '\n')
660 buf[len - 1] = '\0';
662 if (first)
664 first = FALSE;
665 *text = g_strdup (buf);
667 else
669 /* remove \n on EOL */
670 char *tmp;
672 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
673 g_free (*text);
674 *text = tmp;
679 fclose (f);
681 return (*text != NULL);
684 static gboolean
685 panel_save_curent_file_to_clip_file (void)
687 gboolean res;
689 if (current_panel->marked == 0)
690 res = save_text_to_clip_file (selection (current_panel)->fname);
691 else
693 int i;
694 gboolean first = TRUE;
695 char *flist = NULL;
697 for (i = 0; i < current_panel->count; i++)
698 if (current_panel->dir.list[i].f.marked != 0)
699 { /* Skip the unmarked ones */
700 if (first)
702 flist = g_strdup (current_panel->dir.list[i].fname);
703 first = FALSE;
705 else
707 /* Add empty lines after the file */
708 char *tmp;
710 tmp =
711 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
712 g_free (flist);
713 flist = tmp;
717 if (flist != NULL)
719 res = save_text_to_clip_file (flist);
720 g_free (flist);
723 return res;
726 /* Label widget */
728 static cb_ret_t
729 label_callback (Widget * w, widget_msg_t msg, int parm)
731 WLabel *l = (WLabel *) w;
732 Dlg_head *h = l->widget.parent;
734 switch (msg)
736 case WIDGET_INIT:
737 return MSG_HANDLED;
739 /* We don't want to get the focus */
740 case WIDGET_FOCUS:
741 return MSG_NOT_HANDLED;
743 case WIDGET_DRAW:
745 char *p = l->text, *q, c = 0;
746 int y = 0;
748 if (!l->text)
749 return MSG_HANDLED;
751 if (l->transparent)
752 tty_setcolor (DEFAULT_COLOR);
753 else
754 tty_setcolor (DLG_NORMALC (h));
756 for (;;)
758 q = strchr (p, '\n');
759 if (q != NULL)
761 c = q[0];
762 q[0] = '\0';
765 widget_move (&l->widget, y, 0);
766 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
768 if (q == NULL)
769 break;
770 q[0] = c;
771 p = q + 1;
772 y++;
774 return MSG_HANDLED;
777 case WIDGET_DESTROY:
778 g_free (l->text);
779 return MSG_HANDLED;
781 default:
782 return default_proc (msg, parm);
786 void
787 label_set_text (WLabel * label, const char *text)
789 int newcols = label->widget.cols;
790 int newlines;
792 if (label->text && text && !strcmp (label->text, text))
793 return; /* Flickering is not nice */
795 g_free (label->text);
797 if (text != NULL)
799 label->text = g_strdup (text);
800 if (label->auto_adjust_cols)
802 str_msg_term_size (text, &newlines, &newcols);
803 if (newcols > label->widget.cols)
804 label->widget.cols = newcols;
805 if (newlines > label->widget.lines)
806 label->widget.lines = newlines;
809 else
810 label->text = NULL;
812 if (label->widget.parent)
813 label_callback ((Widget *) label, WIDGET_DRAW, 0);
815 if (newcols < label->widget.cols)
816 label->widget.cols = newcols;
819 WLabel *
820 label_new (int y, int x, const char *text)
822 WLabel *l;
823 int cols = 1;
824 int lines = 1;
826 if (text != NULL)
827 str_msg_term_size (text, &lines, &cols);
829 l = g_new (WLabel, 1);
830 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
831 l->text = (text != NULL) ? g_strdup (text) : NULL;
832 l->auto_adjust_cols = 1;
833 l->transparent = 0;
834 widget_want_cursor (l->widget, 0);
835 return l;
838 static cb_ret_t
839 hline_callback (Widget * w, widget_msg_t msg, int parm)
841 WHLine *l = (WHLine *) w;
842 Dlg_head *h = l->widget.parent;
844 switch (msg)
846 case WIDGET_INIT:
847 case WIDGET_RESIZED:
848 if (l->auto_adjust_cols)
850 if (((w->parent->flags & DLG_COMPACT) != 0))
852 w->x = w->parent->x;
853 w->cols = w->parent->cols;
855 else
857 w->x = w->parent->x + 1;
858 w->cols = w->parent->cols - 2;
862 case WIDGET_FOCUS:
863 /* We don't want to get the focus */
864 return MSG_NOT_HANDLED;
866 case WIDGET_DRAW:
867 if (l->transparent)
868 tty_setcolor (DEFAULT_COLOR);
869 else
870 tty_setcolor (DLG_NORMALC (h));
872 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
874 if (l->auto_adjust_cols)
876 widget_move (w, 0, 0);
877 tty_print_alt_char (ACS_LTEE, FALSE);
878 widget_move (w, 0, w->cols - 1);
879 tty_print_alt_char (ACS_RTEE, FALSE);
881 return MSG_HANDLED;
883 default:
884 return default_proc (msg, parm);
889 WHLine *
890 hline_new (int y, int x, int width)
892 WHLine *l;
893 int cols = width;
894 int lines = 1;
896 l = g_new (WHLine, 1);
897 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
898 l->auto_adjust_cols = (width < 0);
899 l->transparent = FALSE;
900 widget_want_cursor (l->widget, 0);
901 return l;
904 /* Gauge widget (progress indicator) */
905 /* Currently width is hardcoded here for text mode */
906 #define gauge_len 47
908 static cb_ret_t
909 gauge_callback (Widget * w, widget_msg_t msg, int parm)
911 WGauge *g = (WGauge *) w;
912 Dlg_head *h = g->widget.parent;
914 if (msg == WIDGET_INIT)
915 return MSG_HANDLED;
917 /* We don't want to get the focus */
918 if (msg == WIDGET_FOCUS)
919 return MSG_NOT_HANDLED;
921 if (msg == WIDGET_DRAW)
923 widget_move (&g->widget, 0, 0);
924 tty_setcolor (DLG_NORMALC (h));
925 if (!g->shown)
926 tty_printf ("%*s", gauge_len, "");
927 else
929 int percentage, columns;
930 long total = g->max, done = g->current;
932 if (total <= 0 || done < 0)
934 done = 0;
935 total = 100;
937 if (done > total)
938 done = total;
939 while (total > 65535)
941 total /= 256;
942 done /= 256;
944 percentage = (200 * done / total + 1) / 2;
945 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
946 tty_print_char ('[');
947 if (g->from_left_to_right)
949 tty_setcolor (GAUGE_COLOR);
950 tty_printf ("%*s", (int) columns, "");
951 tty_setcolor (DLG_NORMALC (h));
952 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
954 else
956 tty_setcolor (DLG_NORMALC (h));
957 tty_printf ("%*s", gauge_len - columns - 7, "");
958 tty_setcolor (GAUGE_COLOR);
959 tty_printf ("%*s", columns, "");
960 tty_setcolor (DLG_NORMALC (h));
961 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
964 return MSG_HANDLED;
967 return default_proc (msg, parm);
970 void
971 gauge_set_value (WGauge * g, int max, int current)
973 if (g->current == current && g->max == max)
974 return; /* Do not flicker */
975 if (max == 0)
976 max = 1; /* I do not like division by zero :) */
978 g->current = current;
979 g->max = max;
980 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
983 void
984 gauge_show (WGauge * g, int shown)
986 if (g->shown == shown)
987 return;
988 g->shown = shown;
989 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
992 WGauge *
993 gauge_new (int y, int x, int shown, int max, int current)
995 WGauge *g = g_new (WGauge, 1);
997 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
998 g->shown = shown;
999 if (max == 0)
1000 max = 1; /* I do not like division by zero :) */
1001 g->max = max;
1002 g->current = current;
1003 g->from_left_to_right = TRUE;
1004 widget_want_cursor (g->widget, 0);
1005 return g;
1009 /* Input widget */
1011 /* {{{ history button */
1013 #define LARGE_HISTORY_BUTTON 1
1015 #ifdef LARGE_HISTORY_BUTTON
1016 # define HISTORY_BUTTON_WIDTH 3
1017 #else
1018 # define HISTORY_BUTTON_WIDTH 1
1019 #endif
1021 #define should_show_history_button(in) \
1022 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1024 static void
1025 draw_history_button (WInput * in)
1027 char c;
1028 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1029 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1030 #ifdef LARGE_HISTORY_BUTTON
1032 Dlg_head *h;
1033 h = in->widget.parent;
1034 tty_setcolor (NORMAL_COLOR);
1035 tty_print_string ("[ ]");
1036 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1037 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1038 tty_print_char (c);
1040 #else
1041 tty_setcolor (MARKED_COLOR);
1042 tty_print_char (c);
1043 #endif
1046 /* }}} history button */
1049 /* Input widgets now have a global kill ring */
1050 /* Pointer to killed data */
1051 static char *kill_buffer = NULL;
1053 void
1054 update_input (WInput * in, int clear_first)
1056 int has_history = 0;
1057 int i;
1058 int buf_len = str_length (in->buffer);
1059 const char *cp;
1060 int pw;
1062 if (should_show_history_button (in))
1063 has_history = HISTORY_BUTTON_WIDTH;
1065 if (in->disable_update)
1066 return;
1068 pw = str_term_width2 (in->buffer, in->point);
1070 /* Make the point visible */
1071 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1074 in->term_first_shown = pw - (in->field_width / 3);
1075 if (in->term_first_shown < 0)
1076 in->term_first_shown = 0;
1079 /* Adjust the mark */
1080 if (in->mark > buf_len)
1081 in->mark = buf_len;
1083 if (has_history)
1084 draw_history_button (in);
1086 tty_setcolor (in->color);
1088 widget_move (&in->widget, 0, 0);
1090 if (!in->is_password)
1092 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1093 in->field_width - has_history));
1095 else
1097 cp = in->buffer;
1098 for (i = -in->term_first_shown; i < in->field_width - has_history; i++)
1100 if (i >= 0)
1102 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1104 if (cp[0] != '\0')
1105 str_cnext_char (&cp);
1109 if (clear_first)
1110 in->first = 0;
1113 void
1114 winput_set_origin (WInput * in, int x, int field_width)
1116 in->widget.x = x;
1117 in->field_width = in->widget.cols = field_width;
1118 update_input (in, 0);
1121 /* {{{ history saving and loading */
1123 int num_history_items_recorded = 60;
1126 This loads and saves the history of an input line to and from the
1127 widget. It is called with the widgets history name on creation of the
1128 widget, and returns the GList list. It stores histories in the file
1129 ~/.mc/history in using the profile code.
1131 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1132 function) then input_new assigns the default text to be the last text
1133 entered, or "" if not found.
1136 GList *
1137 history_get (const char *input_name)
1139 size_t i;
1140 GList *hist = NULL;
1141 char *profile;
1142 mc_config_t *cfg;
1143 char **keys;
1144 size_t keys_num = 0;
1145 char *this_entry;
1147 if (num_history_items_recorded == 0) /* this is how to disable */
1148 return NULL;
1149 if ((input_name == NULL) || (*input_name == '\0'))
1150 return NULL;
1152 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1153 cfg = mc_config_init (profile);
1155 /* get number of keys */
1156 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1157 g_strfreev (keys);
1159 for (i = 0; i < keys_num; i++)
1161 char key[BUF_TINY];
1162 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1163 this_entry = mc_config_get_string (cfg, input_name, key, "");
1165 if (this_entry != NULL)
1166 hist = list_append_unique (hist, this_entry);
1169 mc_config_deinit (cfg);
1170 g_free (profile);
1172 /* return pointer to the last entry in the list */
1173 return g_list_last (hist);
1176 void
1177 history_put (const char *input_name, GList * h)
1179 int i;
1180 char *profile;
1181 mc_config_t *cfg;
1183 if (num_history_items_recorded == 0) /* this is how to disable */
1184 return;
1185 if ((input_name == NULL) || (*input_name == '\0'))
1186 return;
1187 if (h == NULL)
1188 return;
1190 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1192 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1193 if (i != -1)
1194 close (i);
1196 /* Make sure the history is only readable by the user */
1197 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1199 g_free (profile);
1200 return;
1203 /* go to end of list */
1204 h = g_list_last (h);
1206 /* go back 60 places */
1207 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1208 h = g_list_previous (h);
1210 cfg = mc_config_init (profile);
1212 if (input_name != NULL)
1213 mc_config_del_group (cfg, input_name);
1215 /* dump history into profile */
1216 for (i = 0; h != NULL; h = g_list_next (h))
1218 char *text = (char *) h->data;
1220 /* We shouldn't have null entries, but let's be sure */
1221 if (text != NULL)
1223 char key[BUF_TINY];
1224 g_snprintf (key, sizeof (key), "%d", i++);
1225 mc_config_set_string (cfg, input_name, key, text);
1229 mc_config_save_file (cfg, NULL);
1230 mc_config_deinit (cfg);
1231 g_free (profile);
1234 /* }}} history saving and loading */
1237 /* {{{ history display */
1239 static const char *
1240 i18n_htitle (void)
1242 return _(" History ");
1245 typedef struct
1247 Widget *widget;
1248 size_t count;
1249 size_t maxlen;
1250 } dlg_hist_data;
1252 static cb_ret_t
1253 dlg_hist_reposition (Dlg_head * dlg_head)
1255 dlg_hist_data *data;
1256 int x = 0, y, he, wi;
1258 /* guard checks */
1259 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1260 return MSG_NOT_HANDLED;
1262 data = (dlg_hist_data *) dlg_head->data;
1264 y = data->widget->y;
1265 he = data->count + 2;
1267 if (he <= y || y > (LINES - 6))
1269 he = min (he, y - 1);
1270 y -= he;
1272 else
1274 y++;
1275 he = min (he, LINES - y);
1278 if (data->widget->x > 2)
1279 x = data->widget->x - 2;
1281 wi = data->maxlen + 4;
1283 if ((wi + x) > COLS)
1285 wi = min (wi, COLS);
1286 x = COLS - wi;
1289 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1291 return MSG_HANDLED;
1294 static cb_ret_t
1295 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1297 switch (msg)
1299 case DLG_RESIZE:
1300 return dlg_hist_reposition (h);
1302 default:
1303 return default_dlg_callback (h, sender, msg, parm, data);
1307 char *
1308 show_hist (GList ** history, Widget * widget)
1310 GList *z, *hlist = NULL, *hi;
1311 size_t maxlen, i, count = 0;
1312 char *r = NULL;
1313 Dlg_head *query_dlg;
1314 WListbox *query_list;
1315 dlg_hist_data hist_data;
1317 if (*history == NULL)
1318 return NULL;
1320 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1322 for (z = *history; z != NULL; z = g_list_previous (z))
1324 WLEntry *entry;
1326 i = str_term_width1 ((char *) z->data);
1327 maxlen = max (maxlen, i);
1328 count++;
1330 entry = g_new0 (WLEntry, 1);
1331 /* history is being reverted here */
1332 entry->text = g_strdup ((char *) z->data);
1333 hlist = g_list_prepend (hlist, entry);
1336 hist_data.widget = widget;
1337 hist_data.count = count;
1338 hist_data.maxlen = maxlen;
1340 query_dlg =
1341 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1342 "[History-query]", i18n_htitle (), DLG_COMPACT);
1343 query_dlg->data = &hist_data;
1345 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1347 /* this call makes list stick to all sides of dialog, effectively make
1348 it be resized with dialog */
1349 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1351 /* to avoid diplicating of (calculating sizes in two places)
1352 code, call dlg_hist_callback function here, to set dialog and
1353 controls positions.
1354 The main idea - create 4x4 dialog and add 2x2 list in
1355 center of it, and let dialog function resize it to needed
1356 size. */
1357 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1359 if (query_dlg->y < widget->y)
1361 /* draw list entries from bottom upto top */
1362 listbox_set_list (query_list, hlist);
1363 listbox_select_last (query_list);
1365 else
1367 /* draw list entries from top downto bottom */
1368 /* revert history direction */
1369 hlist = g_list_reverse (hlist);
1370 listbox_set_list (query_list, hlist);
1373 if (run_dlg (query_dlg) != B_CANCEL)
1375 char *q;
1377 listbox_get_current (query_list, &q, NULL);
1378 if (q != NULL)
1379 r = g_strdup (q);
1382 /* get modified history from dialog */
1383 z = NULL;
1384 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1386 WLEntry *entry;
1388 entry = (WLEntry *) hi->data;
1389 /* history is being reverted here again */
1390 z = g_list_prepend (z, entry->text);
1391 entry->text = NULL;
1394 destroy_dlg (query_dlg);
1396 /* restore history direction */
1397 if (query_dlg->y < widget->y)
1398 z = g_list_reverse (z);
1400 g_list_foreach (*history, (GFunc) g_free, NULL);
1401 g_list_free (*history);
1402 *history = g_list_last (z);
1404 return r;
1407 static void
1408 do_show_hist (WInput * in)
1410 char *r;
1412 r = show_hist (&in->history, &in->widget);
1413 if (r != NULL)
1415 assign_text (in, r);
1416 g_free (r);
1420 /* }}} history display */
1422 static void
1423 input_destroy (WInput * in)
1425 if (in == NULL)
1427 fprintf (stderr, "Internal error: null Input *\n");
1428 exit (1);
1431 new_input (in);
1433 if (in->history != NULL)
1435 if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
1436 history_put (in->history_name, in->history);
1438 in->history = g_list_first (in->history);
1439 g_list_foreach (in->history, (GFunc) g_free, NULL);
1440 g_list_free (in->history);
1443 g_free (in->buffer);
1444 free_completions (in);
1445 g_free (in->history_name);
1447 g_free (kill_buffer);
1448 kill_buffer = NULL;
1451 void
1452 input_disable_update (WInput * in)
1454 in->disable_update++;
1457 void
1458 input_enable_update (WInput * in)
1460 in->disable_update--;
1461 update_input (in, 0);
1465 static void
1466 push_history (WInput * in, const char *text)
1468 /* input widget where urls with passwords are entered without any
1469 vfs prefix */
1470 const char *password_input_fields[] = {
1471 N_(" Link to a remote machine "),
1472 N_(" FTP to machine "),
1473 N_(" SMB link to machine ")
1475 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1477 char *t;
1478 size_t i;
1479 gboolean empty;
1481 if (text == NULL)
1482 return;
1484 #ifdef ENABLE_NLS
1485 for (i = 0; i < ELEMENTS; i++)
1486 password_input_fields[i] = _(password_input_fields[i]);
1487 #endif
1489 t = g_strstrip (g_strdup (text));
1490 empty = *t == '\0';
1491 g_free (t);
1492 t = g_strdup (empty ? "" : text);
1494 if (in->history_name != NULL)
1496 const char *p = in->history_name + 3;
1498 for (i = 0; i < ELEMENTS; i++)
1499 if (strcmp (p, password_input_fields[i]) == 0)
1500 break;
1502 strip_password (t, i >= ELEMENTS);
1505 in->history = list_append_unique (in->history, t);
1506 in->need_push = 0;
1509 /* Cleans the input line and adds the current text to the history */
1510 void
1511 new_input (WInput * in)
1513 push_history (in, in->buffer);
1514 in->need_push = 1;
1515 in->buffer[0] = '\0';
1516 in->point = 0;
1517 in->charpoint = 0;
1518 in->mark = 0;
1519 free_completions (in);
1520 update_input (in, 0);
1523 static void
1524 move_buffer_backward (WInput * in, int start, int end)
1526 int i, pos, len;
1527 int str_len = str_length (in->buffer);
1528 if (start >= str_len || end > str_len + 1)
1529 return;
1531 pos = str_offset_to_pos (in->buffer, start);
1532 len = str_offset_to_pos (in->buffer, end) - pos;
1534 for (i = pos; in->buffer[i + len - 1]; i++)
1535 in->buffer[i] = in->buffer[i + len];
1538 static cb_ret_t
1539 insert_char (WInput * in, int c_code)
1541 size_t i;
1542 int res;
1544 if (c_code == -1)
1545 return MSG_NOT_HANDLED;
1547 if (in->charpoint >= MB_LEN_MAX)
1548 return MSG_HANDLED;
1550 in->charbuf[in->charpoint] = c_code;
1551 in->charpoint++;
1553 res = str_is_valid_char (in->charbuf, in->charpoint);
1554 if (res < 0)
1556 if (res != -2)
1557 in->charpoint = 0; /* broken multibyte char, skip */
1558 return MSG_HANDLED;
1561 in->need_push = 1;
1562 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1564 /* Expand the buffer */
1565 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1566 char *narea = g_try_renew (char, in->buffer, new_length);
1567 if (narea)
1569 in->buffer = narea;
1570 in->current_max_size = new_length;
1574 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1576 /* bytes from begin */
1577 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1578 /* move chars */
1579 size_t rest_bytes = strlen (in->buffer + ins_point);
1581 for (i = rest_bytes + 1; i > 0; i--)
1582 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1584 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1585 in->point++;
1588 in->charpoint = 0;
1589 return MSG_HANDLED;
1592 static void
1593 beginning_of_line (WInput * in)
1595 in->point = 0;
1596 in->charpoint = 0;
1599 static void
1600 end_of_line (WInput * in)
1602 in->point = str_length (in->buffer);
1603 in->charpoint = 0;
1606 static void
1607 backward_char (WInput * in)
1609 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1611 if (in->point > 0)
1613 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1615 in->charpoint = 0;
1618 static void
1619 forward_char (WInput * in)
1621 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1622 if (act[0] != '\0')
1624 in->point += str_cnext_noncomb_char (&act);
1626 in->charpoint = 0;
1629 static void
1630 forward_word (WInput * in)
1632 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1634 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1636 str_cnext_char (&p);
1637 in->point++;
1639 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1641 str_cnext_char (&p);
1642 in->point++;
1646 static void
1647 backward_word (WInput * in)
1649 const char *p;
1650 const char *p_tmp;
1652 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1653 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1655 while (p != in->buffer)
1657 p_tmp = p;
1658 str_cprev_char (&p);
1659 if (!str_isspace (p) && !str_ispunct (p))
1661 p = p_tmp;
1662 break;
1664 in->point--;
1666 while (p != in->buffer)
1668 str_cprev_char (&p);
1669 if (str_isspace (p) || str_ispunct (p))
1670 break;
1672 in->point--;
1676 static void
1677 key_left (WInput * in)
1679 backward_char (in);
1682 static void
1683 key_ctrl_left (WInput * in)
1685 backward_word (in);
1688 static void
1689 key_right (WInput * in)
1691 forward_char (in);
1694 static void
1695 key_ctrl_right (WInput * in)
1697 forward_word (in);
1699 static void
1700 backward_delete (WInput * in)
1702 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1703 int start;
1705 if (in->point == 0)
1706 return;
1708 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1709 move_buffer_backward (in, start, in->point);
1710 in->charpoint = 0;
1711 in->need_push = 1;
1712 in->point = start;
1715 static void
1716 delete_char (WInput * in)
1718 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1719 int end = in->point;
1721 end += str_cnext_noncomb_char (&act);
1723 move_buffer_backward (in, in->point, end);
1724 in->charpoint = 0;
1725 in->need_push = 1;
1728 static void
1729 copy_region (WInput * in, int x_first, int x_last)
1731 int first = min (x_first, x_last);
1732 int last = max (x_first, x_last);
1734 if (last == first)
1736 /* Copy selected files to clipboard */
1737 panel_save_curent_file_to_clip_file ();
1738 return;
1741 g_free (kill_buffer);
1743 first = str_offset_to_pos (in->buffer, first);
1744 last = str_offset_to_pos (in->buffer, last);
1746 kill_buffer = g_strndup (in->buffer + first, last - first);
1747 save_text_to_clip_file (kill_buffer);
1750 static void
1751 delete_region (WInput * in, int x_first, int x_last)
1753 int first = min (x_first, x_last);
1754 int last = max (x_first, x_last);
1755 size_t len;
1757 in->point = first;
1758 if (in->mark > first)
1759 in->mark = first;
1760 last = str_offset_to_pos (in->buffer, last);
1761 first = str_offset_to_pos (in->buffer, first);
1762 len = strlen (&in->buffer[last]) + 1;
1763 memmove (&in->buffer[first], &in->buffer[last], len);
1764 in->charpoint = 0;
1765 in->need_push = 1;
1768 static void
1769 kill_word (WInput * in)
1771 int old_point = in->point;
1772 int new_point;
1774 forward_word (in);
1775 new_point = in->point;
1776 in->point = old_point;
1778 copy_region (in, old_point, new_point);
1779 delete_region (in, old_point, new_point);
1780 in->need_push = 1;
1781 in->charpoint = 0;
1782 in->charpoint = 0;
1785 static void
1786 back_kill_word (WInput * in)
1788 int old_point = in->point;
1789 int new_point;
1791 backward_word (in);
1792 new_point = in->point;
1793 in->point = old_point;
1795 copy_region (in, old_point, new_point);
1796 delete_region (in, old_point, new_point);
1797 in->need_push = 1;
1800 static void
1801 set_mark (WInput * in)
1803 in->mark = in->point;
1806 static void
1807 kill_save (WInput * in)
1809 copy_region (in, in->mark, in->point);
1812 static void
1813 kill_region (WInput * in)
1815 kill_save (in);
1816 delete_region (in, in->point, in->mark);
1819 static void
1820 clear_region (WInput * in)
1822 delete_region (in, in->point, in->mark);
1825 static void
1826 yank (WInput * in)
1828 if (kill_buffer != NULL)
1830 char *p;
1832 in->charpoint = 0;
1833 for (p = kill_buffer; *p != '\0'; p++)
1834 insert_char (in, *p);
1835 in->charpoint = 0;
1839 static void
1840 kill_line (WInput * in)
1842 int chp = str_offset_to_pos (in->buffer, in->point);
1843 g_free (kill_buffer);
1844 kill_buffer = g_strdup (&in->buffer[chp]);
1845 in->buffer[chp] = '\0';
1846 in->charpoint = 0;
1849 static void
1850 ins_from_clip (WInput * in)
1852 char *p = NULL;
1854 if (load_text_from_clip_file (&p))
1856 char *pp;
1858 for (pp = p; *pp != '\0'; pp++)
1859 insert_char (in, *pp);
1861 g_free (p);
1865 void
1866 assign_text (WInput * in, const char *text)
1868 free_completions (in);
1869 g_free (in->buffer);
1870 in->buffer = g_strdup (text); /* was in->buffer->text */
1871 in->current_max_size = strlen (in->buffer) + 1;
1872 in->point = str_length (in->buffer);
1873 in->mark = 0;
1874 in->need_push = 1;
1875 in->charpoint = 0;
1878 static void
1879 hist_prev (WInput * in)
1881 GList *prev;
1883 if (!in->history)
1884 return;
1886 if (in->need_push)
1887 push_history (in, in->buffer);
1889 prev = g_list_previous (in->history);
1890 if (prev != NULL)
1892 in->history = prev;
1893 assign_text (in, (char *) prev->data);
1894 in->need_push = 0;
1898 static void
1899 hist_next (WInput * in)
1901 if (in->need_push)
1903 push_history (in, in->buffer);
1904 assign_text (in, "");
1905 return;
1908 if (!in->history)
1909 return;
1911 if (!in->history->next)
1913 assign_text (in, "");
1914 return;
1917 in->history = g_list_next (in->history);
1918 assign_text (in, (char *) in->history->data);
1919 in->need_push = 0;
1922 static void
1923 port_region_marked_for_delete (WInput * in)
1925 in->buffer[0] = '\0';
1926 in->point = 0;
1927 in->first = 0;
1928 in->charpoint = 0;
1931 static cb_ret_t
1932 input_execute_cmd (WInput * in, unsigned long command)
1934 cb_ret_t res = MSG_HANDLED;
1936 switch (command)
1938 case CK_InputBol:
1939 beginning_of_line (in);
1940 break;
1941 case CK_InputEol:
1942 end_of_line (in);
1943 break;
1944 case CK_InputMoveLeft:
1945 key_left (in);
1946 break;
1947 case CK_InputWordLeft:
1948 key_ctrl_left (in);
1949 break;
1950 case CK_InputMoveRight:
1951 key_right (in);
1952 break;
1953 case CK_InputWordRight:
1954 key_ctrl_right (in);
1955 break;
1956 case CK_InputBackwardChar:
1957 backward_char (in);
1958 break;
1959 case CK_InputBackwardWord:
1960 backward_word (in);
1961 break;
1962 case CK_InputForwardChar:
1963 forward_char (in);
1964 break;
1965 case CK_InputForwardWord:
1966 forward_word (in);
1967 break;
1968 case CK_InputBackwardDelete:
1969 backward_delete (in);
1970 break;
1971 case CK_InputDeleteChar:
1972 delete_char (in);
1973 break;
1974 case CK_InputKillWord:
1975 kill_word (in);
1976 break;
1977 case CK_InputBackwardKillWord:
1978 back_kill_word (in);
1979 break;
1980 case CK_InputSetMark:
1981 set_mark (in);
1982 break;
1983 case CK_InputKillRegion:
1984 kill_region (in);
1985 break;
1986 case CK_InputClearLine:
1987 clear_region (in);
1988 break;
1989 case CK_InputKillSave:
1990 kill_save (in);
1991 break;
1992 case CK_InputYank:
1993 yank (in);
1994 break;
1995 case CK_InputPaste:
1996 ins_from_clip (in);
1997 break;
1998 case CK_InputKillLine:
1999 kill_line (in);
2000 break;
2001 case CK_InputHistoryPrev:
2002 hist_prev (in);
2003 break;
2004 case CK_InputHistoryNext:
2005 hist_next (in);
2006 break;
2007 case CK_InputHistoryShow:
2008 do_show_hist (in);
2009 break;
2010 case CK_InputComplete:
2011 complete (in);
2012 break;
2013 default:
2014 res = MSG_NOT_HANDLED;
2017 return res;
2020 /* This function is a test for a special input key used in complete.c */
2021 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2022 and 2 if it is a complete key */
2024 is_in_input_map (WInput * in, int key)
2026 size_t i;
2027 for (i = 0; input_map[i].key != 0; i++)
2028 if (key == input_map[i].key)
2030 input_execute_cmd (in, input_map[i].command);
2031 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2033 return 0;
2036 cb_ret_t
2037 handle_char (WInput * in, int key)
2039 cb_ret_t v;
2040 int i;
2042 v = MSG_NOT_HANDLED;
2044 if (quote)
2046 free_completions (in);
2047 v = insert_char (in, key);
2048 update_input (in, 1);
2049 quote = 0;
2050 return v;
2052 for (i = 0; input_map[i].key; i++)
2054 if (key == input_map[i].key)
2056 if (input_map[i].command != CK_InputComplete)
2057 free_completions (in);
2058 input_execute_cmd (in, input_map[i].command);
2059 update_input (in, 1);
2060 v = MSG_HANDLED;
2061 break;
2064 if (input_map[i].command == 0)
2066 if (key > 255)
2067 return MSG_NOT_HANDLED;
2068 if (in->first)
2069 port_region_marked_for_delete (in);
2070 free_completions (in);
2071 v = insert_char (in, key);
2073 update_input (in, 1);
2074 return v;
2077 /* Inserts text in input line */
2078 void
2079 stuff (WInput * in, const char *text, int insert_extra_space)
2081 input_disable_update (in);
2082 while (*text != '\0')
2083 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2084 if (insert_extra_space)
2085 handle_char (in, ' ');
2086 input_enable_update (in);
2087 update_input (in, 1);
2090 void
2091 input_set_point (WInput * in, int pos)
2093 int max_pos = str_length (in->buffer);
2095 if (pos > max_pos)
2096 pos = max_pos;
2097 if (pos != in->point)
2098 free_completions (in);
2099 in->point = pos;
2100 in->charpoint = 0;
2101 update_input (in, 1);
2104 cb_ret_t
2105 input_callback (Widget * w, widget_msg_t msg, int parm)
2107 WInput *in = (WInput *) w;
2108 cb_ret_t v;
2110 switch (msg)
2112 case WIDGET_KEY:
2113 if (parm == XCTRL ('q'))
2115 quote = 1;
2116 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2117 quote = 0;
2118 return v;
2121 /* Keys we want others to handle */
2122 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2123 || parm == KEY_F (10) || parm == '\n')
2124 return MSG_NOT_HANDLED;
2126 /* When pasting multiline text, insert literal Enter */
2127 if ((parm & ~KEY_M_MASK) == '\n')
2129 quote = 1;
2130 v = handle_char (in, '\n');
2131 quote = 0;
2132 return v;
2135 return handle_char (in, parm);
2137 case WIDGET_COMMAND:
2138 return input_execute_cmd (in, parm);
2140 case WIDGET_FOCUS:
2141 case WIDGET_UNFOCUS:
2142 case WIDGET_DRAW:
2143 update_input (in, 0);
2144 return MSG_HANDLED;
2146 case WIDGET_CURSOR:
2147 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2148 - in->term_first_shown);
2149 return MSG_HANDLED;
2151 case WIDGET_DESTROY:
2152 input_destroy (in);
2153 return MSG_HANDLED;
2155 default:
2156 return default_proc (msg, parm);
2160 static int
2161 input_event (Gpm_Event * event, void *data)
2163 WInput *in = data;
2165 if (event->type & (GPM_DOWN | GPM_DRAG))
2167 dlg_select_widget (in);
2169 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2170 && should_show_history_button (in))
2172 do_show_hist (in);
2174 else
2176 in->point = str_length (in->buffer);
2177 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2178 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2180 update_input (in, 1);
2182 return MOU_NORMAL;
2185 WInput *
2186 input_new (int y, int x, int color, int width, const char *def_text,
2187 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2189 WInput *in = g_new (WInput, 1);
2190 size_t initial_buffer_len;
2192 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2194 /* history setup */
2195 in->history_name = NULL;
2196 in->history = NULL;
2197 if ((histname != NULL) && (*histname != '\0'))
2199 in->history_name = g_strdup (histname);
2200 in->history = history_get (histname);
2203 if (def_text == NULL)
2204 def_text = "";
2205 else if (def_text == INPUT_LAST_TEXT)
2207 if ((in->history != NULL) && (in->history->data != NULL))
2208 def_text = (char *) in->history->data;
2209 else
2210 def_text = "";
2213 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2214 in->widget.options |= W_IS_INPUT;
2215 in->completions = NULL;
2216 in->completion_flags = completion_flags;
2217 in->current_max_size = initial_buffer_len;
2218 in->buffer = g_new (char, initial_buffer_len);
2219 in->color = color;
2220 in->field_width = width;
2221 in->first = 1;
2222 in->term_first_shown = 0;
2223 in->disable_update = 0;
2224 in->mark = 0;
2225 in->need_push = 1;
2226 in->is_password = 0;
2228 strcpy (in->buffer, def_text);
2229 in->point = str_length (in->buffer);
2230 in->charpoint = 0;
2232 return in;
2236 /* Listbox widget */
2238 /* Should draw the scrollbar, but currently draws only
2239 * indications that there is more information
2242 static void
2243 listbox_entry_free (void *data)
2245 WLEntry *e = data;
2246 g_free (e->text);
2247 g_free (e);
2250 static void
2251 listbox_drawscroll (WListbox * l)
2253 const int max_line = l->widget.lines - 1;
2254 int line = 0;
2255 int i;
2257 /* Are we at the top? */
2258 widget_move (&l->widget, 0, l->widget.cols);
2259 if (l->top == 0)
2260 tty_print_one_vline (TRUE);
2261 else
2262 tty_print_char ('^');
2264 /* Are we at the bottom? */
2265 widget_move (&l->widget, max_line, l->widget.cols);
2266 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2267 tty_print_one_vline (TRUE);
2268 else
2269 tty_print_char ('v');
2271 /* Now draw the nice relative pointer */
2272 if (l->count != 0)
2273 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2275 for (i = 1; i < max_line; i++)
2277 widget_move (&l->widget, i, l->widget.cols);
2278 if (i != line)
2279 tty_print_one_vline (TRUE);
2280 else
2281 tty_print_char ('*');
2285 static void
2286 listbox_draw (WListbox * l, gboolean focused)
2288 const Dlg_head *h = l->widget.parent;
2289 const int normalc = DLG_NORMALC (h);
2290 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2292 GList *le;
2293 int pos;
2294 int i;
2295 int sel_line = -1;
2297 le = g_list_nth (l->list, l->top);
2298 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2299 pos = (le == NULL) ? 0 : l->top;
2301 for (i = 0; i < l->widget.lines; i++)
2303 const char *text;
2305 /* Display the entry */
2306 if (pos == l->pos && sel_line == -1)
2308 sel_line = i;
2309 tty_setcolor (selc);
2311 else
2312 tty_setcolor (normalc);
2314 widget_move (&l->widget, i, 1);
2316 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2317 text = "";
2318 else
2320 WLEntry *e = (WLEntry *) le->data;
2321 text = e->text;
2322 le = g_list_next (le);
2323 pos++;
2326 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2329 l->cursor_y = sel_line;
2331 if (l->scrollbar && (l->count > l->widget.lines))
2333 tty_setcolor (normalc);
2334 listbox_drawscroll (l);
2338 static int
2339 listbox_check_hotkey (WListbox * l, int key)
2341 int i;
2342 GList *le;
2344 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2346 WLEntry *e = (WLEntry *) le->data;
2348 if (e->hotkey == key)
2349 return i;
2352 return (-1);
2355 /* Selects the last entry and scrolls the list to the bottom */
2356 void
2357 listbox_select_last (WListbox * l)
2359 l->pos = l->count - 1;
2360 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2363 /* Selects the first entry and scrolls the list to the top */
2364 void
2365 listbox_select_first (WListbox * l)
2367 l->pos = l->top = 0;
2370 void
2371 listbox_set_list (WListbox * l, GList * list)
2373 listbox_remove_list (l);
2375 if (l != NULL)
2377 l->list = list;
2378 l->top = l->pos = 0;
2379 l->count = g_list_length (list);
2383 void
2384 listbox_remove_list (WListbox * l)
2386 if ((l != NULL) && (l->count != 0))
2388 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2389 g_list_free (l->list);
2390 l->list = NULL;
2391 l->count = l->pos = l->top = 0;
2395 void
2396 listbox_remove_current (WListbox * l)
2398 if ((l != NULL) && (l->count != 0))
2400 GList *current;
2402 current = g_list_nth (l->list, l->pos);
2403 l->list = g_list_remove_link (l->list, current);
2404 listbox_entry_free ((WLEntry *) current->data);
2405 g_list_free_1 (current);
2406 l->count--;
2408 if (l->count == 0)
2409 l->top = l->pos = 0;
2410 else if (l->pos >= l->count)
2411 l->pos = l->count - 1;
2415 void
2416 listbox_select_entry (WListbox * l, int dest)
2418 GList *le;
2419 int pos;
2420 gboolean top_seen = FALSE;
2422 if (dest < 0)
2423 return;
2425 /* Special case */
2426 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2428 if (pos == l->top)
2429 top_seen = TRUE;
2431 if (pos == dest)
2433 l->pos = dest;
2434 if (!top_seen)
2435 l->top = l->pos;
2436 else if (l->pos - l->top >= l->widget.lines)
2437 l->top = l->pos - l->widget.lines + 1;
2438 return;
2442 /* If we are unable to find it, set decent values */
2443 l->pos = l->top = 0;
2446 /* Selects from base the pos element */
2447 static int
2448 listbox_select_pos (WListbox * l, int base, int pos)
2450 int last = l->count - 1;
2452 base += pos;
2453 if (base >= last)
2454 base = last;
2456 return base;
2459 static void
2460 listbox_fwd (WListbox * l)
2462 if (l->pos + 1 >= l->count)
2463 listbox_select_first (l);
2464 else
2465 listbox_select_entry (l, l->pos + 1);
2468 static void
2469 listbox_back (WListbox * l)
2471 if (l->pos <= 0)
2472 listbox_select_last (l);
2473 else
2474 listbox_select_entry (l, l->pos - 1);
2477 /* Return MSG_HANDLED if we want a redraw */
2478 static cb_ret_t
2479 listbox_key (WListbox * l, int key)
2481 int i;
2483 cb_ret_t j = MSG_NOT_HANDLED;
2485 if (l->list == NULL)
2486 return MSG_NOT_HANDLED;
2488 /* focus on listbox item N by '0'..'9' keys */
2489 if (key >= '0' && key <= '9')
2491 int oldpos = l->pos;
2492 listbox_select_entry (l, key - '0');
2494 /* need scroll to item? */
2495 if (abs (oldpos - l->pos) > l->widget.lines)
2496 l->top = l->pos;
2498 return MSG_HANDLED;
2501 switch (key)
2503 case KEY_HOME:
2504 case KEY_A1:
2505 case ALT ('<'):
2506 listbox_select_first (l);
2507 return MSG_HANDLED;
2509 case KEY_END:
2510 case KEY_C1:
2511 case ALT ('>'):
2512 listbox_select_last (l);
2513 return MSG_HANDLED;
2515 case XCTRL ('p'):
2516 case KEY_UP:
2517 listbox_back (l);
2518 return MSG_HANDLED;
2520 case XCTRL ('n'):
2521 case KEY_DOWN:
2522 listbox_fwd (l);
2523 return MSG_HANDLED;
2525 case KEY_NPAGE:
2526 case XCTRL ('v'):
2527 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2529 listbox_fwd (l);
2530 j = MSG_HANDLED;
2532 break;
2534 case KEY_PPAGE:
2535 case ALT ('v'):
2536 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2538 listbox_back (l);
2539 j = MSG_HANDLED;
2541 break;
2543 case KEY_DC:
2544 case 'd':
2545 if (l->deletable)
2547 gboolean is_last = (l->pos + 1 >= l->count);
2548 gboolean is_more = (l->top + l->widget.lines >= l->count);
2550 listbox_remove_current (l);
2551 if ((l->top > 0) && (is_last || is_more))
2552 l->top--;
2554 return MSG_HANDLED;
2556 case (KEY_M_SHIFT | KEY_DC):
2557 case 'D':
2558 if (l->deletable && confirm_history_cleanup
2559 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2560 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2561 _("Do you want clean this history?"),
2562 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2564 listbox_remove_list (l);
2565 j = MSG_HANDLED;
2567 break;
2569 default:
2570 break;
2573 return j;
2576 static inline void
2577 listbox_destroy (WListbox * l)
2579 /* don't delete list in modifable listbox */
2580 if (!l->deletable)
2581 listbox_remove_list (l);
2584 static cb_ret_t
2585 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2587 WListbox *l = (WListbox *) w;
2588 Dlg_head *h = l->widget.parent;
2589 cb_ret_t ret_code;
2591 switch (msg)
2593 case WIDGET_INIT:
2594 return MSG_HANDLED;
2596 case WIDGET_HOTKEY:
2598 int pos, action;
2600 pos = listbox_check_hotkey (l, parm);
2601 if (pos < 0)
2602 return MSG_NOT_HANDLED;
2604 listbox_select_entry (l, pos);
2605 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2607 if (l->cback != NULL)
2608 action = l->cback (l);
2609 else
2610 action = LISTBOX_DONE;
2612 if (action == LISTBOX_DONE)
2614 h->ret_value = B_ENTER;
2615 dlg_stop (h);
2618 return MSG_HANDLED;
2621 case WIDGET_KEY:
2622 ret_code = listbox_key (l, parm);
2623 if (ret_code != MSG_NOT_HANDLED)
2625 listbox_draw (l, TRUE);
2626 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2628 return ret_code;
2630 case WIDGET_CURSOR:
2631 widget_move (&l->widget, l->cursor_y, 0);
2632 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2633 return MSG_HANDLED;
2635 case WIDGET_FOCUS:
2636 case WIDGET_UNFOCUS:
2637 case WIDGET_DRAW:
2638 listbox_draw (l, msg != WIDGET_UNFOCUS);
2639 return MSG_HANDLED;
2641 case WIDGET_DESTROY:
2642 listbox_destroy (l);
2643 return MSG_HANDLED;
2645 case WIDGET_RESIZED:
2646 return MSG_HANDLED;
2648 default:
2649 return default_proc (msg, parm);
2653 static int
2654 listbox_event (Gpm_Event * event, void *data)
2656 WListbox *l = data;
2657 int i;
2659 Dlg_head *h = l->widget.parent;
2661 /* Single click */
2662 if (event->type & GPM_DOWN)
2663 dlg_select_widget (l);
2665 if (l->list == NULL)
2666 return MOU_NORMAL;
2668 if (event->type & (GPM_DOWN | GPM_DRAG))
2670 int ret = MOU_REPEAT;
2672 if (event->x < 0 || event->x > l->widget.cols)
2673 return ret;
2675 if (event->y < 1)
2676 for (i = -event->y; i >= 0; i--)
2677 listbox_back (l);
2678 else if (event->y > l->widget.lines)
2679 for (i = event->y - l->widget.lines; i > 0; i--)
2680 listbox_fwd (l);
2681 else if (event->buttons & GPM_B_UP)
2683 listbox_back (l);
2684 ret = MOU_NORMAL;
2686 else if (event->buttons & GPM_B_DOWN)
2688 listbox_fwd (l);
2689 ret = MOU_NORMAL;
2691 else
2692 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2694 /* We need to refresh ourselves since the dialog manager doesn't */
2695 /* know about this event */
2696 listbox_draw (l, TRUE);
2697 return ret;
2700 /* Double click */
2701 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2703 int action;
2705 if (event->x < 0 || event->x >= l->widget.cols
2706 || event->y < 1 || event->y > l->widget.lines)
2707 return MOU_NORMAL;
2709 dlg_select_widget (l);
2710 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2712 if (l->cback != NULL)
2713 action = l->cback (l);
2714 else
2715 action = LISTBOX_DONE;
2717 if (action == LISTBOX_DONE)
2719 h->ret_value = B_ENTER;
2720 dlg_stop (h);
2721 return MOU_NORMAL;
2724 return MOU_NORMAL;
2727 WListbox *
2728 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2730 WListbox *l = g_new (WListbox, 1);
2732 if (height <= 0)
2733 height = 1;
2735 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2737 l->list = NULL;
2738 l->top = l->pos = 0;
2739 l->count = 0;
2740 l->deletable = deletable;
2741 l->cback = callback;
2742 l->allow_duplicates = TRUE;
2743 l->scrollbar = !tty_is_slow ();
2744 widget_want_hotkey (l->widget, 1);
2746 return l;
2749 static int
2750 listbox_entry_cmp (const void *a, const void *b)
2752 const WLEntry *ea = (const WLEntry *) a;
2753 const WLEntry *eb = (const WLEntry *) b;
2755 return strcmp (ea->text, eb->text);
2758 /* Listbox item adding function */
2759 static inline void
2760 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2762 switch (pos)
2764 case LISTBOX_APPEND_AT_END:
2765 l->list = g_list_append (l->list, e);
2766 break;
2768 case LISTBOX_APPEND_BEFORE:
2769 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2770 if (l->pos > 0)
2771 l->pos--;
2772 break;
2774 case LISTBOX_APPEND_AFTER:
2775 l->list = g_list_insert (l->list, e, l->pos + 1);
2776 break;
2778 case LISTBOX_APPEND_SORTED:
2779 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2780 break;
2782 default:
2783 return;
2786 l->count++;
2789 char *
2790 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2792 WLEntry *entry;
2794 if (l == NULL)
2795 return NULL;
2797 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2798 return NULL;
2800 entry = g_new (WLEntry, 1);
2801 entry->text = g_strdup (text);
2802 entry->data = data;
2803 entry->hotkey = hotkey;
2805 listbox_append_item (l, entry, pos);
2807 return entry->text;
2811 listbox_search_text (WListbox * l, const char *text)
2813 if (l != NULL)
2815 int i;
2816 GList *le;
2818 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2820 WLEntry *e = (WLEntry *) le->data;
2822 if (strcmp (e->text, text) == 0)
2823 return i;
2827 return (-1);
2830 /* Returns the current string text as well as the associated extra data */
2831 void
2832 listbox_get_current (WListbox * l, char **string, void **extra)
2834 WLEntry *e = NULL;
2835 gboolean ok;
2837 if (l != NULL)
2838 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2840 ok = (e != NULL);
2842 if (string != NULL)
2843 *string = ok ? e->text : NULL;
2845 if (extra != NULL)
2846 *extra = ok ? e->data : NULL;
2850 /* ButtonBar widget */
2852 /* returns TRUE if a function has been called, FALSE otherwise. */
2853 static gboolean
2854 buttonbar_call (WButtonBar * bb, int i)
2856 cb_ret_t ret = MSG_NOT_HANDLED;
2858 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
2859 ret = bb->widget.parent->callback (bb->widget.parent,
2860 (Widget *) bb, DLG_ACTION,
2861 bb->labels[i].command, bb->labels[i].receiver);
2862 return ret;
2865 /* calculate width of one button, width is never lesser than 7 */
2866 static int
2867 buttonbat_get_button_width (void)
2869 int result = COLS / BUTTONBAR_LABELS_NUM;
2870 return (result >= 7) ? result : 7;
2873 static cb_ret_t
2874 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
2876 WButtonBar *bb = (WButtonBar *) w;
2877 int i;
2878 const char *text;
2880 switch (msg)
2882 case WIDGET_FOCUS:
2883 return MSG_NOT_HANDLED;
2885 case WIDGET_HOTKEY:
2886 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2887 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2888 return MSG_HANDLED;
2889 return MSG_NOT_HANDLED;
2891 case WIDGET_DRAW:
2892 if (bb->visible)
2894 int offset = 0;
2895 int count_free_positions;
2897 widget_move (&bb->widget, 0, 0);
2898 tty_setcolor (DEFAULT_COLOR);
2899 bb->btn_width = buttonbat_get_button_width ();
2900 tty_printf ("%-*s", bb->widget.cols, "");
2901 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2903 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++)
2905 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2906 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2907 tty_printf ("%2d", i + 1);
2908 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2909 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2910 tty_print_string (str_fit_to_term (text,
2911 bb->btn_width - 2 + (int) (offset <
2912 count_free_positions),
2913 J_LEFT_FIT));
2915 if (count_free_positions != 0 && offset < count_free_positions)
2916 offset++;
2919 return MSG_HANDLED;
2921 case WIDGET_DESTROY:
2922 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2923 g_free (bb->labels[i].text);
2924 return MSG_HANDLED;
2926 default:
2927 return default_proc (msg, parm);
2931 static int
2932 buttonbar_event (Gpm_Event * event, void *data)
2934 WButtonBar *bb = data;
2935 int button;
2937 if (!(event->type & GPM_UP))
2938 return MOU_NORMAL;
2939 if (event->y == 2)
2940 return MOU_NORMAL;
2941 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2942 if (button < BUTTONBAR_LABELS_NUM)
2943 buttonbar_call (bb, button);
2944 return MOU_NORMAL;
2947 WButtonBar *
2948 buttonbar_new (gboolean visible)
2950 WButtonBar *bb;
2952 bb = g_new0 (WButtonBar, 1);
2954 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
2955 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2956 bb->visible = visible;
2957 widget_want_hotkey (bb->widget, 1);
2958 widget_want_cursor (bb->widget, 0);
2959 bb->btn_width = buttonbat_get_button_width ();
2961 return bb;
2964 static void
2965 set_label_text (WButtonBar * bb, int lc_index, const char *text)
2967 g_free (bb->labels[lc_index - 1].text);
2968 bb->labels[lc_index - 1].text = g_strdup (text);
2971 /* Find ButtonBar widget in the dialog */
2972 WButtonBar *
2973 find_buttonbar (const Dlg_head * h)
2975 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2978 void
2979 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
2980 const struct global_keymap_t *keymap, const Widget * receiver)
2982 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
2984 unsigned long command = CK_Ignore_Key;
2986 if (keymap != NULL)
2987 command = lookup_keymap_command (keymap, KEY_F (idx));
2989 if ((text == NULL) || (text[0] == '\0'))
2990 set_label_text (bb, idx, "");
2991 else
2992 set_label_text (bb, idx, text);
2994 bb->labels[idx - 1].command = command;
2995 bb->labels[idx - 1].receiver = (Widget *) receiver;
2999 void
3000 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
3002 bb->visible = visible;
3005 void
3006 buttonbar_redraw (WButtonBar * bb)
3008 if (bb != NULL)
3009 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3012 static cb_ret_t
3013 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3015 WGroupbox *g = (WGroupbox *) w;
3017 switch (msg)
3019 case WIDGET_INIT:
3020 return MSG_HANDLED;
3022 case WIDGET_FOCUS:
3023 return MSG_NOT_HANDLED;
3025 case WIDGET_DRAW:
3026 tty_setcolor (COLOR_NORMAL);
3027 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
3028 g->widget.x - g->widget.parent->x, g->widget.lines, g->widget.cols, TRUE);
3030 tty_setcolor (COLOR_HOT_NORMAL);
3031 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
3032 g->widget.x - g->widget.parent->x + 1);
3033 tty_print_string (g->title);
3034 return MSG_HANDLED;
3036 case WIDGET_DESTROY:
3037 g_free (g->title);
3038 return MSG_HANDLED;
3040 default:
3041 return default_proc (msg, parm);
3045 WGroupbox *
3046 groupbox_new (int y, int x, int height, int width, const char *title)
3048 WGroupbox *g = g_new (WGroupbox, 1);
3050 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3052 g->widget.options &= ~W_WANT_CURSOR;
3053 widget_want_hotkey (g->widget, 0);
3055 /* Strip existing spaces, add one space before and after the title */
3056 if (title)
3058 char *t;
3059 t = g_strstrip (g_strdup (title));
3060 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3061 g_free (t);
3064 return g;