Removed hardcoded hotkeys in dialog.c Replaced keymap's initialization from main...
[midnight-commander.git] / src / widget.c
blob10656e732edb3901216669e23ee3821e1d14e37b
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 static void
65 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
67 Dlg_head *h = w->parent;
69 tty_setcolor (hotkey
70 ? (focused
71 ? DLG_HOT_FOCUSC (h)
72 : DLG_HOT_NORMALC (h)) : (focused ? DLG_FOCUSC (h) : DLG_NORMALC (h)));
75 struct hotkey_t
76 parse_hotkey (const char *text)
78 struct hotkey_t result;
79 const char *cp, *p;
81 /* search for '&', that is not on the of text */
82 cp = strchr (text, '&');
83 if (cp != NULL && cp[1] != '\0')
85 result.start = g_strndup (text, cp - text);
87 /* skip '&' */
88 cp++;
89 p = str_cget_next_char (cp);
90 result.hotkey = g_strndup (cp, p - cp);
92 cp = p;
93 result.end = g_strdup (cp);
95 else
97 result.start = g_strdup (text);
98 result.hotkey = NULL;
99 result.end = NULL;
102 return result;
105 void
106 release_hotkey (const struct hotkey_t hotkey)
108 g_free (hotkey.start);
109 g_free (hotkey.hotkey);
110 g_free (hotkey.end);
114 hotkey_width (const struct hotkey_t hotkey)
116 int result;
118 result = str_term_width1 (hotkey.start);
119 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
120 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
121 return result;
124 static void
125 draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
127 widget_selectcolor (w, focused, FALSE);
128 tty_print_string (hotkey.start);
130 if (hotkey.hotkey != NULL)
132 widget_selectcolor (w, focused, TRUE);
133 tty_print_string (hotkey.hotkey);
134 widget_selectcolor (w, focused, FALSE);
137 if (hotkey.end != NULL)
138 tty_print_string (hotkey.end);
142 /* Default callback for widgets */
143 cb_ret_t
144 default_proc (widget_msg_t msg, int parm)
146 (void) parm;
148 switch (msg)
150 case WIDGET_INIT:
151 case WIDGET_FOCUS:
152 case WIDGET_UNFOCUS:
153 case WIDGET_DRAW:
154 case WIDGET_DESTROY:
155 case WIDGET_CURSOR:
156 case WIDGET_IDLE:
157 return MSG_HANDLED;
159 default:
160 return MSG_NOT_HANDLED;
164 static int button_event (Gpm_Event * event, void *);
166 int quote = 0;
168 static cb_ret_t
169 button_callback (Widget * w, widget_msg_t msg, int parm)
171 WButton *b = (WButton *) w;
172 int stop = 0;
173 int off = 0;
174 Dlg_head *h = b->widget.parent;
176 switch (msg)
178 case WIDGET_HOTKEY:
180 * Don't let the default button steal Enter from the current
181 * button. This is a workaround for the flawed event model
182 * when hotkeys are sent to all widgets before the key is
183 * handled by the current widget.
185 if (parm == '\n' && h->current == &b->widget)
187 button_callback (w, WIDGET_KEY, ' ');
188 return MSG_HANDLED;
191 if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
193 button_callback (w, WIDGET_KEY, ' ');
194 return MSG_HANDLED;
197 if (b->text.hotkey != NULL)
199 if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
201 button_callback (w, WIDGET_KEY, ' ');
202 return MSG_HANDLED;
205 return MSG_NOT_HANDLED;
207 case WIDGET_KEY:
208 if (parm != ' ' && parm != '\n')
209 return MSG_NOT_HANDLED;
211 if (b->callback)
212 stop = (*b->callback) (b->action);
213 if (!b->callback || stop)
215 h->ret_value = b->action;
216 dlg_stop (h);
218 return MSG_HANDLED;
220 case WIDGET_CURSOR:
221 switch (b->flags)
223 case DEFPUSH_BUTTON:
224 off = 3;
225 break;
226 case NORMAL_BUTTON:
227 off = 2;
228 break;
229 case NARROW_BUTTON:
230 off = 1;
231 break;
232 case HIDDEN_BUTTON:
233 default:
234 off = 0;
235 break;
237 widget_move (&b->widget, 0, b->hotpos + off);
238 return MSG_HANDLED;
240 case WIDGET_UNFOCUS:
241 case WIDGET_FOCUS:
242 case WIDGET_DRAW:
243 if (msg == WIDGET_UNFOCUS)
244 b->selected = 0;
245 else if (msg == WIDGET_FOCUS)
246 b->selected = 1;
248 widget_selectcolor (w, b->selected, FALSE);
249 widget_move (w, 0, 0);
251 switch (b->flags)
253 case DEFPUSH_BUTTON:
254 tty_print_string ("[< ");
255 break;
256 case NORMAL_BUTTON:
257 tty_print_string ("[ ");
258 break;
259 case NARROW_BUTTON:
260 tty_print_string ("[");
261 break;
262 case HIDDEN_BUTTON:
263 default:
264 return MSG_HANDLED;
267 draw_hotkey (w, b->text, b->selected);
269 switch (b->flags)
271 case DEFPUSH_BUTTON:
272 tty_print_string (" >]");
273 break;
274 case NORMAL_BUTTON:
275 tty_print_string (" ]");
276 break;
277 case NARROW_BUTTON:
278 tty_print_string ("]");
279 break;
281 return MSG_HANDLED;
283 case WIDGET_DESTROY:
284 release_hotkey (b->text);
285 return MSG_HANDLED;
287 default:
288 return default_proc (msg, parm);
292 static int
293 button_event (Gpm_Event * event, void *data)
295 WButton *b = data;
297 if (event->type & (GPM_DOWN | GPM_UP))
299 Dlg_head *h = b->widget.parent;
300 dlg_select_widget (b);
301 if (event->type & GPM_UP)
303 button_callback ((Widget *) data, WIDGET_KEY, ' ');
304 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
305 return MOU_NORMAL;
308 return MOU_NORMAL;
312 button_get_len (const WButton * b)
314 int ret = hotkey_width (b->text);
315 switch (b->flags)
317 case DEFPUSH_BUTTON:
318 ret += 6;
319 break;
320 case NORMAL_BUTTON:
321 ret += 4;
322 break;
323 case NARROW_BUTTON:
324 ret += 2;
325 break;
326 case HIDDEN_BUTTON:
327 default:
328 return 0;
330 return ret;
333 WButton *
334 button_new (int y, int x, int action, int flags, const char *text, bcback callback)
336 WButton *b = g_new (WButton, 1);
338 b->action = action;
339 b->flags = flags;
340 b->text = parse_hotkey (text);
342 init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
344 b->selected = 0;
345 b->callback = callback;
346 widget_want_hotkey (b->widget, 1);
347 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
349 return b;
352 const char *
353 button_get_text (const WButton * b)
355 if (b->text.hotkey != NULL)
356 return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
357 else
358 return g_strdup (b->text.start);
361 void
362 button_set_text (WButton * b, const char *text)
364 release_hotkey (b->text);
365 b->text = parse_hotkey (text);
366 b->widget.cols = button_get_len (b);
367 dlg_redraw (b->widget.parent);
371 /* Radio button widget */
372 static int radio_event (Gpm_Event * event, void *);
374 static cb_ret_t
375 radio_callback (Widget * w, widget_msg_t msg, int parm)
377 WRadio *r = (WRadio *) w;
378 int i;
379 Dlg_head *h = r->widget.parent;
381 switch (msg)
383 case WIDGET_HOTKEY:
385 int lp = g_ascii_tolower ((gchar) parm);
387 for (i = 0; i < r->count; i++)
389 if (r->texts[i].hotkey != NULL)
391 int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
393 if (c != lp)
394 continue;
395 r->pos = i;
397 /* Take action */
398 radio_callback (w, WIDGET_KEY, ' ');
399 return MSG_HANDLED;
403 return MSG_NOT_HANDLED;
405 case WIDGET_KEY:
406 switch (parm)
408 case ' ':
409 r->sel = r->pos;
410 h->callback (h, w, DLG_ACTION, 0, NULL);
411 radio_callback (w, WIDGET_FOCUS, ' ');
412 return MSG_HANDLED;
414 case KEY_UP:
415 case KEY_LEFT:
416 if (r->pos > 0)
418 r->pos--;
419 return MSG_HANDLED;
421 return MSG_NOT_HANDLED;
423 case KEY_DOWN:
424 case KEY_RIGHT:
425 if (r->count - 1 > r->pos)
427 r->pos++;
428 return MSG_HANDLED;
431 return MSG_NOT_HANDLED;
433 case WIDGET_CURSOR:
434 h->callback (h, w, DLG_ACTION, 0, NULL);
435 radio_callback (w, WIDGET_FOCUS, ' ');
436 widget_move (&r->widget, r->pos, 1);
437 return MSG_HANDLED;
439 case WIDGET_UNFOCUS:
440 case WIDGET_FOCUS:
441 case WIDGET_DRAW:
442 for (i = 0; i < r->count; i++)
444 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
445 widget_selectcolor (w, focused, FALSE);
446 widget_move (&r->widget, i, 0);
447 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
448 draw_hotkey (w, r->texts[i], focused);
450 return MSG_HANDLED;
452 case WIDGET_DESTROY:
453 for (i = 0; i < r->count; i++)
455 release_hotkey (r->texts[i]);
457 g_free (r->texts);
458 return MSG_HANDLED;
460 default:
461 return default_proc (msg, parm);
465 static int
466 radio_event (Gpm_Event * event, void *data)
468 WRadio *r = data;
469 Widget *w = data;
471 if (event->type & (GPM_DOWN | GPM_UP))
473 Dlg_head *h = r->widget.parent;
475 r->pos = event->y - 1;
476 dlg_select_widget (r);
477 if (event->type & GPM_UP)
479 radio_callback (w, WIDGET_KEY, ' ');
480 radio_callback (w, WIDGET_FOCUS, 0);
481 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
482 return MOU_NORMAL;
485 return MOU_NORMAL;
488 WRadio *
489 radio_new (int y, int x, int count, const char **texts)
491 WRadio *result = g_new (WRadio, 1);
492 int i, max, m;
494 /* Compute the longest string */
495 result->texts = g_new (struct hotkey_t, count);
497 max = 0;
498 for (i = 0; i < count; i++)
500 result->texts[i] = parse_hotkey (texts[i]);
501 m = hotkey_width (result->texts[i]);
502 if (m > max)
503 max = m;
506 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
507 result->state = 1;
508 result->pos = 0;
509 result->sel = 0;
510 result->count = count;
511 widget_want_hotkey (result->widget, 1);
513 return result;
517 /* Checkbutton widget */
519 static int check_event (Gpm_Event * event, void *);
521 static cb_ret_t
522 check_callback (Widget * w, widget_msg_t msg, int parm)
524 WCheck *c = (WCheck *) w;
525 Dlg_head *h = c->widget.parent;
527 switch (msg)
529 case WIDGET_HOTKEY:
530 if (c->text.hotkey != NULL)
532 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
535 check_callback (w, WIDGET_KEY, ' '); /* make action */
536 return MSG_HANDLED;
539 return MSG_NOT_HANDLED;
541 case WIDGET_KEY:
542 if (parm != ' ')
543 return MSG_NOT_HANDLED;
544 c->state ^= C_BOOL;
545 c->state ^= C_CHANGE;
546 h->callback (h, w, DLG_ACTION, 0, NULL);
547 check_callback (w, WIDGET_FOCUS, ' ');
548 return MSG_HANDLED;
550 case WIDGET_CURSOR:
551 widget_move (&c->widget, 0, 1);
552 return MSG_HANDLED;
554 case WIDGET_FOCUS:
555 case WIDGET_UNFOCUS:
556 case WIDGET_DRAW:
557 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
558 widget_move (&c->widget, 0, 0);
559 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
560 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
561 return MSG_HANDLED;
563 case WIDGET_DESTROY:
564 release_hotkey (c->text);
565 return MSG_HANDLED;
567 default:
568 return default_proc (msg, parm);
572 static int
573 check_event (Gpm_Event * event, void *data)
575 WCheck *c = data;
576 Widget *w = data;
578 if (event->type & (GPM_DOWN | GPM_UP))
580 Dlg_head *h = c->widget.parent;
582 dlg_select_widget (c);
583 if (event->type & GPM_UP)
585 check_callback (w, WIDGET_KEY, ' ');
586 check_callback (w, WIDGET_FOCUS, 0);
587 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
588 return MOU_NORMAL;
591 return MOU_NORMAL;
594 WCheck *
595 check_new (int y, int x, int state, const char *text)
597 WCheck *c = g_new (WCheck, 1);
599 c->text = parse_hotkey (text);
601 init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
602 c->state = state ? C_BOOL : 0;
603 widget_want_hotkey (c->widget, 1);
605 return c;
608 static gboolean
609 save_text_to_clip_file (const char *text)
611 int file;
612 char *fname = NULL;
613 ssize_t ret;
614 size_t str_len;
616 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
617 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
618 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
619 g_free (fname);
621 if (file == -1)
622 return FALSE;
624 str_len = strlen (text);
625 ret = mc_write (file, (char *) text, str_len);
626 mc_close (file);
627 return ret == (ssize_t) str_len;
630 static gboolean
631 load_text_from_clip_file (char **text)
633 char buf[BUF_LARGE];
634 FILE *f;
635 char *fname = NULL;
636 gboolean first = TRUE;
638 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
639 f = fopen (fname, "r");
640 g_free (fname);
642 if (f == NULL)
643 return FALSE;
645 *text = NULL;
647 while (fgets (buf, sizeof (buf), f))
649 size_t len;
651 len = strlen (buf);
652 if (len > 0)
654 if (buf[len - 1] == '\n')
655 buf[len - 1] = '\0';
657 if (first)
659 first = FALSE;
660 *text = g_strdup (buf);
662 else
664 /* remove \n on EOL */
665 char *tmp;
667 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
668 g_free (*text);
669 *text = tmp;
674 fclose (f);
676 return (*text != NULL);
679 static gboolean
680 panel_save_curent_file_to_clip_file (void)
682 gboolean res;
684 if (current_panel->marked == 0)
685 res = save_text_to_clip_file (selection (current_panel)->fname);
686 else
688 int i;
689 gboolean first = TRUE;
690 char *flist = NULL;
692 for (i = 0; i < current_panel->count; i++)
693 if (current_panel->dir.list[i].f.marked != 0)
694 { /* Skip the unmarked ones */
695 if (first)
697 flist = g_strdup (current_panel->dir.list[i].fname);
698 first = FALSE;
700 else
702 /* Add empty lines after the file */
703 char *tmp;
705 tmp =
706 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
707 g_free (flist);
708 flist = tmp;
712 if (flist != NULL)
714 res = save_text_to_clip_file (flist);
715 g_free (flist);
718 return res;
721 /* Label widget */
723 static cb_ret_t
724 label_callback (Widget * w, widget_msg_t msg, int parm)
726 WLabel *l = (WLabel *) w;
727 Dlg_head *h = l->widget.parent;
729 switch (msg)
731 case WIDGET_INIT:
732 return MSG_HANDLED;
734 /* We don't want to get the focus */
735 case WIDGET_FOCUS:
736 return MSG_NOT_HANDLED;
738 case WIDGET_DRAW:
740 char *p = l->text, *q, c = 0;
741 int y = 0;
743 if (!l->text)
744 return MSG_HANDLED;
746 if (l->transparent)
747 tty_setcolor (DEFAULT_COLOR);
748 else
749 tty_setcolor (DLG_NORMALC (h));
751 for (;;)
753 q = strchr (p, '\n');
754 if (q != NULL)
756 c = q[0];
757 q[0] = '\0';
760 widget_move (&l->widget, y, 0);
761 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
763 if (q == NULL)
764 break;
765 q[0] = c;
766 p = q + 1;
767 y++;
769 return MSG_HANDLED;
772 case WIDGET_DESTROY:
773 g_free (l->text);
774 return MSG_HANDLED;
776 default:
777 return default_proc (msg, parm);
781 void
782 label_set_text (WLabel * label, const char *text)
784 int newcols = label->widget.cols;
785 int newlines;
787 if (label->text && text && !strcmp (label->text, text))
788 return; /* Flickering is not nice */
790 g_free (label->text);
792 if (text != NULL)
794 label->text = g_strdup (text);
795 if (label->auto_adjust_cols)
797 str_msg_term_size (text, &newlines, &newcols);
798 if (newcols > label->widget.cols)
799 label->widget.cols = newcols;
800 if (newlines > label->widget.lines)
801 label->widget.lines = newlines;
804 else
805 label->text = NULL;
807 if (label->widget.parent)
808 label_callback ((Widget *) label, WIDGET_DRAW, 0);
810 if (newcols < label->widget.cols)
811 label->widget.cols = newcols;
814 WLabel *
815 label_new (int y, int x, const char *text)
817 WLabel *l;
818 int cols = 1;
819 int lines = 1;
821 if (text != NULL)
822 str_msg_term_size (text, &lines, &cols);
824 l = g_new (WLabel, 1);
825 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
826 l->text = (text != NULL) ? g_strdup (text) : NULL;
827 l->auto_adjust_cols = 1;
828 l->transparent = 0;
829 widget_want_cursor (l->widget, 0);
830 return l;
833 static cb_ret_t
834 hline_callback (Widget * w, widget_msg_t msg, int parm)
836 WHLine *l = (WHLine *) w;
837 Dlg_head *h = l->widget.parent;
839 switch (msg)
841 case WIDGET_INIT:
842 case WIDGET_RESIZED:
843 if (l->auto_adjust_cols)
845 if (((w->parent->flags & DLG_COMPACT) != 0))
847 w->x = w->parent->x;
848 w->cols = w->parent->cols;
850 else
852 w->x = w->parent->x + 1;
853 w->cols = w->parent->cols - 2;
857 case WIDGET_FOCUS:
858 /* We don't want to get the focus */
859 return MSG_NOT_HANDLED;
861 case WIDGET_DRAW:
862 if (l->transparent)
863 tty_setcolor (DEFAULT_COLOR);
864 else
865 tty_setcolor (DLG_NORMALC (h));
867 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
869 if (l->auto_adjust_cols)
871 widget_move (w, 0, 0);
872 tty_print_alt_char (ACS_LTEE, FALSE);
873 widget_move (w, 0, w->cols - 1);
874 tty_print_alt_char (ACS_RTEE, FALSE);
876 return MSG_HANDLED;
878 default:
879 return default_proc (msg, parm);
884 WHLine *
885 hline_new (int y, int x, int width)
887 WHLine *l;
888 int cols = width;
889 int lines = 1;
891 l = g_new (WHLine, 1);
892 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
893 l->auto_adjust_cols = (width < 0);
894 l->transparent = FALSE;
895 widget_want_cursor (l->widget, 0);
896 return l;
899 /* Gauge widget (progress indicator) */
900 /* Currently width is hardcoded here for text mode */
901 #define gauge_len 47
903 static cb_ret_t
904 gauge_callback (Widget * w, widget_msg_t msg, int parm)
906 WGauge *g = (WGauge *) w;
907 Dlg_head *h = g->widget.parent;
909 if (msg == WIDGET_INIT)
910 return MSG_HANDLED;
912 /* We don't want to get the focus */
913 if (msg == WIDGET_FOCUS)
914 return MSG_NOT_HANDLED;
916 if (msg == WIDGET_DRAW)
918 widget_move (&g->widget, 0, 0);
919 tty_setcolor (DLG_NORMALC (h));
920 if (!g->shown)
921 tty_printf ("%*s", gauge_len, "");
922 else
924 int percentage, columns;
925 long total = g->max, done = g->current;
927 if (total <= 0 || done < 0)
929 done = 0;
930 total = 100;
932 if (done > total)
933 done = total;
934 while (total > 65535)
936 total /= 256;
937 done /= 256;
939 percentage = (200 * done / total + 1) / 2;
940 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
941 tty_print_char ('[');
942 if (g->from_left_to_right)
944 tty_setcolor (GAUGE_COLOR);
945 tty_printf ("%*s", (int) columns, "");
946 tty_setcolor (DLG_NORMALC (h));
947 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
949 else
951 tty_setcolor (DLG_NORMALC (h));
952 tty_printf ("%*s", gauge_len - columns - 7, "");
953 tty_setcolor (GAUGE_COLOR);
954 tty_printf ("%*s", columns, "");
955 tty_setcolor (DLG_NORMALC (h));
956 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
959 return MSG_HANDLED;
962 return default_proc (msg, parm);
965 void
966 gauge_set_value (WGauge * g, int max, int current)
968 if (g->current == current && g->max == max)
969 return; /* Do not flicker */
970 if (max == 0)
971 max = 1; /* I do not like division by zero :) */
973 g->current = current;
974 g->max = max;
975 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
978 void
979 gauge_show (WGauge * g, int shown)
981 if (g->shown == shown)
982 return;
983 g->shown = shown;
984 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
987 WGauge *
988 gauge_new (int y, int x, int shown, int max, int current)
990 WGauge *g = g_new (WGauge, 1);
992 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
993 g->shown = shown;
994 if (max == 0)
995 max = 1; /* I do not like division by zero :) */
996 g->max = max;
997 g->current = current;
998 g->from_left_to_right = TRUE;
999 widget_want_cursor (g->widget, 0);
1000 return g;
1004 /* Input widget */
1006 /* {{{ history button */
1008 #define LARGE_HISTORY_BUTTON 1
1010 #ifdef LARGE_HISTORY_BUTTON
1011 # define HISTORY_BUTTON_WIDTH 3
1012 #else
1013 # define HISTORY_BUTTON_WIDTH 1
1014 #endif
1016 #define should_show_history_button(in) \
1017 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1019 static void
1020 draw_history_button (WInput * in)
1022 char c;
1023 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1024 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1025 #ifdef LARGE_HISTORY_BUTTON
1027 Dlg_head *h;
1028 h = in->widget.parent;
1029 tty_setcolor (NORMAL_COLOR);
1030 tty_print_string ("[ ]");
1031 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1032 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1033 tty_print_char (c);
1035 #else
1036 tty_setcolor (MARKED_COLOR);
1037 tty_print_char (c);
1038 #endif
1041 /* }}} history button */
1044 /* Input widgets now have a global kill ring */
1045 /* Pointer to killed data */
1046 static char *kill_buffer = NULL;
1048 void
1049 update_input (WInput * in, int clear_first)
1051 int has_history = 0;
1052 int i;
1053 int buf_len = str_length (in->buffer);
1054 const char *cp;
1055 int pw;
1057 if (should_show_history_button (in))
1058 has_history = HISTORY_BUTTON_WIDTH;
1060 if (in->disable_update)
1061 return;
1063 pw = str_term_width2 (in->buffer, in->point);
1065 /* Make the point visible */
1066 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1069 in->term_first_shown = pw - (in->field_width / 3);
1070 if (in->term_first_shown < 0)
1071 in->term_first_shown = 0;
1074 /* Adjust the mark */
1075 if (in->mark > buf_len)
1076 in->mark = buf_len;
1078 if (has_history)
1079 draw_history_button (in);
1081 tty_setcolor (in->color);
1083 widget_move (&in->widget, 0, 0);
1085 if (!in->is_password)
1087 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1088 in->field_width - has_history));
1090 else
1092 cp = in->buffer;
1093 for (i = -in->term_first_shown; i < in->field_width - has_history; i++)
1095 if (i >= 0)
1097 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1099 if (cp[0] != '\0')
1100 str_cnext_char (&cp);
1104 if (clear_first)
1105 in->first = 0;
1108 void
1109 winput_set_origin (WInput * in, int x, int field_width)
1111 in->widget.x = x;
1112 in->field_width = in->widget.cols = field_width;
1113 update_input (in, 0);
1116 /* {{{ history saving and loading */
1118 int num_history_items_recorded = 60;
1121 This loads and saves the history of an input line to and from the
1122 widget. It is called with the widgets history name on creation of the
1123 widget, and returns the GList list. It stores histories in the file
1124 ~/.mc/history in using the profile code.
1126 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1127 function) then input_new assigns the default text to be the last text
1128 entered, or "" if not found.
1131 GList *
1132 history_get (const char *input_name)
1134 size_t i;
1135 GList *hist = NULL;
1136 char *profile;
1137 mc_config_t *cfg;
1138 char **keys;
1139 size_t keys_num = 0;
1140 char *this_entry;
1142 if (num_history_items_recorded == 0) /* this is how to disable */
1143 return NULL;
1144 if ((input_name == NULL) || (*input_name == '\0'))
1145 return NULL;
1147 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1148 cfg = mc_config_init (profile);
1150 /* get number of keys */
1151 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1152 g_strfreev (keys);
1154 for (i = 0; i < keys_num; i++)
1156 char key[BUF_TINY];
1157 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1158 this_entry = mc_config_get_string (cfg, input_name, key, "");
1160 if (this_entry != NULL)
1161 hist = list_append_unique (hist, this_entry);
1164 mc_config_deinit (cfg);
1165 g_free (profile);
1167 /* return pointer to the last entry in the list */
1168 return g_list_last (hist);
1171 void
1172 history_put (const char *input_name, GList * h)
1174 int i;
1175 char *profile;
1176 mc_config_t *cfg;
1178 if (num_history_items_recorded == 0) /* this is how to disable */
1179 return;
1180 if ((input_name == NULL) || (*input_name == '\0'))
1181 return;
1182 if (h == NULL)
1183 return;
1185 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1187 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1188 if (i != -1)
1189 close (i);
1191 /* Make sure the history is only readable by the user */
1192 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1194 g_free (profile);
1195 return;
1198 /* go to end of list */
1199 h = g_list_last (h);
1201 /* go back 60 places */
1202 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1203 h = g_list_previous (h);
1205 cfg = mc_config_init (profile);
1207 if (input_name != NULL)
1208 mc_config_del_group (cfg, input_name);
1210 /* dump history into profile */
1211 for (i = 0; h != NULL; h = g_list_next (h))
1213 char *text = (char *) h->data;
1215 /* We shouldn't have null entries, but let's be sure */
1216 if (text != NULL)
1218 char key[BUF_TINY];
1219 g_snprintf (key, sizeof (key), "%d", i++);
1220 mc_config_set_string (cfg, input_name, key, text);
1224 mc_config_save_file (cfg, NULL);
1225 mc_config_deinit (cfg);
1226 g_free (profile);
1229 /* }}} history saving and loading */
1232 /* {{{ history display */
1234 static const char *
1235 i18n_htitle (void)
1237 return _(" History ");
1240 typedef struct
1242 Widget *widget;
1243 size_t count;
1244 size_t maxlen;
1245 } dlg_hist_data;
1247 static cb_ret_t
1248 dlg_hist_reposition (Dlg_head * dlg_head)
1250 dlg_hist_data *data;
1251 int x = 0, y, he, wi;
1253 /* guard checks */
1254 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1255 return MSG_NOT_HANDLED;
1257 data = (dlg_hist_data *) dlg_head->data;
1259 y = data->widget->y;
1260 he = data->count + 2;
1262 if (he <= y || y > (LINES - 6))
1264 he = min (he, y - 1);
1265 y -= he;
1267 else
1269 y++;
1270 he = min (he, LINES - y);
1273 if (data->widget->x > 2)
1274 x = data->widget->x - 2;
1276 wi = data->maxlen + 4;
1278 if ((wi + x) > COLS)
1280 wi = min (wi, COLS);
1281 x = COLS - wi;
1284 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1286 return MSG_HANDLED;
1289 static cb_ret_t
1290 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1292 switch (msg)
1294 case DLG_RESIZE:
1295 return dlg_hist_reposition (h);
1297 default:
1298 return default_dlg_callback (h, sender, msg, parm, data);
1302 char *
1303 show_hist (GList ** history, Widget * widget)
1305 GList *z, *hlist = NULL, *hi;
1306 size_t maxlen, i, count = 0;
1307 char *r = NULL;
1308 Dlg_head *query_dlg;
1309 WListbox *query_list;
1310 dlg_hist_data hist_data;
1312 if (*history == NULL)
1313 return NULL;
1315 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1317 for (z = *history; z != NULL; z = g_list_previous (z))
1319 WLEntry *entry;
1321 i = str_term_width1 ((char *) z->data);
1322 maxlen = max (maxlen, i);
1323 count++;
1325 entry = g_new0 (WLEntry, 1);
1326 /* history is being reverted here */
1327 entry->text = g_strdup ((char *) z->data);
1328 hlist = g_list_prepend (hlist, entry);
1331 hist_data.widget = widget;
1332 hist_data.count = count;
1333 hist_data.maxlen = maxlen;
1335 query_dlg =
1336 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1337 "[History-query]", i18n_htitle (), DLG_COMPACT);
1338 query_dlg->data = &hist_data;
1340 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1342 /* this call makes list stick to all sides of dialog, effectively make
1343 it be resized with dialog */
1344 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1346 /* to avoid diplicating of (calculating sizes in two places)
1347 code, call dlg_hist_callback function here, to set dialog and
1348 controls positions.
1349 The main idea - create 4x4 dialog and add 2x2 list in
1350 center of it, and let dialog function resize it to needed
1351 size. */
1352 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1354 if (query_dlg->y < widget->y)
1356 /* draw list entries from bottom upto top */
1357 listbox_set_list (query_list, hlist);
1358 listbox_select_last (query_list);
1360 else
1362 /* draw list entries from top downto bottom */
1363 /* revert history direction */
1364 hlist = g_list_reverse (hlist);
1365 listbox_set_list (query_list, hlist);
1368 if (run_dlg (query_dlg) != B_CANCEL)
1370 char *q;
1372 listbox_get_current (query_list, &q, NULL);
1373 if (q != NULL)
1374 r = g_strdup (q);
1377 /* get modified history from dialog */
1378 z = NULL;
1379 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1381 WLEntry *entry;
1383 entry = (WLEntry *) hi->data;
1384 /* history is being reverted here again */
1385 z = g_list_prepend (z, entry->text);
1386 entry->text = NULL;
1389 destroy_dlg (query_dlg);
1391 /* restore history direction */
1392 if (query_dlg->y < widget->y)
1393 z = g_list_reverse (z);
1395 g_list_foreach (*history, (GFunc) g_free, NULL);
1396 g_list_free (*history);
1397 *history = g_list_last (z);
1399 return r;
1402 static void
1403 do_show_hist (WInput * in)
1405 char *r;
1407 r = show_hist (&in->history, &in->widget);
1408 if (r != NULL)
1410 assign_text (in, r);
1411 g_free (r);
1415 /* }}} history display */
1417 static void
1418 input_destroy (WInput * in)
1420 if (in == NULL)
1422 fprintf (stderr, "Internal error: null Input *\n");
1423 exit (EXIT_FAILURE);
1426 new_input (in);
1428 if (in->history != NULL)
1430 if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
1431 history_put (in->history_name, in->history);
1433 in->history = g_list_first (in->history);
1434 g_list_foreach (in->history, (GFunc) g_free, NULL);
1435 g_list_free (in->history);
1438 g_free (in->buffer);
1439 free_completions (in);
1440 g_free (in->history_name);
1442 g_free (kill_buffer);
1443 kill_buffer = NULL;
1446 void
1447 input_disable_update (WInput * in)
1449 in->disable_update++;
1452 void
1453 input_enable_update (WInput * in)
1455 in->disable_update--;
1456 update_input (in, 0);
1460 static void
1461 push_history (WInput * in, const char *text)
1463 /* input widget where urls with passwords are entered without any
1464 vfs prefix */
1465 const char *password_input_fields[] = {
1466 N_(" Link to a remote machine "),
1467 N_(" FTP to machine "),
1468 N_(" SMB link to machine ")
1470 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1472 char *t;
1473 size_t i;
1474 gboolean empty;
1476 if (text == NULL)
1477 return;
1479 #ifdef ENABLE_NLS
1480 for (i = 0; i < ELEMENTS; i++)
1481 password_input_fields[i] = _(password_input_fields[i]);
1482 #endif
1484 t = g_strstrip (g_strdup (text));
1485 empty = *t == '\0';
1486 g_free (t);
1487 t = g_strdup (empty ? "" : text);
1489 if (in->history_name != NULL)
1491 const char *p = in->history_name + 3;
1493 for (i = 0; i < ELEMENTS; i++)
1494 if (strcmp (p, password_input_fields[i]) == 0)
1495 break;
1497 strip_password (t, i >= ELEMENTS);
1500 in->history = list_append_unique (in->history, t);
1501 in->need_push = 0;
1504 /* Cleans the input line and adds the current text to the history */
1505 void
1506 new_input (WInput * in)
1508 push_history (in, in->buffer);
1509 in->need_push = 1;
1510 in->buffer[0] = '\0';
1511 in->point = 0;
1512 in->charpoint = 0;
1513 in->mark = 0;
1514 free_completions (in);
1515 update_input (in, 0);
1518 static void
1519 move_buffer_backward (WInput * in, int start, int end)
1521 int i, pos, len;
1522 int str_len = str_length (in->buffer);
1523 if (start >= str_len || end > str_len + 1)
1524 return;
1526 pos = str_offset_to_pos (in->buffer, start);
1527 len = str_offset_to_pos (in->buffer, end) - pos;
1529 for (i = pos; in->buffer[i + len - 1]; i++)
1530 in->buffer[i] = in->buffer[i + len];
1533 static cb_ret_t
1534 insert_char (WInput * in, int c_code)
1536 size_t i;
1537 int res;
1539 if (c_code == -1)
1540 return MSG_NOT_HANDLED;
1542 if (in->charpoint >= MB_LEN_MAX)
1543 return MSG_HANDLED;
1545 in->charbuf[in->charpoint] = c_code;
1546 in->charpoint++;
1548 res = str_is_valid_char (in->charbuf, in->charpoint);
1549 if (res < 0)
1551 if (res != -2)
1552 in->charpoint = 0; /* broken multibyte char, skip */
1553 return MSG_HANDLED;
1556 in->need_push = 1;
1557 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1559 /* Expand the buffer */
1560 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1561 char *narea = g_try_renew (char, in->buffer, new_length);
1562 if (narea)
1564 in->buffer = narea;
1565 in->current_max_size = new_length;
1569 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1571 /* bytes from begin */
1572 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1573 /* move chars */
1574 size_t rest_bytes = strlen (in->buffer + ins_point);
1576 for (i = rest_bytes + 1; i > 0; i--)
1577 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1579 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1580 in->point++;
1583 in->charpoint = 0;
1584 return MSG_HANDLED;
1587 static void
1588 beginning_of_line (WInput * in)
1590 in->point = 0;
1591 in->charpoint = 0;
1594 static void
1595 end_of_line (WInput * in)
1597 in->point = str_length (in->buffer);
1598 in->charpoint = 0;
1601 static void
1602 backward_char (WInput * in)
1604 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1606 if (in->point > 0)
1608 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1610 in->charpoint = 0;
1613 static void
1614 forward_char (WInput * in)
1616 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1617 if (act[0] != '\0')
1619 in->point += str_cnext_noncomb_char (&act);
1621 in->charpoint = 0;
1624 static void
1625 forward_word (WInput * in)
1627 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1629 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1631 str_cnext_char (&p);
1632 in->point++;
1634 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1636 str_cnext_char (&p);
1637 in->point++;
1641 static void
1642 backward_word (WInput * in)
1644 const char *p;
1645 const char *p_tmp;
1647 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1648 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1650 while (p != in->buffer)
1652 p_tmp = p;
1653 str_cprev_char (&p);
1654 if (!str_isspace (p) && !str_ispunct (p))
1656 p = p_tmp;
1657 break;
1659 in->point--;
1661 while (p != in->buffer)
1663 str_cprev_char (&p);
1664 if (str_isspace (p) || str_ispunct (p))
1665 break;
1667 in->point--;
1671 static void
1672 key_left (WInput * in)
1674 backward_char (in);
1677 static void
1678 key_ctrl_left (WInput * in)
1680 backward_word (in);
1683 static void
1684 key_right (WInput * in)
1686 forward_char (in);
1689 static void
1690 key_ctrl_right (WInput * in)
1692 forward_word (in);
1694 static void
1695 backward_delete (WInput * in)
1697 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1698 int start;
1700 if (in->point == 0)
1701 return;
1703 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1704 move_buffer_backward (in, start, in->point);
1705 in->charpoint = 0;
1706 in->need_push = 1;
1707 in->point = start;
1710 static void
1711 delete_char (WInput * in)
1713 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1714 int end = in->point;
1716 end += str_cnext_noncomb_char (&act);
1718 move_buffer_backward (in, in->point, end);
1719 in->charpoint = 0;
1720 in->need_push = 1;
1723 static void
1724 copy_region (WInput * in, int x_first, int x_last)
1726 int first = min (x_first, x_last);
1727 int last = max (x_first, x_last);
1729 if (last == first)
1731 /* Copy selected files to clipboard */
1732 panel_save_curent_file_to_clip_file ();
1733 return;
1736 g_free (kill_buffer);
1738 first = str_offset_to_pos (in->buffer, first);
1739 last = str_offset_to_pos (in->buffer, last);
1741 kill_buffer = g_strndup (in->buffer + first, last - first);
1742 save_text_to_clip_file (kill_buffer);
1745 static void
1746 delete_region (WInput * in, int x_first, int x_last)
1748 int first = min (x_first, x_last);
1749 int last = max (x_first, x_last);
1750 size_t len;
1752 in->point = first;
1753 if (in->mark > first)
1754 in->mark = first;
1755 last = str_offset_to_pos (in->buffer, last);
1756 first = str_offset_to_pos (in->buffer, first);
1757 len = strlen (&in->buffer[last]) + 1;
1758 memmove (&in->buffer[first], &in->buffer[last], len);
1759 in->charpoint = 0;
1760 in->need_push = 1;
1763 static void
1764 kill_word (WInput * in)
1766 int old_point = in->point;
1767 int new_point;
1769 forward_word (in);
1770 new_point = in->point;
1771 in->point = old_point;
1773 copy_region (in, old_point, new_point);
1774 delete_region (in, old_point, new_point);
1775 in->need_push = 1;
1776 in->charpoint = 0;
1777 in->charpoint = 0;
1780 static void
1781 back_kill_word (WInput * in)
1783 int old_point = in->point;
1784 int new_point;
1786 backward_word (in);
1787 new_point = in->point;
1788 in->point = old_point;
1790 copy_region (in, old_point, new_point);
1791 delete_region (in, old_point, new_point);
1792 in->need_push = 1;
1795 static void
1796 set_mark (WInput * in)
1798 in->mark = in->point;
1801 static void
1802 kill_save (WInput * in)
1804 copy_region (in, in->mark, in->point);
1807 static void
1808 kill_region (WInput * in)
1810 kill_save (in);
1811 delete_region (in, in->point, in->mark);
1814 static void
1815 clear_region (WInput * in)
1817 delete_region (in, in->point, in->mark);
1820 static void
1821 yank (WInput * in)
1823 if (kill_buffer != NULL)
1825 char *p;
1827 in->charpoint = 0;
1828 for (p = kill_buffer; *p != '\0'; p++)
1829 insert_char (in, *p);
1830 in->charpoint = 0;
1834 static void
1835 kill_line (WInput * in)
1837 int chp = str_offset_to_pos (in->buffer, in->point);
1838 g_free (kill_buffer);
1839 kill_buffer = g_strdup (&in->buffer[chp]);
1840 in->buffer[chp] = '\0';
1841 in->charpoint = 0;
1844 static void
1845 ins_from_clip (WInput * in)
1847 char *p = NULL;
1849 if (load_text_from_clip_file (&p))
1851 char *pp;
1853 for (pp = p; *pp != '\0'; pp++)
1854 insert_char (in, *pp);
1856 g_free (p);
1860 void
1861 assign_text (WInput * in, const char *text)
1863 free_completions (in);
1864 g_free (in->buffer);
1865 in->buffer = g_strdup (text); /* was in->buffer->text */
1866 in->current_max_size = strlen (in->buffer) + 1;
1867 in->point = str_length (in->buffer);
1868 in->mark = 0;
1869 in->need_push = 1;
1870 in->charpoint = 0;
1873 static void
1874 hist_prev (WInput * in)
1876 GList *prev;
1878 if (!in->history)
1879 return;
1881 if (in->need_push)
1882 push_history (in, in->buffer);
1884 prev = g_list_previous (in->history);
1885 if (prev != NULL)
1887 in->history = prev;
1888 assign_text (in, (char *) prev->data);
1889 in->need_push = 0;
1893 static void
1894 hist_next (WInput * in)
1896 if (in->need_push)
1898 push_history (in, in->buffer);
1899 assign_text (in, "");
1900 return;
1903 if (!in->history)
1904 return;
1906 if (!in->history->next)
1908 assign_text (in, "");
1909 return;
1912 in->history = g_list_next (in->history);
1913 assign_text (in, (char *) in->history->data);
1914 in->need_push = 0;
1917 static void
1918 port_region_marked_for_delete (WInput * in)
1920 in->buffer[0] = '\0';
1921 in->point = 0;
1922 in->first = 0;
1923 in->charpoint = 0;
1926 static cb_ret_t
1927 input_execute_cmd (WInput * in, unsigned long command)
1929 cb_ret_t res = MSG_HANDLED;
1931 switch (command)
1933 case CK_InputBol:
1934 beginning_of_line (in);
1935 break;
1936 case CK_InputEol:
1937 end_of_line (in);
1938 break;
1939 case CK_InputMoveLeft:
1940 key_left (in);
1941 break;
1942 case CK_InputWordLeft:
1943 key_ctrl_left (in);
1944 break;
1945 case CK_InputMoveRight:
1946 key_right (in);
1947 break;
1948 case CK_InputWordRight:
1949 key_ctrl_right (in);
1950 break;
1951 case CK_InputBackwardChar:
1952 backward_char (in);
1953 break;
1954 case CK_InputBackwardWord:
1955 backward_word (in);
1956 break;
1957 case CK_InputForwardChar:
1958 forward_char (in);
1959 break;
1960 case CK_InputForwardWord:
1961 forward_word (in);
1962 break;
1963 case CK_InputBackwardDelete:
1964 backward_delete (in);
1965 break;
1966 case CK_InputDeleteChar:
1967 delete_char (in);
1968 break;
1969 case CK_InputKillWord:
1970 kill_word (in);
1971 break;
1972 case CK_InputBackwardKillWord:
1973 back_kill_word (in);
1974 break;
1975 case CK_InputSetMark:
1976 set_mark (in);
1977 break;
1978 case CK_InputKillRegion:
1979 kill_region (in);
1980 break;
1981 case CK_InputClearLine:
1982 clear_region (in);
1983 break;
1984 case CK_InputKillSave:
1985 kill_save (in);
1986 break;
1987 case CK_InputYank:
1988 yank (in);
1989 break;
1990 case CK_InputPaste:
1991 ins_from_clip (in);
1992 break;
1993 case CK_InputKillLine:
1994 kill_line (in);
1995 break;
1996 case CK_InputHistoryPrev:
1997 hist_prev (in);
1998 break;
1999 case CK_InputHistoryNext:
2000 hist_next (in);
2001 break;
2002 case CK_InputHistoryShow:
2003 do_show_hist (in);
2004 break;
2005 case CK_InputComplete:
2006 complete (in);
2007 break;
2008 default:
2009 res = MSG_NOT_HANDLED;
2012 return res;
2015 /* This function is a test for a special input key used in complete.c */
2016 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2017 and 2 if it is a complete key */
2019 is_in_input_map (WInput * in, int key)
2021 size_t i;
2022 for (i = 0; input_map[i].key != 0; i++)
2023 if (key == input_map[i].key)
2025 input_execute_cmd (in, input_map[i].command);
2026 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2028 return 0;
2031 cb_ret_t
2032 handle_char (WInput * in, int key)
2034 cb_ret_t v;
2035 int i;
2037 v = MSG_NOT_HANDLED;
2039 if (quote)
2041 free_completions (in);
2042 v = insert_char (in, key);
2043 update_input (in, 1);
2044 quote = 0;
2045 return v;
2047 for (i = 0; input_map[i].key; i++)
2049 if (key == input_map[i].key)
2051 if (input_map[i].command != CK_InputComplete)
2052 free_completions (in);
2053 input_execute_cmd (in, input_map[i].command);
2054 update_input (in, 1);
2055 v = MSG_HANDLED;
2056 break;
2059 if (input_map[i].command == 0)
2061 if (key > 255)
2062 return MSG_NOT_HANDLED;
2063 if (in->first)
2064 port_region_marked_for_delete (in);
2065 free_completions (in);
2066 v = insert_char (in, key);
2068 update_input (in, 1);
2069 return v;
2072 /* Inserts text in input line */
2073 void
2074 stuff (WInput * in, const char *text, int insert_extra_space)
2076 input_disable_update (in);
2077 while (*text != '\0')
2078 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2079 if (insert_extra_space)
2080 handle_char (in, ' ');
2081 input_enable_update (in);
2082 update_input (in, 1);
2085 void
2086 input_set_point (WInput * in, int pos)
2088 int max_pos = str_length (in->buffer);
2090 if (pos > max_pos)
2091 pos = max_pos;
2092 if (pos != in->point)
2093 free_completions (in);
2094 in->point = pos;
2095 in->charpoint = 0;
2096 update_input (in, 1);
2099 cb_ret_t
2100 input_callback (Widget * w, widget_msg_t msg, int parm)
2102 WInput *in = (WInput *) w;
2103 cb_ret_t v;
2105 switch (msg)
2107 case WIDGET_KEY:
2108 if (parm == XCTRL ('q'))
2110 quote = 1;
2111 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2112 quote = 0;
2113 return v;
2116 /* Keys we want others to handle */
2117 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2118 || parm == KEY_F (10) || parm == '\n')
2119 return MSG_NOT_HANDLED;
2121 /* When pasting multiline text, insert literal Enter */
2122 if ((parm & ~KEY_M_MASK) == '\n')
2124 quote = 1;
2125 v = handle_char (in, '\n');
2126 quote = 0;
2127 return v;
2130 return handle_char (in, parm);
2132 case WIDGET_COMMAND:
2133 return input_execute_cmd (in, parm);
2135 case WIDGET_FOCUS:
2136 case WIDGET_UNFOCUS:
2137 case WIDGET_DRAW:
2138 update_input (in, 0);
2139 return MSG_HANDLED;
2141 case WIDGET_CURSOR:
2142 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2143 - in->term_first_shown);
2144 return MSG_HANDLED;
2146 case WIDGET_DESTROY:
2147 input_destroy (in);
2148 return MSG_HANDLED;
2150 default:
2151 return default_proc (msg, parm);
2155 static int
2156 input_event (Gpm_Event * event, void *data)
2158 WInput *in = data;
2160 if (event->type & (GPM_DOWN | GPM_DRAG))
2162 dlg_select_widget (in);
2164 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2165 && should_show_history_button (in))
2167 do_show_hist (in);
2169 else
2171 in->point = str_length (in->buffer);
2172 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2173 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2175 update_input (in, 1);
2177 return MOU_NORMAL;
2180 WInput *
2181 input_new (int y, int x, int color, int width, const char *def_text,
2182 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2184 WInput *in = g_new (WInput, 1);
2185 size_t initial_buffer_len;
2187 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2189 /* history setup */
2190 in->history_name = NULL;
2191 in->history = NULL;
2192 if ((histname != NULL) && (*histname != '\0'))
2194 in->history_name = g_strdup (histname);
2195 in->history = history_get (histname);
2198 if (def_text == NULL)
2199 def_text = "";
2200 else if (def_text == INPUT_LAST_TEXT)
2202 if ((in->history != NULL) && (in->history->data != NULL))
2203 def_text = (char *) in->history->data;
2204 else
2205 def_text = "";
2208 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2209 in->widget.options |= W_IS_INPUT;
2210 in->completions = NULL;
2211 in->completion_flags = completion_flags;
2212 in->current_max_size = initial_buffer_len;
2213 in->buffer = g_new (char, initial_buffer_len);
2214 in->color = color;
2215 in->field_width = width;
2216 in->first = 1;
2217 in->term_first_shown = 0;
2218 in->disable_update = 0;
2219 in->mark = 0;
2220 in->need_push = 1;
2221 in->is_password = 0;
2223 strcpy (in->buffer, def_text);
2224 in->point = str_length (in->buffer);
2225 in->charpoint = 0;
2227 return in;
2231 /* Listbox widget */
2233 /* Should draw the scrollbar, but currently draws only
2234 * indications that there is more information
2237 static void
2238 listbox_entry_free (void *data)
2240 WLEntry *e = data;
2241 g_free (e->text);
2242 g_free (e);
2245 static void
2246 listbox_drawscroll (WListbox * l)
2248 const int max_line = l->widget.lines - 1;
2249 int line = 0;
2250 int i;
2252 /* Are we at the top? */
2253 widget_move (&l->widget, 0, l->widget.cols);
2254 if (l->top == 0)
2255 tty_print_one_vline (TRUE);
2256 else
2257 tty_print_char ('^');
2259 /* Are we at the bottom? */
2260 widget_move (&l->widget, max_line, l->widget.cols);
2261 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2262 tty_print_one_vline (TRUE);
2263 else
2264 tty_print_char ('v');
2266 /* Now draw the nice relative pointer */
2267 if (l->count != 0)
2268 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2270 for (i = 1; i < max_line; i++)
2272 widget_move (&l->widget, i, l->widget.cols);
2273 if (i != line)
2274 tty_print_one_vline (TRUE);
2275 else
2276 tty_print_char ('*');
2280 static void
2281 listbox_draw (WListbox * l, gboolean focused)
2283 const Dlg_head *h = l->widget.parent;
2284 const int normalc = DLG_NORMALC (h);
2285 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2287 GList *le;
2288 int pos;
2289 int i;
2290 int sel_line = -1;
2292 le = g_list_nth (l->list, l->top);
2293 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2294 pos = (le == NULL) ? 0 : l->top;
2296 for (i = 0; i < l->widget.lines; i++)
2298 const char *text;
2300 /* Display the entry */
2301 if (pos == l->pos && sel_line == -1)
2303 sel_line = i;
2304 tty_setcolor (selc);
2306 else
2307 tty_setcolor (normalc);
2309 widget_move (&l->widget, i, 1);
2311 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2312 text = "";
2313 else
2315 WLEntry *e = (WLEntry *) le->data;
2316 text = e->text;
2317 le = g_list_next (le);
2318 pos++;
2321 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2324 l->cursor_y = sel_line;
2326 if (l->scrollbar && (l->count > l->widget.lines))
2328 tty_setcolor (normalc);
2329 listbox_drawscroll (l);
2333 static int
2334 listbox_check_hotkey (WListbox * l, int key)
2336 int i;
2337 GList *le;
2339 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2341 WLEntry *e = (WLEntry *) le->data;
2343 if (e->hotkey == key)
2344 return i;
2347 return (-1);
2350 /* Selects the last entry and scrolls the list to the bottom */
2351 void
2352 listbox_select_last (WListbox * l)
2354 l->pos = l->count - 1;
2355 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2358 /* Selects the first entry and scrolls the list to the top */
2359 void
2360 listbox_select_first (WListbox * l)
2362 l->pos = l->top = 0;
2365 void
2366 listbox_set_list (WListbox * l, GList * list)
2368 listbox_remove_list (l);
2370 if (l != NULL)
2372 l->list = list;
2373 l->top = l->pos = 0;
2374 l->count = g_list_length (list);
2378 void
2379 listbox_remove_list (WListbox * l)
2381 if ((l != NULL) && (l->count != 0))
2383 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2384 g_list_free (l->list);
2385 l->list = NULL;
2386 l->count = l->pos = l->top = 0;
2390 void
2391 listbox_remove_current (WListbox * l)
2393 if ((l != NULL) && (l->count != 0))
2395 GList *current;
2397 current = g_list_nth (l->list, l->pos);
2398 l->list = g_list_remove_link (l->list, current);
2399 listbox_entry_free ((WLEntry *) current->data);
2400 g_list_free_1 (current);
2401 l->count--;
2403 if (l->count == 0)
2404 l->top = l->pos = 0;
2405 else if (l->pos >= l->count)
2406 l->pos = l->count - 1;
2410 void
2411 listbox_select_entry (WListbox * l, int dest)
2413 GList *le;
2414 int pos;
2415 gboolean top_seen = FALSE;
2417 if (dest < 0)
2418 return;
2420 /* Special case */
2421 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2423 if (pos == l->top)
2424 top_seen = TRUE;
2426 if (pos == dest)
2428 l->pos = dest;
2429 if (!top_seen)
2430 l->top = l->pos;
2431 else if (l->pos - l->top >= l->widget.lines)
2432 l->top = l->pos - l->widget.lines + 1;
2433 return;
2437 /* If we are unable to find it, set decent values */
2438 l->pos = l->top = 0;
2441 /* Selects from base the pos element */
2442 static int
2443 listbox_select_pos (WListbox * l, int base, int pos)
2445 int last = l->count - 1;
2447 base += pos;
2448 if (base >= last)
2449 base = last;
2451 return base;
2454 static void
2455 listbox_fwd (WListbox * l)
2457 if (l->pos + 1 >= l->count)
2458 listbox_select_first (l);
2459 else
2460 listbox_select_entry (l, l->pos + 1);
2463 static void
2464 listbox_back (WListbox * l)
2466 if (l->pos <= 0)
2467 listbox_select_last (l);
2468 else
2469 listbox_select_entry (l, l->pos - 1);
2472 /* Return MSG_HANDLED if we want a redraw */
2473 static cb_ret_t
2474 listbox_key (WListbox * l, int key)
2476 int i;
2478 cb_ret_t j = MSG_NOT_HANDLED;
2480 if (l->list == NULL)
2481 return MSG_NOT_HANDLED;
2483 /* focus on listbox item N by '0'..'9' keys */
2484 if (key >= '0' && key <= '9')
2486 int oldpos = l->pos;
2487 listbox_select_entry (l, key - '0');
2489 /* need scroll to item? */
2490 if (abs (oldpos - l->pos) > l->widget.lines)
2491 l->top = l->pos;
2493 return MSG_HANDLED;
2496 switch (key)
2498 case KEY_HOME:
2499 case KEY_A1:
2500 case ALT ('<'):
2501 listbox_select_first (l);
2502 return MSG_HANDLED;
2504 case KEY_END:
2505 case KEY_C1:
2506 case ALT ('>'):
2507 listbox_select_last (l);
2508 return MSG_HANDLED;
2510 case XCTRL ('p'):
2511 case KEY_UP:
2512 listbox_back (l);
2513 return MSG_HANDLED;
2515 case XCTRL ('n'):
2516 case KEY_DOWN:
2517 listbox_fwd (l);
2518 return MSG_HANDLED;
2520 case KEY_NPAGE:
2521 case XCTRL ('v'):
2522 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2524 listbox_fwd (l);
2525 j = MSG_HANDLED;
2527 break;
2529 case KEY_PPAGE:
2530 case ALT ('v'):
2531 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2533 listbox_back (l);
2534 j = MSG_HANDLED;
2536 break;
2538 case KEY_DC:
2539 case 'd':
2540 if (l->deletable)
2542 gboolean is_last = (l->pos + 1 >= l->count);
2543 gboolean is_more = (l->top + l->widget.lines >= l->count);
2545 listbox_remove_current (l);
2546 if ((l->top > 0) && (is_last || is_more))
2547 l->top--;
2549 return MSG_HANDLED;
2551 case (KEY_M_SHIFT | KEY_DC):
2552 case 'D':
2553 if (l->deletable && confirm_history_cleanup
2554 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2555 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2556 _("Do you want clean this history?"),
2557 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2559 listbox_remove_list (l);
2560 j = MSG_HANDLED;
2562 break;
2564 default:
2565 break;
2568 return j;
2571 static inline void
2572 listbox_destroy (WListbox * l)
2574 /* don't delete list in modifable listbox */
2575 if (!l->deletable)
2576 listbox_remove_list (l);
2579 static cb_ret_t
2580 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2582 WListbox *l = (WListbox *) w;
2583 Dlg_head *h = l->widget.parent;
2584 cb_ret_t ret_code;
2586 switch (msg)
2588 case WIDGET_INIT:
2589 return MSG_HANDLED;
2591 case WIDGET_HOTKEY:
2593 int pos, action;
2595 pos = listbox_check_hotkey (l, parm);
2596 if (pos < 0)
2597 return MSG_NOT_HANDLED;
2599 listbox_select_entry (l, pos);
2600 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2602 if (l->cback != NULL)
2603 action = l->cback (l);
2604 else
2605 action = LISTBOX_DONE;
2607 if (action == LISTBOX_DONE)
2609 h->ret_value = B_ENTER;
2610 dlg_stop (h);
2613 return MSG_HANDLED;
2616 case WIDGET_KEY:
2617 ret_code = listbox_key (l, parm);
2618 if (ret_code != MSG_NOT_HANDLED)
2620 listbox_draw (l, TRUE);
2621 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2623 return ret_code;
2625 case WIDGET_CURSOR:
2626 widget_move (&l->widget, l->cursor_y, 0);
2627 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2628 return MSG_HANDLED;
2630 case WIDGET_FOCUS:
2631 case WIDGET_UNFOCUS:
2632 case WIDGET_DRAW:
2633 listbox_draw (l, msg != WIDGET_UNFOCUS);
2634 return MSG_HANDLED;
2636 case WIDGET_DESTROY:
2637 listbox_destroy (l);
2638 return MSG_HANDLED;
2640 case WIDGET_RESIZED:
2641 return MSG_HANDLED;
2643 default:
2644 return default_proc (msg, parm);
2648 static int
2649 listbox_event (Gpm_Event * event, void *data)
2651 WListbox *l = data;
2652 int i;
2654 Dlg_head *h = l->widget.parent;
2656 /* Single click */
2657 if (event->type & GPM_DOWN)
2658 dlg_select_widget (l);
2660 if (l->list == NULL)
2661 return MOU_NORMAL;
2663 if (event->type & (GPM_DOWN | GPM_DRAG))
2665 int ret = MOU_REPEAT;
2667 if (event->x < 0 || event->x > l->widget.cols)
2668 return ret;
2670 if (event->y < 1)
2671 for (i = -event->y; i >= 0; i--)
2672 listbox_back (l);
2673 else if (event->y > l->widget.lines)
2674 for (i = event->y - l->widget.lines; i > 0; i--)
2675 listbox_fwd (l);
2676 else if (event->buttons & GPM_B_UP)
2678 listbox_back (l);
2679 ret = MOU_NORMAL;
2681 else if (event->buttons & GPM_B_DOWN)
2683 listbox_fwd (l);
2684 ret = MOU_NORMAL;
2686 else
2687 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2689 /* We need to refresh ourselves since the dialog manager doesn't */
2690 /* know about this event */
2691 listbox_draw (l, TRUE);
2692 return ret;
2695 /* Double click */
2696 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2698 int action;
2700 if (event->x < 0 || event->x >= l->widget.cols
2701 || event->y < 1 || event->y > l->widget.lines)
2702 return MOU_NORMAL;
2704 dlg_select_widget (l);
2705 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2707 if (l->cback != NULL)
2708 action = l->cback (l);
2709 else
2710 action = LISTBOX_DONE;
2712 if (action == LISTBOX_DONE)
2714 h->ret_value = B_ENTER;
2715 dlg_stop (h);
2716 return MOU_NORMAL;
2719 return MOU_NORMAL;
2722 WListbox *
2723 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2725 WListbox *l = g_new (WListbox, 1);
2727 if (height <= 0)
2728 height = 1;
2730 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2732 l->list = NULL;
2733 l->top = l->pos = 0;
2734 l->count = 0;
2735 l->deletable = deletable;
2736 l->cback = callback;
2737 l->allow_duplicates = TRUE;
2738 l->scrollbar = !tty_is_slow ();
2739 widget_want_hotkey (l->widget, 1);
2741 return l;
2744 static int
2745 listbox_entry_cmp (const void *a, const void *b)
2747 const WLEntry *ea = (const WLEntry *) a;
2748 const WLEntry *eb = (const WLEntry *) b;
2750 return strcmp (ea->text, eb->text);
2753 /* Listbox item adding function */
2754 static inline void
2755 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2757 switch (pos)
2759 case LISTBOX_APPEND_AT_END:
2760 l->list = g_list_append (l->list, e);
2761 break;
2763 case LISTBOX_APPEND_BEFORE:
2764 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2765 if (l->pos > 0)
2766 l->pos--;
2767 break;
2769 case LISTBOX_APPEND_AFTER:
2770 l->list = g_list_insert (l->list, e, l->pos + 1);
2771 break;
2773 case LISTBOX_APPEND_SORTED:
2774 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2775 break;
2777 default:
2778 return;
2781 l->count++;
2784 char *
2785 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2787 WLEntry *entry;
2789 if (l == NULL)
2790 return NULL;
2792 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2793 return NULL;
2795 entry = g_new (WLEntry, 1);
2796 entry->text = g_strdup (text);
2797 entry->data = data;
2798 entry->hotkey = hotkey;
2800 listbox_append_item (l, entry, pos);
2802 return entry->text;
2806 listbox_search_text (WListbox * l, const char *text)
2808 if (l != NULL)
2810 int i;
2811 GList *le;
2813 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2815 WLEntry *e = (WLEntry *) le->data;
2817 if (strcmp (e->text, text) == 0)
2818 return i;
2822 return (-1);
2825 /* Returns the current string text as well as the associated extra data */
2826 void
2827 listbox_get_current (WListbox * l, char **string, void **extra)
2829 WLEntry *e = NULL;
2830 gboolean ok;
2832 if (l != NULL)
2833 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2835 ok = (e != NULL);
2837 if (string != NULL)
2838 *string = ok ? e->text : NULL;
2840 if (extra != NULL)
2841 *extra = ok ? e->data : NULL;
2845 /* ButtonBar widget */
2847 /* returns TRUE if a function has been called, FALSE otherwise. */
2848 static gboolean
2849 buttonbar_call (WButtonBar * bb, int i)
2851 cb_ret_t ret = MSG_NOT_HANDLED;
2853 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
2854 ret = bb->widget.parent->callback (bb->widget.parent,
2855 (Widget *) bb, DLG_ACTION,
2856 bb->labels[i].command, bb->labels[i].receiver);
2857 return ret;
2860 /* calculate width of one button, width is never lesser than 7 */
2861 static int
2862 buttonbat_get_button_width (void)
2864 int result = COLS / BUTTONBAR_LABELS_NUM;
2865 return (result >= 7) ? result : 7;
2868 static cb_ret_t
2869 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
2871 WButtonBar *bb = (WButtonBar *) w;
2872 int i;
2873 const char *text;
2875 switch (msg)
2877 case WIDGET_FOCUS:
2878 return MSG_NOT_HANDLED;
2880 case WIDGET_HOTKEY:
2881 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2882 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2883 return MSG_HANDLED;
2884 return MSG_NOT_HANDLED;
2886 case WIDGET_DRAW:
2887 if (bb->visible)
2889 int offset = 0;
2890 int count_free_positions;
2892 widget_move (&bb->widget, 0, 0);
2893 tty_setcolor (DEFAULT_COLOR);
2894 bb->btn_width = buttonbat_get_button_width ();
2895 tty_printf ("%-*s", bb->widget.cols, "");
2896 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2898 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++)
2900 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2901 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2902 tty_printf ("%2d", i + 1);
2903 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2904 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2905 tty_print_string (str_fit_to_term (text,
2906 bb->btn_width - 2 + (int) (offset <
2907 count_free_positions),
2908 J_LEFT_FIT));
2910 if (count_free_positions != 0 && offset < count_free_positions)
2911 offset++;
2914 return MSG_HANDLED;
2916 case WIDGET_DESTROY:
2917 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2918 g_free (bb->labels[i].text);
2919 return MSG_HANDLED;
2921 default:
2922 return default_proc (msg, parm);
2926 static int
2927 buttonbar_event (Gpm_Event * event, void *data)
2929 WButtonBar *bb = data;
2930 int button;
2932 if (!(event->type & GPM_UP))
2933 return MOU_NORMAL;
2934 if (event->y == 2)
2935 return MOU_NORMAL;
2936 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2937 if (button < BUTTONBAR_LABELS_NUM)
2938 buttonbar_call (bb, button);
2939 return MOU_NORMAL;
2942 WButtonBar *
2943 buttonbar_new (gboolean visible)
2945 WButtonBar *bb;
2947 bb = g_new0 (WButtonBar, 1);
2949 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
2950 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2951 bb->visible = visible;
2952 widget_want_hotkey (bb->widget, 1);
2953 widget_want_cursor (bb->widget, 0);
2954 bb->btn_width = buttonbat_get_button_width ();
2956 return bb;
2959 static void
2960 set_label_text (WButtonBar * bb, int lc_index, const char *text)
2962 g_free (bb->labels[lc_index - 1].text);
2963 bb->labels[lc_index - 1].text = g_strdup (text);
2966 /* Find ButtonBar widget in the dialog */
2967 WButtonBar *
2968 find_buttonbar (const Dlg_head * h)
2970 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2973 void
2974 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
2975 const struct global_keymap_t *keymap, const Widget * receiver)
2977 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
2979 unsigned long command = CK_Ignore_Key;
2981 if (keymap != NULL)
2982 command = lookup_keymap_command (keymap, KEY_F (idx));
2984 if ((text == NULL) || (text[0] == '\0'))
2985 set_label_text (bb, idx, "");
2986 else
2987 set_label_text (bb, idx, text);
2989 bb->labels[idx - 1].command = command;
2990 bb->labels[idx - 1].receiver = (Widget *) receiver;
2994 void
2995 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
2997 bb->visible = visible;
3000 void
3001 buttonbar_redraw (WButtonBar * bb)
3003 if (bb != NULL)
3004 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3007 static cb_ret_t
3008 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3010 WGroupbox *g = (WGroupbox *) w;
3012 switch (msg)
3014 case WIDGET_INIT:
3015 return MSG_HANDLED;
3017 case WIDGET_FOCUS:
3018 return MSG_NOT_HANDLED;
3020 case WIDGET_DRAW:
3021 tty_setcolor (COLOR_NORMAL);
3022 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
3023 g->widget.x - g->widget.parent->x, g->widget.lines, g->widget.cols, TRUE);
3025 tty_setcolor (COLOR_HOT_NORMAL);
3026 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
3027 g->widget.x - g->widget.parent->x + 1);
3028 tty_print_string (g->title);
3029 return MSG_HANDLED;
3031 case WIDGET_DESTROY:
3032 g_free (g->title);
3033 return MSG_HANDLED;
3035 default:
3036 return default_proc (msg, parm);
3040 WGroupbox *
3041 groupbox_new (int y, int x, int height, int width, const char *title)
3043 WGroupbox *g = g_new (WGroupbox, 1);
3045 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3047 g->widget.options &= ~W_WANT_CURSOR;
3048 widget_want_hotkey (g->widget, 0);
3050 /* Strip existing spaces, add one space before and after the title */
3051 if (title)
3053 char *t;
3054 t = g_strstrip (g_strdup (title));
3055 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3056 g_free (t);
3059 return g;