diffviewer: Added libmc for build depends
[midnight-commander.git] / src / widget.c
blob34e9c46054595986f7007047db44f43d5d14a627
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;
619 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
620 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
621 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
622 g_free (fname);
624 if (file == -1)
625 return FALSE;
627 mc_write (file, (char *) text, strlen (text));
628 mc_close (file);
629 return TRUE;
632 static gboolean
633 load_text_from_clip_file (char **text)
635 char buf[BUF_LARGE];
636 FILE *f;
637 char *fname = NULL;
638 gboolean first = TRUE;
640 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
641 f = fopen (fname, "r");
642 g_free (fname);
644 if (f == NULL)
645 return FALSE;
647 *text = NULL;
649 while (fgets (buf, sizeof (buf), f))
651 size_t len;
653 len = strlen (buf);
654 if (len > 0)
656 if (buf[len - 1] == '\n')
657 buf[len - 1] = '\0';
659 if (first)
661 first = FALSE;
662 *text = g_strdup (buf);
664 else
666 /* remove \n on EOL */
667 char *tmp;
669 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
670 g_free (*text);
671 *text = tmp;
676 fclose (f);
678 return (*text != NULL);
681 static gboolean
682 panel_save_curent_file_to_clip_file (void)
684 gboolean res;
686 if (current_panel->marked == 0)
687 res = save_text_to_clip_file (selection (current_panel)->fname);
688 else
690 int i;
691 gboolean first = TRUE;
692 char *flist = NULL;
694 for (i = 0; i < current_panel->count; i++)
695 if (current_panel->dir.list[i].f.marked != 0)
696 { /* Skip the unmarked ones */
697 if (first)
699 flist = g_strdup (current_panel->dir.list[i].fname);
700 first = FALSE;
702 else
704 /* Add empty lines after the file */
705 char *tmp;
707 tmp =
708 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
709 g_free (flist);
710 flist = tmp;
714 if (flist != NULL)
716 res = save_text_to_clip_file (flist);
717 g_free (flist);
720 return res;
723 /* Label widget */
725 static cb_ret_t
726 label_callback (Widget * w, widget_msg_t msg, int parm)
728 WLabel *l = (WLabel *) w;
729 Dlg_head *h = l->widget.parent;
731 switch (msg)
733 case WIDGET_INIT:
734 return MSG_HANDLED;
736 /* We don't want to get the focus */
737 case WIDGET_FOCUS:
738 return MSG_NOT_HANDLED;
740 case WIDGET_DRAW:
742 char *p = l->text, *q, c = 0;
743 int y = 0;
745 if (!l->text)
746 return MSG_HANDLED;
748 if (l->transparent)
749 tty_setcolor (DEFAULT_COLOR);
750 else
751 tty_setcolor (DLG_NORMALC (h));
753 for (;;)
755 q = strchr (p, '\n');
756 if (q != NULL)
758 c = q[0];
759 q[0] = '\0';
762 widget_move (&l->widget, y, 0);
763 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
765 if (q == NULL)
766 break;
767 q[0] = c;
768 p = q + 1;
769 y++;
771 return MSG_HANDLED;
774 case WIDGET_DESTROY:
775 g_free (l->text);
776 return MSG_HANDLED;
778 default:
779 return default_proc (msg, parm);
783 void
784 label_set_text (WLabel * label, const char *text)
786 int newcols = label->widget.cols;
787 int newlines;
789 if (label->text && text && !strcmp (label->text, text))
790 return; /* Flickering is not nice */
792 g_free (label->text);
794 if (text != NULL)
796 label->text = g_strdup (text);
797 if (label->auto_adjust_cols)
799 str_msg_term_size (text, &newlines, &newcols);
800 if (newcols > label->widget.cols)
801 label->widget.cols = newcols;
802 if (newlines > label->widget.lines)
803 label->widget.lines = newlines;
806 else
807 label->text = NULL;
809 if (label->widget.parent)
810 label_callback ((Widget *) label, WIDGET_DRAW, 0);
812 if (newcols < label->widget.cols)
813 label->widget.cols = newcols;
816 WLabel *
817 label_new (int y, int x, const char *text)
819 WLabel *l;
820 int cols = 1;
821 int lines = 1;
823 if (text != NULL)
824 str_msg_term_size (text, &lines, &cols);
826 l = g_new (WLabel, 1);
827 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
828 l->text = (text != NULL) ? g_strdup (text) : NULL;
829 l->auto_adjust_cols = 1;
830 l->transparent = 0;
831 widget_want_cursor (l->widget, 0);
832 return l;
835 static cb_ret_t
836 hline_callback (Widget * w, widget_msg_t msg, int parm)
838 WHLine *l = (WHLine *) w;
839 Dlg_head *h = l->widget.parent;
841 switch (msg)
843 case WIDGET_INIT:
844 case WIDGET_RESIZED:
845 if (l->auto_adjust_cols)
847 if (((w->parent->flags & DLG_COMPACT) != 0))
849 w->x = w->parent->x;
850 w->cols = w->parent->cols;
852 else
854 w->x = w->parent->x + 1;
855 w->cols = w->parent->cols - 2;
859 case WIDGET_FOCUS:
860 /* We don't want to get the focus */
861 return MSG_NOT_HANDLED;
863 case WIDGET_DRAW:
864 if (l->transparent)
865 tty_setcolor (DEFAULT_COLOR);
866 else
867 tty_setcolor (DLG_NORMALC (h));
869 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
871 if (l->auto_adjust_cols)
873 widget_move (w, 0, 0);
874 tty_print_alt_char (ACS_LTEE, FALSE);
875 widget_move (w, 0, w->cols - 1);
876 tty_print_alt_char (ACS_RTEE, FALSE);
878 return MSG_HANDLED;
880 default:
881 return default_proc (msg, parm);
886 WHLine *
887 hline_new (int y, int x, int width)
889 WHLine *l;
890 int cols = width;
891 int lines = 1;
893 l = g_new (WHLine, 1);
894 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
895 l->auto_adjust_cols = (width < 0);
896 l->transparent = FALSE;
897 widget_want_cursor (l->widget, 0);
898 return l;
901 /* Gauge widget (progress indicator) */
902 /* Currently width is hardcoded here for text mode */
903 #define gauge_len 47
905 static cb_ret_t
906 gauge_callback (Widget * w, widget_msg_t msg, int parm)
908 WGauge *g = (WGauge *) w;
909 Dlg_head *h = g->widget.parent;
911 if (msg == WIDGET_INIT)
912 return MSG_HANDLED;
914 /* We don't want to get the focus */
915 if (msg == WIDGET_FOCUS)
916 return MSG_NOT_HANDLED;
918 if (msg == WIDGET_DRAW)
920 widget_move (&g->widget, 0, 0);
921 tty_setcolor (DLG_NORMALC (h));
922 if (!g->shown)
923 tty_printf ("%*s", gauge_len, "");
924 else
926 int percentage, columns;
927 long total = g->max, done = g->current;
929 if (total <= 0 || done < 0)
931 done = 0;
932 total = 100;
934 if (done > total)
935 done = total;
936 while (total > 65535)
938 total /= 256;
939 done /= 256;
941 percentage = (200 * done / total + 1) / 2;
942 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
943 tty_print_char ('[');
944 if (g->from_left_to_right)
946 tty_setcolor (GAUGE_COLOR);
947 tty_printf ("%*s", (int) columns, "");
948 tty_setcolor (DLG_NORMALC (h));
949 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
951 else
953 tty_setcolor (DLG_NORMALC (h));
954 tty_printf ("%*s", gauge_len - columns - 7, "");
955 tty_setcolor (GAUGE_COLOR);
956 tty_printf ("%*s", columns, "");
957 tty_setcolor (DLG_NORMALC (h));
958 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
961 return MSG_HANDLED;
964 return default_proc (msg, parm);
967 void
968 gauge_set_value (WGauge * g, int max, int current)
970 if (g->current == current && g->max == max)
971 return; /* Do not flicker */
972 if (max == 0)
973 max = 1; /* I do not like division by zero :) */
975 g->current = current;
976 g->max = max;
977 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
980 void
981 gauge_show (WGauge * g, int shown)
983 if (g->shown == shown)
984 return;
985 g->shown = shown;
986 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
989 WGauge *
990 gauge_new (int y, int x, int shown, int max, int current)
992 WGauge *g = g_new (WGauge, 1);
994 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
995 g->shown = shown;
996 if (max == 0)
997 max = 1; /* I do not like division by zero :) */
998 g->max = max;
999 g->current = current;
1000 g->from_left_to_right = TRUE;
1001 widget_want_cursor (g->widget, 0);
1002 return g;
1006 /* Input widget */
1008 /* {{{ history button */
1010 #define LARGE_HISTORY_BUTTON 1
1012 #ifdef LARGE_HISTORY_BUTTON
1013 # define HISTORY_BUTTON_WIDTH 3
1014 #else
1015 # define HISTORY_BUTTON_WIDTH 1
1016 #endif
1018 #define should_show_history_button(in) \
1019 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1021 static void
1022 draw_history_button (WInput * in)
1024 char c;
1025 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1026 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1027 #ifdef LARGE_HISTORY_BUTTON
1029 Dlg_head *h;
1030 h = in->widget.parent;
1031 tty_setcolor (NORMAL_COLOR);
1032 tty_print_string ("[ ]");
1033 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1034 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1035 tty_print_char (c);
1037 #else
1038 tty_setcolor (MARKED_COLOR);
1039 tty_print_char (c);
1040 #endif
1043 /* }}} history button */
1046 /* Input widgets now have a global kill ring */
1047 /* Pointer to killed data */
1048 static char *kill_buffer = NULL;
1050 void
1051 update_input (WInput * in, int clear_first)
1053 int has_history = 0;
1054 int i;
1055 int buf_len = str_length (in->buffer);
1056 const char *cp;
1057 int pw;
1059 if (should_show_history_button (in))
1060 has_history = HISTORY_BUTTON_WIDTH;
1062 if (in->disable_update)
1063 return;
1065 pw = str_term_width2 (in->buffer, in->point);
1067 /* Make the point visible */
1068 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1071 in->term_first_shown = pw - (in->field_width / 3);
1072 if (in->term_first_shown < 0)
1073 in->term_first_shown = 0;
1076 /* Adjust the mark */
1077 if (in->mark > buf_len)
1078 in->mark = buf_len;
1080 if (has_history)
1081 draw_history_button (in);
1083 tty_setcolor (in->color);
1085 widget_move (&in->widget, 0, 0);
1087 if (!in->is_password)
1089 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1090 in->field_width - has_history));
1092 else
1094 cp = in->buffer;
1095 for (i = -in->term_first_shown; i < in->field_width - has_history; i++)
1097 if (i >= 0)
1099 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1101 if (cp[0] != '\0')
1102 str_cnext_char (&cp);
1106 if (clear_first)
1107 in->first = 0;
1110 void
1111 winput_set_origin (WInput * in, int x, int field_width)
1113 in->widget.x = x;
1114 in->field_width = in->widget.cols = field_width;
1115 update_input (in, 0);
1118 /* {{{ history saving and loading */
1120 int num_history_items_recorded = 60;
1123 This loads and saves the history of an input line to and from the
1124 widget. It is called with the widgets history name on creation of the
1125 widget, and returns the GList list. It stores histories in the file
1126 ~/.mc/history in using the profile code.
1128 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1129 function) then input_new assigns the default text to be the last text
1130 entered, or "" if not found.
1133 GList *
1134 history_get (const char *input_name)
1136 size_t i;
1137 GList *hist = NULL;
1138 char *profile;
1139 mc_config_t *cfg;
1140 char **keys;
1141 size_t keys_num = 0;
1142 char *this_entry;
1144 if (num_history_items_recorded == 0) /* this is how to disable */
1145 return NULL;
1146 if ((input_name == NULL) || (*input_name == '\0'))
1147 return NULL;
1149 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1150 cfg = mc_config_init (profile);
1152 /* get number of keys */
1153 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1154 g_strfreev (keys);
1156 for (i = 0; i < keys_num; i++)
1158 char key[BUF_TINY];
1159 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1160 this_entry = mc_config_get_string (cfg, input_name, key, "");
1162 if (this_entry != NULL)
1163 hist = list_append_unique (hist, this_entry);
1166 mc_config_deinit (cfg);
1167 g_free (profile);
1169 /* return pointer to the last entry in the list */
1170 return g_list_last (hist);
1173 void
1174 history_put (const char *input_name, GList * h)
1176 int i;
1177 char *profile;
1178 mc_config_t *cfg;
1180 if (num_history_items_recorded == 0) /* this is how to disable */
1181 return;
1182 if ((input_name == NULL) || (*input_name == '\0'))
1183 return;
1184 if (h == NULL)
1185 return;
1187 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1189 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1190 if (i != -1)
1191 close (i);
1193 /* Make sure the history is only readable by the user */
1194 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1196 g_free (profile);
1197 return;
1200 /* go to end of list */
1201 h = g_list_last (h);
1203 /* go back 60 places */
1204 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1205 h = g_list_previous (h);
1207 cfg = mc_config_init (profile);
1209 if (input_name != NULL)
1210 mc_config_del_group (cfg, input_name);
1212 /* dump history into profile */
1213 for (i = 0; h != NULL; h = g_list_next (h))
1215 char *text = (char *) h->data;
1217 /* We shouldn't have null entries, but let's be sure */
1218 if (text != NULL)
1220 char key[BUF_TINY];
1221 g_snprintf (key, sizeof (key), "%d", i++);
1222 mc_config_set_string (cfg, input_name, key, text);
1226 mc_config_save_file (cfg, NULL);
1227 mc_config_deinit (cfg);
1228 g_free (profile);
1231 /* }}} history saving and loading */
1234 /* {{{ history display */
1236 static const char *
1237 i18n_htitle (void)
1239 return _(" History ");
1242 typedef struct
1244 Widget *widget;
1245 size_t count;
1246 size_t maxlen;
1247 } dlg_hist_data;
1249 static cb_ret_t
1250 dlg_hist_reposition (Dlg_head * dlg_head)
1252 dlg_hist_data *data;
1253 int x = 0, y, he, wi;
1255 /* guard checks */
1256 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1257 return MSG_NOT_HANDLED;
1259 data = (dlg_hist_data *) dlg_head->data;
1261 y = data->widget->y;
1262 he = data->count + 2;
1264 if (he <= y || y > (LINES - 6))
1266 he = min (he, y - 1);
1267 y -= he;
1269 else
1271 y++;
1272 he = min (he, LINES - y);
1275 if (data->widget->x > 2)
1276 x = data->widget->x - 2;
1278 wi = data->maxlen + 4;
1280 if ((wi + x) > COLS)
1282 wi = min (wi, COLS);
1283 x = COLS - wi;
1286 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1288 return MSG_HANDLED;
1291 static cb_ret_t
1292 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1294 switch (msg)
1296 case DLG_RESIZE:
1297 return dlg_hist_reposition (h);
1299 default:
1300 return default_dlg_callback (h, sender, msg, parm, data);
1304 char *
1305 show_hist (GList ** history, Widget * widget)
1307 GList *z, *hlist = NULL, *hi;
1308 size_t maxlen, i, count = 0;
1309 char *r = NULL;
1310 Dlg_head *query_dlg;
1311 WListbox *query_list;
1312 dlg_hist_data hist_data;
1314 if (*history == NULL)
1315 return NULL;
1317 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1319 for (z = *history; z != NULL; z = g_list_previous (z))
1321 WLEntry *entry;
1323 i = str_term_width1 ((char *) z->data);
1324 maxlen = max (maxlen, i);
1325 count++;
1327 entry = g_new0 (WLEntry, 1);
1328 /* history is being reverted here */
1329 entry->text = g_strdup ((char *) z->data);
1330 hlist = g_list_prepend (hlist, entry);
1333 hist_data.widget = widget;
1334 hist_data.count = count;
1335 hist_data.maxlen = maxlen;
1337 query_dlg =
1338 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1339 "[History-query]", i18n_htitle (), DLG_COMPACT);
1340 query_dlg->data = &hist_data;
1342 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1344 /* this call makes list stick to all sides of dialog, effectively make
1345 it be resized with dialog */
1346 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1348 /* to avoid diplicating of (calculating sizes in two places)
1349 code, call dlg_hist_callback function here, to set dialog and
1350 controls positions.
1351 The main idea - create 4x4 dialog and add 2x2 list in
1352 center of it, and let dialog function resize it to needed
1353 size. */
1354 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1356 if (query_dlg->y < widget->y)
1358 /* draw list entries from bottom upto top */
1359 listbox_set_list (query_list, hlist);
1360 listbox_select_last (query_list);
1362 else
1364 /* draw list entries from top downto bottom */
1365 /* revert history direction */
1366 hlist = g_list_reverse (hlist);
1367 listbox_set_list (query_list, hlist);
1370 if (run_dlg (query_dlg) != B_CANCEL)
1372 char *q;
1374 listbox_get_current (query_list, &q, NULL);
1375 if (q != NULL)
1376 r = g_strdup (q);
1379 /* get modified history from dialog */
1380 z = NULL;
1381 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1383 WLEntry *entry;
1385 entry = (WLEntry *) hi->data;
1386 /* history is being reverted here again */
1387 z = g_list_prepend (z, entry->text);
1388 entry->text = NULL;
1391 destroy_dlg (query_dlg);
1393 /* restore history direction */
1394 if (query_dlg->y < widget->y)
1395 z = g_list_reverse (z);
1397 g_list_foreach (*history, (GFunc) g_free, NULL);
1398 g_list_free (*history);
1399 *history = g_list_last (z);
1401 return r;
1404 static void
1405 do_show_hist (WInput * in)
1407 char *r;
1409 r = show_hist (&in->history, &in->widget);
1410 if (r != NULL)
1412 assign_text (in, r);
1413 g_free (r);
1417 /* }}} history display */
1419 static void
1420 input_destroy (WInput * in)
1422 if (in == NULL)
1424 fprintf (stderr, "Internal error: null Input *\n");
1425 exit (1);
1428 new_input (in);
1430 if (in->history != NULL)
1432 if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
1433 history_put (in->history_name, in->history);
1435 in->history = g_list_first (in->history);
1436 g_list_foreach (in->history, (GFunc) g_free, NULL);
1437 g_list_free (in->history);
1440 g_free (in->buffer);
1441 free_completions (in);
1442 g_free (in->history_name);
1444 g_free (kill_buffer);
1445 kill_buffer = NULL;
1448 void
1449 input_disable_update (WInput * in)
1451 in->disable_update++;
1454 void
1455 input_enable_update (WInput * in)
1457 in->disable_update--;
1458 update_input (in, 0);
1462 static void
1463 push_history (WInput * in, const char *text)
1465 /* input widget where urls with passwords are entered without any
1466 vfs prefix */
1467 const char *password_input_fields[] = {
1468 N_(" Link to a remote machine "),
1469 N_(" FTP to machine "),
1470 N_(" SMB link to machine ")
1472 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1474 char *t;
1475 size_t i;
1476 gboolean empty;
1478 if (text == NULL)
1479 return;
1481 #ifdef ENABLE_NLS
1482 for (i = 0; i < ELEMENTS; i++)
1483 password_input_fields[i] = _(password_input_fields[i]);
1484 #endif
1486 t = g_strstrip (g_strdup (text));
1487 empty = *t == '\0';
1488 g_free (t);
1489 t = g_strdup (empty ? "" : text);
1491 if (in->history_name != NULL)
1493 const char *p = in->history_name + 3;
1495 for (i = 0; i < ELEMENTS; i++)
1496 if (strcmp (p, password_input_fields[i]) == 0)
1497 break;
1499 strip_password (t, i >= ELEMENTS);
1502 in->history = list_append_unique (in->history, t);
1503 in->need_push = 0;
1506 /* Cleans the input line and adds the current text to the history */
1507 void
1508 new_input (WInput * in)
1510 push_history (in, in->buffer);
1511 in->need_push = 1;
1512 in->buffer[0] = '\0';
1513 in->point = 0;
1514 in->charpoint = 0;
1515 in->mark = 0;
1516 free_completions (in);
1517 update_input (in, 0);
1520 static void
1521 move_buffer_backward (WInput * in, int start, int end)
1523 int i, pos, len;
1524 int str_len = str_length (in->buffer);
1525 if (start >= str_len || end > str_len + 1)
1526 return;
1528 pos = str_offset_to_pos (in->buffer, start);
1529 len = str_offset_to_pos (in->buffer, end) - pos;
1531 for (i = pos; in->buffer[i + len - 1]; i++)
1532 in->buffer[i] = in->buffer[i + len];
1535 static cb_ret_t
1536 insert_char (WInput * in, int c_code)
1538 size_t i;
1539 int res;
1541 if (c_code == -1)
1542 return MSG_NOT_HANDLED;
1544 if (in->charpoint >= MB_LEN_MAX)
1545 return MSG_HANDLED;
1547 in->charbuf[in->charpoint] = c_code;
1548 in->charpoint++;
1550 res = str_is_valid_char (in->charbuf, in->charpoint);
1551 if (res < 0)
1553 if (res != -2)
1554 in->charpoint = 0; /* broken multibyte char, skip */
1555 return MSG_HANDLED;
1558 in->need_push = 1;
1559 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1561 /* Expand the buffer */
1562 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1563 char *narea = g_try_renew (char, in->buffer, new_length);
1564 if (narea)
1566 in->buffer = narea;
1567 in->current_max_size = new_length;
1571 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1573 /* bytes from begin */
1574 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1575 /* move chars */
1576 size_t rest_bytes = strlen (in->buffer + ins_point);
1578 for (i = rest_bytes + 1; i > 0; i--)
1579 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1581 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1582 in->point++;
1585 in->charpoint = 0;
1586 return MSG_HANDLED;
1589 static void
1590 beginning_of_line (WInput * in)
1592 in->point = 0;
1593 in->charpoint = 0;
1596 static void
1597 end_of_line (WInput * in)
1599 in->point = str_length (in->buffer);
1600 in->charpoint = 0;
1603 static void
1604 backward_char (WInput * in)
1606 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1608 if (in->point > 0)
1610 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1612 in->charpoint = 0;
1615 static void
1616 forward_char (WInput * in)
1618 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1619 if (act[0] != '\0')
1621 in->point += str_cnext_noncomb_char (&act);
1623 in->charpoint = 0;
1626 static void
1627 forward_word (WInput * in)
1629 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1631 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1633 str_cnext_char (&p);
1634 in->point++;
1636 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1638 str_cnext_char (&p);
1639 in->point++;
1643 static void
1644 backward_word (WInput * in)
1646 const char *p;
1647 const char *p_tmp;
1649 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1650 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1652 while (p != in->buffer)
1654 p_tmp = p;
1655 str_cprev_char (&p);
1656 if (!str_isspace (p) && !str_ispunct (p))
1658 p = p_tmp;
1659 break;
1661 in->point--;
1663 while (p != in->buffer)
1665 str_cprev_char (&p);
1666 if (str_isspace (p) || str_ispunct (p))
1667 break;
1669 in->point--;
1673 static void
1674 key_left (WInput * in)
1676 backward_char (in);
1679 static void
1680 key_ctrl_left (WInput * in)
1682 backward_word (in);
1685 static void
1686 key_right (WInput * in)
1688 forward_char (in);
1691 static void
1692 key_ctrl_right (WInput * in)
1694 forward_word (in);
1696 static void
1697 backward_delete (WInput * in)
1699 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1700 int start;
1702 if (in->point == 0)
1703 return;
1705 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1706 move_buffer_backward (in, start, in->point);
1707 in->charpoint = 0;
1708 in->need_push = 1;
1709 in->point = start;
1712 static void
1713 delete_char (WInput * in)
1715 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1716 int end = in->point;
1718 end += str_cnext_noncomb_char (&act);
1720 move_buffer_backward (in, in->point, end);
1721 in->charpoint = 0;
1722 in->need_push = 1;
1725 static void
1726 copy_region (WInput * in, int x_first, int x_last)
1728 int first = min (x_first, x_last);
1729 int last = max (x_first, x_last);
1731 if (last == first)
1733 /* Copy selected files to clipboard */
1734 panel_save_curent_file_to_clip_file ();
1735 return;
1738 g_free (kill_buffer);
1740 first = str_offset_to_pos (in->buffer, first);
1741 last = str_offset_to_pos (in->buffer, last);
1743 kill_buffer = g_strndup (in->buffer + first, last - first);
1744 save_text_to_clip_file (kill_buffer);
1747 static void
1748 delete_region (WInput * in, int x_first, int x_last)
1750 int first = min (x_first, x_last);
1751 int last = max (x_first, x_last);
1752 size_t len;
1754 in->point = first;
1755 if (in->mark > first)
1756 in->mark = first;
1757 last = str_offset_to_pos (in->buffer, last);
1758 first = str_offset_to_pos (in->buffer, first);
1759 len = strlen (&in->buffer[last]) + 1;
1760 memmove (&in->buffer[first], &in->buffer[last], len);
1761 in->charpoint = 0;
1762 in->need_push = 1;
1765 static void
1766 kill_word (WInput * in)
1768 int old_point = in->point;
1769 int new_point;
1771 forward_word (in);
1772 new_point = in->point;
1773 in->point = old_point;
1775 copy_region (in, old_point, new_point);
1776 delete_region (in, old_point, new_point);
1777 in->need_push = 1;
1778 in->charpoint = 0;
1779 in->charpoint = 0;
1782 static void
1783 back_kill_word (WInput * in)
1785 int old_point = in->point;
1786 int new_point;
1788 backward_word (in);
1789 new_point = in->point;
1790 in->point = old_point;
1792 copy_region (in, old_point, new_point);
1793 delete_region (in, old_point, new_point);
1794 in->need_push = 1;
1797 static void
1798 set_mark (WInput * in)
1800 in->mark = in->point;
1803 static void
1804 kill_save (WInput * in)
1806 copy_region (in, in->mark, in->point);
1809 static void
1810 kill_region (WInput * in)
1812 kill_save (in);
1813 delete_region (in, in->point, in->mark);
1816 static void
1817 clear_region (WInput * in)
1819 delete_region (in, in->point, in->mark);
1822 static void
1823 yank (WInput * in)
1825 if (kill_buffer != NULL)
1827 char *p;
1829 in->charpoint = 0;
1830 for (p = kill_buffer; *p != '\0'; p++)
1831 insert_char (in, *p);
1832 in->charpoint = 0;
1836 static void
1837 kill_line (WInput * in)
1839 int chp = str_offset_to_pos (in->buffer, in->point);
1840 g_free (kill_buffer);
1841 kill_buffer = g_strdup (&in->buffer[chp]);
1842 in->buffer[chp] = '\0';
1843 in->charpoint = 0;
1846 static void
1847 ins_from_clip (WInput * in)
1849 char *p = NULL;
1851 if (load_text_from_clip_file (&p))
1853 char *pp;
1855 for (pp = p; *pp != '\0'; pp++)
1856 insert_char (in, *pp);
1858 g_free (p);
1862 void
1863 assign_text (WInput * in, const char *text)
1865 free_completions (in);
1866 g_free (in->buffer);
1867 in->buffer = g_strdup (text); /* was in->buffer->text */
1868 in->current_max_size = strlen (in->buffer) + 1;
1869 in->point = str_length (in->buffer);
1870 in->mark = 0;
1871 in->need_push = 1;
1872 in->charpoint = 0;
1875 static void
1876 hist_prev (WInput * in)
1878 GList *prev;
1880 if (!in->history)
1881 return;
1883 if (in->need_push)
1884 push_history (in, in->buffer);
1886 prev = g_list_previous (in->history);
1887 if (prev != NULL)
1889 in->history = prev;
1890 assign_text (in, (char *) prev->data);
1891 in->need_push = 0;
1895 static void
1896 hist_next (WInput * in)
1898 if (in->need_push)
1900 push_history (in, in->buffer);
1901 assign_text (in, "");
1902 return;
1905 if (!in->history)
1906 return;
1908 if (!in->history->next)
1910 assign_text (in, "");
1911 return;
1914 in->history = g_list_next (in->history);
1915 assign_text (in, (char *) in->history->data);
1916 in->need_push = 0;
1919 static void
1920 port_region_marked_for_delete (WInput * in)
1922 in->buffer[0] = '\0';
1923 in->point = 0;
1924 in->first = 0;
1925 in->charpoint = 0;
1928 static cb_ret_t
1929 input_execute_cmd (WInput * in, unsigned long command)
1931 cb_ret_t res = MSG_HANDLED;
1933 switch (command)
1935 case CK_InputBol:
1936 beginning_of_line (in);
1937 break;
1938 case CK_InputEol:
1939 end_of_line (in);
1940 break;
1941 case CK_InputMoveLeft:
1942 key_left (in);
1943 break;
1944 case CK_InputWordLeft:
1945 key_ctrl_left (in);
1946 break;
1947 case CK_InputMoveRight:
1948 key_right (in);
1949 break;
1950 case CK_InputWordRight:
1951 key_ctrl_right (in);
1952 break;
1953 case CK_InputBackwardChar:
1954 backward_char (in);
1955 break;
1956 case CK_InputBackwardWord:
1957 backward_word (in);
1958 break;
1959 case CK_InputForwardChar:
1960 forward_char (in);
1961 break;
1962 case CK_InputForwardWord:
1963 forward_word (in);
1964 break;
1965 case CK_InputBackwardDelete:
1966 backward_delete (in);
1967 break;
1968 case CK_InputDeleteChar:
1969 delete_char (in);
1970 break;
1971 case CK_InputKillWord:
1972 kill_word (in);
1973 break;
1974 case CK_InputBackwardKillWord:
1975 back_kill_word (in);
1976 break;
1977 case CK_InputSetMark:
1978 set_mark (in);
1979 break;
1980 case CK_InputKillRegion:
1981 kill_region (in);
1982 break;
1983 case CK_InputClearLine:
1984 clear_region (in);
1985 break;
1986 case CK_InputKillSave:
1987 kill_save (in);
1988 break;
1989 case CK_InputYank:
1990 yank (in);
1991 break;
1992 case CK_InputPaste:
1993 ins_from_clip (in);
1994 break;
1995 case CK_InputKillLine:
1996 kill_line (in);
1997 break;
1998 case CK_InputHistoryPrev:
1999 hist_prev (in);
2000 break;
2001 case CK_InputHistoryNext:
2002 hist_next (in);
2003 break;
2004 case CK_InputHistoryShow:
2005 do_show_hist (in);
2006 break;
2007 case CK_InputComplete:
2008 complete (in);
2009 break;
2010 default:
2011 res = MSG_NOT_HANDLED;
2014 return res;
2017 /* This function is a test for a special input key used in complete.c */
2018 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2019 and 2 if it is a complete key */
2021 is_in_input_map (WInput * in, int key)
2023 size_t i;
2024 for (i = 0; input_map[i].key != 0; i++)
2025 if (key == input_map[i].key)
2027 input_execute_cmd (in, input_map[i].command);
2028 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2030 return 0;
2033 cb_ret_t
2034 handle_char (WInput * in, int key)
2036 cb_ret_t v;
2037 int i;
2039 v = MSG_NOT_HANDLED;
2041 if (quote)
2043 free_completions (in);
2044 v = insert_char (in, key);
2045 update_input (in, 1);
2046 quote = 0;
2047 return v;
2049 for (i = 0; input_map[i].key; i++)
2051 if (key == input_map[i].key)
2053 if (input_map[i].command != CK_InputComplete)
2054 free_completions (in);
2055 input_execute_cmd (in, input_map[i].command);
2056 update_input (in, 1);
2057 v = MSG_HANDLED;
2058 break;
2061 if (input_map[i].command == 0)
2063 if (key > 255)
2064 return MSG_NOT_HANDLED;
2065 if (in->first)
2066 port_region_marked_for_delete (in);
2067 free_completions (in);
2068 v = insert_char (in, key);
2070 update_input (in, 1);
2071 return v;
2074 /* Inserts text in input line */
2075 void
2076 stuff (WInput * in, const char *text, int insert_extra_space)
2078 input_disable_update (in);
2079 while (*text != '\0')
2080 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2081 if (insert_extra_space)
2082 handle_char (in, ' ');
2083 input_enable_update (in);
2084 update_input (in, 1);
2087 void
2088 input_set_point (WInput * in, int pos)
2090 int max_pos = str_length (in->buffer);
2092 if (pos > max_pos)
2093 pos = max_pos;
2094 if (pos != in->point)
2095 free_completions (in);
2096 in->point = pos;
2097 in->charpoint = 0;
2098 update_input (in, 1);
2101 cb_ret_t
2102 input_callback (Widget * w, widget_msg_t msg, int parm)
2104 WInput *in = (WInput *) w;
2105 cb_ret_t v;
2107 switch (msg)
2109 case WIDGET_KEY:
2110 if (parm == XCTRL ('q'))
2112 quote = 1;
2113 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2114 quote = 0;
2115 return v;
2118 /* Keys we want others to handle */
2119 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2120 || parm == KEY_F (10) || parm == '\n')
2121 return MSG_NOT_HANDLED;
2123 /* When pasting multiline text, insert literal Enter */
2124 if ((parm & ~KEY_M_MASK) == '\n')
2126 quote = 1;
2127 v = handle_char (in, '\n');
2128 quote = 0;
2129 return v;
2132 return handle_char (in, parm);
2134 case WIDGET_COMMAND:
2135 return input_execute_cmd (in, parm);
2137 case WIDGET_FOCUS:
2138 case WIDGET_UNFOCUS:
2139 case WIDGET_DRAW:
2140 update_input (in, 0);
2141 return MSG_HANDLED;
2143 case WIDGET_CURSOR:
2144 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2145 - in->term_first_shown);
2146 return MSG_HANDLED;
2148 case WIDGET_DESTROY:
2149 input_destroy (in);
2150 return MSG_HANDLED;
2152 default:
2153 return default_proc (msg, parm);
2157 static int
2158 input_event (Gpm_Event * event, void *data)
2160 WInput *in = data;
2162 if (event->type & (GPM_DOWN | GPM_DRAG))
2164 dlg_select_widget (in);
2166 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2167 && should_show_history_button (in))
2169 do_show_hist (in);
2171 else
2173 in->point = str_length (in->buffer);
2174 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2175 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2177 update_input (in, 1);
2179 return MOU_NORMAL;
2182 WInput *
2183 input_new (int y, int x, int color, int width, const char *def_text,
2184 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2186 WInput *in = g_new (WInput, 1);
2187 size_t initial_buffer_len;
2189 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2191 /* history setup */
2192 in->history_name = NULL;
2193 in->history = NULL;
2194 if ((histname != NULL) && (*histname != '\0'))
2196 in->history_name = g_strdup (histname);
2197 in->history = history_get (histname);
2200 if (def_text == NULL)
2201 def_text = "";
2202 else if (def_text == INPUT_LAST_TEXT)
2204 if ((in->history != NULL) && (in->history->data != NULL))
2205 def_text = (char *) in->history->data;
2206 else
2207 def_text = "";
2210 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2211 in->widget.options |= W_IS_INPUT;
2212 in->completions = NULL;
2213 in->completion_flags = completion_flags;
2214 in->current_max_size = initial_buffer_len;
2215 in->buffer = g_new (char, initial_buffer_len);
2216 in->color = color;
2217 in->field_width = width;
2218 in->first = 1;
2219 in->term_first_shown = 0;
2220 in->disable_update = 0;
2221 in->mark = 0;
2222 in->need_push = 1;
2223 in->is_password = 0;
2225 strcpy (in->buffer, def_text);
2226 in->point = str_length (in->buffer);
2227 in->charpoint = 0;
2229 return in;
2233 /* Listbox widget */
2235 /* Should draw the scrollbar, but currently draws only
2236 * indications that there is more information
2239 static void
2240 listbox_entry_free (void *data)
2242 WLEntry *e = data;
2243 g_free (e->text);
2244 g_free (e);
2247 static void
2248 listbox_drawscroll (WListbox * l)
2250 const int max_line = l->widget.lines - 1;
2251 int line = 0;
2252 int i;
2254 /* Are we at the top? */
2255 widget_move (&l->widget, 0, l->widget.cols);
2256 if (l->top == 0)
2257 tty_print_one_vline (TRUE);
2258 else
2259 tty_print_char ('^');
2261 /* Are we at the bottom? */
2262 widget_move (&l->widget, max_line, l->widget.cols);
2263 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2264 tty_print_one_vline (TRUE);
2265 else
2266 tty_print_char ('v');
2268 /* Now draw the nice relative pointer */
2269 if (l->count != 0)
2270 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2272 for (i = 1; i < max_line; i++)
2274 widget_move (&l->widget, i, l->widget.cols);
2275 if (i != line)
2276 tty_print_one_vline (TRUE);
2277 else
2278 tty_print_char ('*');
2282 static void
2283 listbox_draw (WListbox * l, gboolean focused)
2285 const Dlg_head *h = l->widget.parent;
2286 const int normalc = DLG_NORMALC (h);
2287 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2289 GList *le;
2290 int pos;
2291 int i;
2292 int sel_line = -1;
2294 le = g_list_nth (l->list, l->top);
2295 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2296 pos = (le == NULL) ? 0 : l->top;
2298 for (i = 0; i < l->widget.lines; i++)
2300 const char *text;
2302 /* Display the entry */
2303 if (pos == l->pos && sel_line == -1)
2305 sel_line = i;
2306 tty_setcolor (selc);
2308 else
2309 tty_setcolor (normalc);
2311 widget_move (&l->widget, i, 1);
2313 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2314 text = "";
2315 else
2317 WLEntry *e = (WLEntry *) le->data;
2318 text = e->text;
2319 le = g_list_next (le);
2320 pos++;
2323 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2326 l->cursor_y = sel_line;
2328 if (l->scrollbar && (l->count > l->widget.lines))
2330 tty_setcolor (normalc);
2331 listbox_drawscroll (l);
2335 static int
2336 listbox_check_hotkey (WListbox * l, int key)
2338 int i;
2339 GList *le;
2341 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2343 WLEntry *e = (WLEntry *) le->data;
2345 if (e->hotkey == key)
2346 return i;
2349 return (-1);
2352 /* Selects the last entry and scrolls the list to the bottom */
2353 void
2354 listbox_select_last (WListbox * l)
2356 l->pos = l->count - 1;
2357 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2360 /* Selects the first entry and scrolls the list to the top */
2361 void
2362 listbox_select_first (WListbox * l)
2364 l->pos = l->top = 0;
2367 void
2368 listbox_set_list (WListbox * l, GList * list)
2370 listbox_remove_list (l);
2372 if (l != NULL)
2374 l->list = list;
2375 l->top = l->pos = 0;
2376 l->count = g_list_length (list);
2380 void
2381 listbox_remove_list (WListbox * l)
2383 if ((l != NULL) && (l->count != 0))
2385 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2386 g_list_free (l->list);
2387 l->list = NULL;
2388 l->count = l->pos = l->top = 0;
2392 void
2393 listbox_remove_current (WListbox * l)
2395 if ((l != NULL) && (l->count != 0))
2397 GList *current;
2399 current = g_list_nth (l->list, l->pos);
2400 l->list = g_list_remove_link (l->list, current);
2401 listbox_entry_free ((WLEntry *) current->data);
2402 g_list_free_1 (current);
2403 l->count--;
2405 if (l->count == 0)
2406 l->top = l->pos = 0;
2407 else if (l->pos >= l->count)
2408 l->pos = l->count - 1;
2412 void
2413 listbox_select_entry (WListbox * l, int dest)
2415 GList *le;
2416 int pos;
2417 gboolean top_seen = FALSE;
2419 if (dest < 0)
2420 return;
2422 /* Special case */
2423 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2425 if (pos == l->top)
2426 top_seen = TRUE;
2428 if (pos == dest)
2430 l->pos = dest;
2431 if (!top_seen)
2432 l->top = l->pos;
2433 else if (l->pos - l->top >= l->widget.lines)
2434 l->top = l->pos - l->widget.lines + 1;
2435 return;
2439 /* If we are unable to find it, set decent values */
2440 l->pos = l->top = 0;
2443 /* Selects from base the pos element */
2444 static int
2445 listbox_select_pos (WListbox * l, int base, int pos)
2447 int last = l->count - 1;
2449 base += pos;
2450 if (base >= last)
2451 base = last;
2453 return base;
2456 static void
2457 listbox_fwd (WListbox * l)
2459 if (l->pos + 1 >= l->count)
2460 listbox_select_first (l);
2461 else
2462 listbox_select_entry (l, l->pos + 1);
2465 static void
2466 listbox_back (WListbox * l)
2468 if (l->pos <= 0)
2469 listbox_select_last (l);
2470 else
2471 listbox_select_entry (l, l->pos - 1);
2474 /* Return MSG_HANDLED if we want a redraw */
2475 static cb_ret_t
2476 listbox_key (WListbox * l, int key)
2478 int i;
2480 cb_ret_t j = MSG_NOT_HANDLED;
2482 if (l->list == NULL)
2483 return MSG_NOT_HANDLED;
2485 /* focus on listbox item N by '0'..'9' keys */
2486 if (key >= '0' && key <= '9')
2488 int oldpos = l->pos;
2489 listbox_select_entry (l, key - '0');
2491 /* need scroll to item? */
2492 if (abs (oldpos - l->pos) > l->widget.lines)
2493 l->top = l->pos;
2495 return MSG_HANDLED;
2498 switch (key)
2500 case KEY_HOME:
2501 case KEY_A1:
2502 case ALT ('<'):
2503 listbox_select_first (l);
2504 return MSG_HANDLED;
2506 case KEY_END:
2507 case KEY_C1:
2508 case ALT ('>'):
2509 listbox_select_last (l);
2510 return MSG_HANDLED;
2512 case XCTRL ('p'):
2513 case KEY_UP:
2514 listbox_back (l);
2515 return MSG_HANDLED;
2517 case XCTRL ('n'):
2518 case KEY_DOWN:
2519 listbox_fwd (l);
2520 return MSG_HANDLED;
2522 case KEY_NPAGE:
2523 case XCTRL ('v'):
2524 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2526 listbox_fwd (l);
2527 j = MSG_HANDLED;
2529 break;
2531 case KEY_PPAGE:
2532 case ALT ('v'):
2533 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2535 listbox_back (l);
2536 j = MSG_HANDLED;
2538 break;
2540 case KEY_DC:
2541 case 'd':
2542 if (l->deletable)
2544 gboolean is_last = (l->pos + 1 >= l->count);
2545 gboolean is_more = (l->top + l->widget.lines >= l->count);
2547 listbox_remove_current (l);
2548 if ((l->top > 0) && (is_last || is_more))
2549 l->top--;
2551 return MSG_HANDLED;
2553 case (KEY_M_SHIFT | KEY_DC):
2554 case 'D':
2555 if (l->deletable && confirm_history_cleanup
2556 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2557 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2558 _("Do you want clean this history?"),
2559 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2561 listbox_remove_list (l);
2562 j = MSG_HANDLED;
2564 break;
2566 default:
2567 break;
2570 return j;
2573 static inline void
2574 listbox_destroy (WListbox * l)
2576 /* don't delete list in modifable listbox */
2577 if (!l->deletable)
2578 listbox_remove_list (l);
2581 static cb_ret_t
2582 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2584 WListbox *l = (WListbox *) w;
2585 Dlg_head *h = l->widget.parent;
2586 cb_ret_t ret_code;
2588 switch (msg)
2590 case WIDGET_INIT:
2591 return MSG_HANDLED;
2593 case WIDGET_HOTKEY:
2595 int pos, action;
2597 pos = listbox_check_hotkey (l, parm);
2598 if (pos < 0)
2599 return MSG_NOT_HANDLED;
2601 listbox_select_entry (l, pos);
2602 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2604 if (l->cback != NULL)
2605 action = l->cback (l);
2606 else
2607 action = LISTBOX_DONE;
2609 if (action == LISTBOX_DONE)
2611 h->ret_value = B_ENTER;
2612 dlg_stop (h);
2615 return MSG_HANDLED;
2618 case WIDGET_KEY:
2619 ret_code = listbox_key (l, parm);
2620 if (ret_code != MSG_NOT_HANDLED)
2622 listbox_draw (l, TRUE);
2623 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2625 return ret_code;
2627 case WIDGET_CURSOR:
2628 widget_move (&l->widget, l->cursor_y, 0);
2629 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2630 return MSG_HANDLED;
2632 case WIDGET_FOCUS:
2633 case WIDGET_UNFOCUS:
2634 case WIDGET_DRAW:
2635 listbox_draw (l, msg != WIDGET_UNFOCUS);
2636 return MSG_HANDLED;
2638 case WIDGET_DESTROY:
2639 listbox_destroy (l);
2640 return MSG_HANDLED;
2642 case WIDGET_RESIZED:
2643 return MSG_HANDLED;
2645 default:
2646 return default_proc (msg, parm);
2650 static int
2651 listbox_event (Gpm_Event * event, void *data)
2653 WListbox *l = data;
2654 int i;
2656 Dlg_head *h = l->widget.parent;
2658 /* Single click */
2659 if (event->type & GPM_DOWN)
2660 dlg_select_widget (l);
2662 if (l->list == NULL)
2663 return MOU_NORMAL;
2665 if (event->type & (GPM_DOWN | GPM_DRAG))
2667 int ret = MOU_REPEAT;
2669 if (event->x < 0 || event->x > l->widget.cols)
2670 return ret;
2672 if (event->y < 1)
2673 for (i = -event->y; i >= 0; i--)
2674 listbox_back (l);
2675 else if (event->y > l->widget.lines)
2676 for (i = event->y - l->widget.lines; i > 0; i--)
2677 listbox_fwd (l);
2678 else if (event->buttons & GPM_B_UP)
2680 listbox_back (l);
2681 ret = MOU_NORMAL;
2683 else if (event->buttons & GPM_B_DOWN)
2685 listbox_fwd (l);
2686 ret = MOU_NORMAL;
2688 else
2689 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2691 /* We need to refresh ourselves since the dialog manager doesn't */
2692 /* know about this event */
2693 listbox_draw (l, TRUE);
2694 return ret;
2697 /* Double click */
2698 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2700 int action;
2702 if (event->x < 0 || event->x >= l->widget.cols
2703 || event->y < 1 || event->y > l->widget.lines)
2704 return MOU_NORMAL;
2706 dlg_select_widget (l);
2707 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2709 if (l->cback != NULL)
2710 action = l->cback (l);
2711 else
2712 action = LISTBOX_DONE;
2714 if (action == LISTBOX_DONE)
2716 h->ret_value = B_ENTER;
2717 dlg_stop (h);
2718 return MOU_NORMAL;
2721 return MOU_NORMAL;
2724 WListbox *
2725 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2727 WListbox *l = g_new (WListbox, 1);
2729 if (height <= 0)
2730 height = 1;
2732 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2734 l->list = NULL;
2735 l->top = l->pos = 0;
2736 l->count = 0;
2737 l->deletable = deletable;
2738 l->cback = callback;
2739 l->allow_duplicates = TRUE;
2740 l->scrollbar = !tty_is_slow ();
2741 widget_want_hotkey (l->widget, 1);
2743 return l;
2746 static int
2747 listbox_entry_cmp (const void *a, const void *b)
2749 const WLEntry *ea = (const WLEntry *) a;
2750 const WLEntry *eb = (const WLEntry *) b;
2752 return strcmp (ea->text, eb->text);
2755 /* Listbox item adding function */
2756 static inline void
2757 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2759 switch (pos)
2761 case LISTBOX_APPEND_AT_END:
2762 l->list = g_list_append (l->list, e);
2763 break;
2765 case LISTBOX_APPEND_BEFORE:
2766 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2767 if (l->pos > 0)
2768 l->pos--;
2769 break;
2771 case LISTBOX_APPEND_AFTER:
2772 l->list = g_list_insert (l->list, e, l->pos + 1);
2773 break;
2775 case LISTBOX_APPEND_SORTED:
2776 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2777 break;
2779 default:
2780 return;
2783 l->count++;
2786 char *
2787 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2789 WLEntry *entry;
2791 if (l == NULL)
2792 return NULL;
2794 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2795 return NULL;
2797 entry = g_new (WLEntry, 1);
2798 entry->text = g_strdup (text);
2799 entry->data = data;
2800 entry->hotkey = hotkey;
2802 listbox_append_item (l, entry, pos);
2804 return entry->text;
2808 listbox_search_text (WListbox * l, const char *text)
2810 if (l != NULL)
2812 int i;
2813 GList *le;
2815 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2817 WLEntry *e = (WLEntry *) le->data;
2819 if (strcmp (e->text, text) == 0)
2820 return i;
2824 return (-1);
2827 /* Returns the current string text as well as the associated extra data */
2828 void
2829 listbox_get_current (WListbox * l, char **string, void **extra)
2831 WLEntry *e = NULL;
2832 gboolean ok;
2834 if (l != NULL)
2835 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2837 ok = (e != NULL);
2839 if (string != NULL)
2840 *string = ok ? e->text : NULL;
2842 if (extra != NULL)
2843 *extra = ok ? e->data : NULL;
2847 /* ButtonBar widget */
2849 /* returns TRUE if a function has been called, FALSE otherwise. */
2850 static gboolean
2851 buttonbar_call (WButtonBar * bb, int i)
2853 cb_ret_t ret = MSG_NOT_HANDLED;
2855 if (bb != NULL)
2856 ret = bb->widget.parent->callback (bb->widget.parent,
2857 (Widget *) bb, DLG_ACTION,
2858 bb->labels[i].command, bb->labels[i].receiver);
2859 return ret;
2862 /* calculate width of one button, width is never lesser than 7 */
2863 static int
2864 buttonbat_get_button_width (void)
2866 int result = COLS / BUTTONBAR_LABELS_NUM;
2867 return (result >= 7) ? result : 7;
2870 static cb_ret_t
2871 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
2873 WButtonBar *bb = (WButtonBar *) w;
2874 int i;
2875 const char *text;
2877 switch (msg)
2879 case WIDGET_FOCUS:
2880 return MSG_NOT_HANDLED;
2882 case WIDGET_HOTKEY:
2883 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2884 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2885 return MSG_HANDLED;
2886 return MSG_NOT_HANDLED;
2888 case WIDGET_DRAW:
2889 if (bb->visible)
2891 int offset = 0;
2892 int count_free_positions;
2894 widget_move (&bb->widget, 0, 0);
2895 tty_setcolor (DEFAULT_COLOR);
2896 bb->btn_width = buttonbat_get_button_width ();
2897 tty_printf ("%-*s", bb->widget.cols, "");
2898 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2900 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++)
2902 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2903 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2904 tty_printf ("%2d", i + 1);
2905 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2906 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2907 tty_print_string (str_fit_to_term (text,
2908 bb->btn_width - 2 + (int) (offset <
2909 count_free_positions),
2910 J_LEFT_FIT));
2912 if (count_free_positions != 0 && offset < count_free_positions)
2913 offset++;
2916 return MSG_HANDLED;
2918 case WIDGET_DESTROY:
2919 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2920 g_free (bb->labels[i].text);
2921 return MSG_HANDLED;
2923 default:
2924 return default_proc (msg, parm);
2928 static int
2929 buttonbar_event (Gpm_Event * event, void *data)
2931 WButtonBar *bb = data;
2932 int button;
2934 if (!(event->type & GPM_UP))
2935 return MOU_NORMAL;
2936 if (event->y == 2)
2937 return MOU_NORMAL;
2938 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2939 if (button < BUTTONBAR_LABELS_NUM)
2940 buttonbar_call (bb, button);
2941 return MOU_NORMAL;
2944 WButtonBar *
2945 buttonbar_new (gboolean visible)
2947 WButtonBar *bb;
2949 bb = g_new0 (WButtonBar, 1);
2951 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
2952 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2953 bb->visible = visible;
2954 widget_want_hotkey (bb->widget, 1);
2955 widget_want_cursor (bb->widget, 0);
2956 bb->btn_width = buttonbat_get_button_width ();
2958 return bb;
2961 static void
2962 set_label_text (WButtonBar * bb, int lc_index, const char *text)
2964 g_free (bb->labels[lc_index - 1].text);
2965 bb->labels[lc_index - 1].text = g_strdup (text);
2968 /* Find ButtonBar widget in the dialog */
2969 WButtonBar *
2970 find_buttonbar (const Dlg_head * h)
2972 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2975 void
2976 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
2977 const struct global_keymap_t *keymap, const Widget * receiver)
2979 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
2981 unsigned long command = CK_Ignore_Key;
2983 if (keymap != NULL)
2984 command = lookup_keymap_command (keymap, KEY_F (idx));
2986 if ((text == NULL) || (text[0] == '\0'))
2987 set_label_text (bb, idx, "");
2988 else
2989 set_label_text (bb, idx, text);
2991 bb->labels[idx - 1].command = command;
2992 bb->labels[idx - 1].receiver = (Widget *) receiver;
2996 void
2997 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
2999 bb->visible = visible;
3002 void
3003 buttonbar_redraw (WButtonBar * bb)
3005 if (bb != NULL)
3006 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3009 static cb_ret_t
3010 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3012 WGroupbox *g = (WGroupbox *) w;
3014 switch (msg)
3016 case WIDGET_INIT:
3017 return MSG_HANDLED;
3019 case WIDGET_FOCUS:
3020 return MSG_NOT_HANDLED;
3022 case WIDGET_DRAW:
3023 tty_setcolor (COLOR_NORMAL);
3024 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
3025 g->widget.x - g->widget.parent->x, g->widget.lines, g->widget.cols, TRUE);
3027 tty_setcolor (COLOR_HOT_NORMAL);
3028 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
3029 g->widget.x - g->widget.parent->x + 1);
3030 tty_print_string (g->title);
3031 return MSG_HANDLED;
3033 case WIDGET_DESTROY:
3034 g_free (g->title);
3035 return MSG_HANDLED;
3037 default:
3038 return default_proc (msg, parm);
3042 WGroupbox *
3043 groupbox_new (int y, int x, int height, int width, const char *title)
3045 WGroupbox *g = g_new (WGroupbox, 1);
3047 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3049 g->widget.options &= ~W_WANT_CURSOR;
3050 widget_want_hotkey (g->widget, 0);
3052 /* Strip existing spaces, add one space before and after the title */
3053 if (title)
3055 char *t;
3056 t = g_strstrip (g_strdup (title));
3057 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3058 g_free (t);
3061 return g;