Merge branch '2161_del_clear_dialog_field'
[midnight-commander/osp/pkrayzel.git] / src / widget.c
blob23de4f25c9cf5538408975a96a36524a99177789
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 */
63 #include "setup.h" /* num_history_items_recorded */
65 static void
66 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
68 Dlg_head *h = w->parent;
70 tty_setcolor (hotkey
71 ? (focused
72 ? DLG_HOT_FOCUSC (h)
73 : DLG_HOT_NORMALC (h)) : (focused ? DLG_FOCUSC (h) : DLG_NORMALC (h)));
76 struct hotkey_t
77 parse_hotkey (const char *text)
79 struct hotkey_t result;
80 const char *cp, *p;
82 /* search for '&', that is not on the of text */
83 cp = strchr (text, '&');
84 if (cp != NULL && cp[1] != '\0')
86 result.start = g_strndup (text, cp - text);
88 /* skip '&' */
89 cp++;
90 p = str_cget_next_char (cp);
91 result.hotkey = g_strndup (cp, p - cp);
93 cp = p;
94 result.end = g_strdup (cp);
96 else
98 result.start = g_strdup (text);
99 result.hotkey = NULL;
100 result.end = NULL;
103 return result;
106 void
107 release_hotkey (const struct hotkey_t hotkey)
109 g_free (hotkey.start);
110 g_free (hotkey.hotkey);
111 g_free (hotkey.end);
115 hotkey_width (const struct hotkey_t hotkey)
117 int result;
119 result = str_term_width1 (hotkey.start);
120 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
121 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
122 return result;
125 static void
126 draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
128 widget_selectcolor (w, focused, FALSE);
129 tty_print_string (hotkey.start);
131 if (hotkey.hotkey != NULL)
133 widget_selectcolor (w, focused, TRUE);
134 tty_print_string (hotkey.hotkey);
135 widget_selectcolor (w, focused, FALSE);
138 if (hotkey.end != NULL)
139 tty_print_string (hotkey.end);
143 /* Default callback for widgets */
144 cb_ret_t
145 default_proc (widget_msg_t msg, int parm)
147 (void) parm;
149 switch (msg)
151 case WIDGET_INIT:
152 case WIDGET_FOCUS:
153 case WIDGET_UNFOCUS:
154 case WIDGET_DRAW:
155 case WIDGET_DESTROY:
156 case WIDGET_CURSOR:
157 case WIDGET_IDLE:
158 return MSG_HANDLED;
160 default:
161 return MSG_NOT_HANDLED;
165 static int button_event (Gpm_Event * event, void *);
167 int quote = 0;
169 static cb_ret_t
170 button_callback (Widget * w, widget_msg_t msg, int parm)
172 WButton *b = (WButton *) w;
173 int stop = 0;
174 int off = 0;
175 Dlg_head *h = b->widget.parent;
177 switch (msg)
179 case WIDGET_HOTKEY:
181 * Don't let the default button steal Enter from the current
182 * button. This is a workaround for the flawed event model
183 * when hotkeys are sent to all widgets before the key is
184 * handled by the current widget.
186 if (parm == '\n' && h->current == &b->widget)
188 button_callback (w, WIDGET_KEY, ' ');
189 return MSG_HANDLED;
192 if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
194 button_callback (w, WIDGET_KEY, ' ');
195 return MSG_HANDLED;
198 if (b->text.hotkey != NULL)
200 if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
202 button_callback (w, WIDGET_KEY, ' ');
203 return MSG_HANDLED;
206 return MSG_NOT_HANDLED;
208 case WIDGET_KEY:
209 if (parm != ' ' && parm != '\n')
210 return MSG_NOT_HANDLED;
212 if (b->callback)
213 stop = (*b->callback) (b->action);
214 if (!b->callback || stop)
216 h->ret_value = b->action;
217 dlg_stop (h);
219 return MSG_HANDLED;
221 case WIDGET_CURSOR:
222 switch (b->flags)
224 case DEFPUSH_BUTTON:
225 off = 3;
226 break;
227 case NORMAL_BUTTON:
228 off = 2;
229 break;
230 case NARROW_BUTTON:
231 off = 1;
232 break;
233 case HIDDEN_BUTTON:
234 default:
235 off = 0;
236 break;
238 widget_move (&b->widget, 0, b->hotpos + off);
239 return MSG_HANDLED;
241 case WIDGET_UNFOCUS:
242 case WIDGET_FOCUS:
243 case WIDGET_DRAW:
244 if (msg == WIDGET_UNFOCUS)
245 b->selected = 0;
246 else if (msg == WIDGET_FOCUS)
247 b->selected = 1;
249 widget_selectcolor (w, b->selected, FALSE);
250 widget_move (w, 0, 0);
252 switch (b->flags)
254 case DEFPUSH_BUTTON:
255 tty_print_string ("[< ");
256 break;
257 case NORMAL_BUTTON:
258 tty_print_string ("[ ");
259 break;
260 case NARROW_BUTTON:
261 tty_print_string ("[");
262 break;
263 case HIDDEN_BUTTON:
264 default:
265 return MSG_HANDLED;
268 draw_hotkey (w, b->text, b->selected);
270 switch (b->flags)
272 case DEFPUSH_BUTTON:
273 tty_print_string (" >]");
274 break;
275 case NORMAL_BUTTON:
276 tty_print_string (" ]");
277 break;
278 case NARROW_BUTTON:
279 tty_print_string ("]");
280 break;
282 return MSG_HANDLED;
284 case WIDGET_DESTROY:
285 release_hotkey (b->text);
286 return MSG_HANDLED;
288 default:
289 return default_proc (msg, parm);
293 static int
294 button_event (Gpm_Event * event, void *data)
296 WButton *b = data;
298 if (event->type & (GPM_DOWN | GPM_UP))
300 Dlg_head *h = b->widget.parent;
301 dlg_select_widget (b);
302 if (event->type & GPM_UP)
304 button_callback ((Widget *) data, WIDGET_KEY, ' ');
305 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
306 return MOU_NORMAL;
309 return MOU_NORMAL;
313 button_get_len (const WButton * b)
315 int ret = hotkey_width (b->text);
316 switch (b->flags)
318 case DEFPUSH_BUTTON:
319 ret += 6;
320 break;
321 case NORMAL_BUTTON:
322 ret += 4;
323 break;
324 case NARROW_BUTTON:
325 ret += 2;
326 break;
327 case HIDDEN_BUTTON:
328 default:
329 return 0;
331 return ret;
334 WButton *
335 button_new (int y, int x, int action, int flags, const char *text, bcback callback)
337 WButton *b = g_new (WButton, 1);
339 b->action = action;
340 b->flags = flags;
341 b->text = parse_hotkey (text);
343 init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
345 b->selected = 0;
346 b->callback = callback;
347 widget_want_hotkey (b->widget, 1);
348 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
350 return b;
353 const char *
354 button_get_text (const WButton * b)
356 if (b->text.hotkey != NULL)
357 return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
358 else
359 return g_strdup (b->text.start);
362 void
363 button_set_text (WButton * b, const char *text)
365 release_hotkey (b->text);
366 b->text = parse_hotkey (text);
367 b->widget.cols = button_get_len (b);
368 dlg_redraw (b->widget.parent);
372 /* Radio button widget */
373 static int radio_event (Gpm_Event * event, void *);
375 static cb_ret_t
376 radio_callback (Widget * w, widget_msg_t msg, int parm)
378 WRadio *r = (WRadio *) w;
379 int i;
380 Dlg_head *h = r->widget.parent;
382 switch (msg)
384 case WIDGET_HOTKEY:
386 int lp = g_ascii_tolower ((gchar) parm);
388 for (i = 0; i < r->count; i++)
390 if (r->texts[i].hotkey != NULL)
392 int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
394 if (c != lp)
395 continue;
396 r->pos = i;
398 /* Take action */
399 radio_callback (w, WIDGET_KEY, ' ');
400 return MSG_HANDLED;
404 return MSG_NOT_HANDLED;
406 case WIDGET_KEY:
407 switch (parm)
409 case ' ':
410 r->sel = r->pos;
411 h->callback (h, w, DLG_ACTION, 0, NULL);
412 radio_callback (w, WIDGET_FOCUS, ' ');
413 return MSG_HANDLED;
415 case KEY_UP:
416 case KEY_LEFT:
417 if (r->pos > 0)
419 r->pos--;
420 return MSG_HANDLED;
422 return MSG_NOT_HANDLED;
424 case KEY_DOWN:
425 case KEY_RIGHT:
426 if (r->count - 1 > r->pos)
428 r->pos++;
429 return MSG_HANDLED;
432 return MSG_NOT_HANDLED;
434 case WIDGET_CURSOR:
435 h->callback (h, w, DLG_ACTION, 0, NULL);
436 radio_callback (w, WIDGET_FOCUS, ' ');
437 widget_move (&r->widget, r->pos, 1);
438 return MSG_HANDLED;
440 case WIDGET_UNFOCUS:
441 case WIDGET_FOCUS:
442 case WIDGET_DRAW:
443 for (i = 0; i < r->count; i++)
445 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
446 widget_selectcolor (w, focused, FALSE);
447 widget_move (&r->widget, i, 0);
448 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
449 draw_hotkey (w, r->texts[i], focused);
451 return MSG_HANDLED;
453 case WIDGET_DESTROY:
454 for (i = 0; i < r->count; i++)
456 release_hotkey (r->texts[i]);
458 g_free (r->texts);
459 return MSG_HANDLED;
461 default:
462 return default_proc (msg, parm);
466 static int
467 radio_event (Gpm_Event * event, void *data)
469 WRadio *r = data;
470 Widget *w = data;
472 if (event->type & (GPM_DOWN | GPM_UP))
474 Dlg_head *h = r->widget.parent;
476 r->pos = event->y - 1;
477 dlg_select_widget (r);
478 if (event->type & GPM_UP)
480 radio_callback (w, WIDGET_KEY, ' ');
481 radio_callback (w, WIDGET_FOCUS, 0);
482 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
483 return MOU_NORMAL;
486 return MOU_NORMAL;
489 WRadio *
490 radio_new (int y, int x, int count, const char **texts)
492 WRadio *result = g_new (WRadio, 1);
493 int i, max, m;
495 /* Compute the longest string */
496 result->texts = g_new (struct hotkey_t, count);
498 max = 0;
499 for (i = 0; i < count; i++)
501 result->texts[i] = parse_hotkey (texts[i]);
502 m = hotkey_width (result->texts[i]);
503 if (m > max)
504 max = m;
507 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
508 result->state = 1;
509 result->pos = 0;
510 result->sel = 0;
511 result->count = count;
512 widget_want_hotkey (result->widget, 1);
514 return result;
518 /* Checkbutton widget */
520 static int check_event (Gpm_Event * event, void *);
522 static cb_ret_t
523 check_callback (Widget * w, widget_msg_t msg, int parm)
525 WCheck *c = (WCheck *) w;
526 Dlg_head *h = c->widget.parent;
528 switch (msg)
530 case WIDGET_HOTKEY:
531 if (c->text.hotkey != NULL)
533 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
536 check_callback (w, WIDGET_KEY, ' '); /* make action */
537 return MSG_HANDLED;
540 return MSG_NOT_HANDLED;
542 case WIDGET_KEY:
543 if (parm != ' ')
544 return MSG_NOT_HANDLED;
545 c->state ^= C_BOOL;
546 c->state ^= C_CHANGE;
547 h->callback (h, w, DLG_ACTION, 0, NULL);
548 check_callback (w, WIDGET_FOCUS, ' ');
549 return MSG_HANDLED;
551 case WIDGET_CURSOR:
552 widget_move (&c->widget, 0, 1);
553 return MSG_HANDLED;
555 case WIDGET_FOCUS:
556 case WIDGET_UNFOCUS:
557 case WIDGET_DRAW:
558 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
559 widget_move (&c->widget, 0, 0);
560 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
561 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
562 return MSG_HANDLED;
564 case WIDGET_DESTROY:
565 release_hotkey (c->text);
566 return MSG_HANDLED;
568 default:
569 return default_proc (msg, parm);
573 static int
574 check_event (Gpm_Event * event, void *data)
576 WCheck *c = data;
577 Widget *w = data;
579 if (event->type & (GPM_DOWN | GPM_UP))
581 Dlg_head *h = c->widget.parent;
583 dlg_select_widget (c);
584 if (event->type & GPM_UP)
586 check_callback (w, WIDGET_KEY, ' ');
587 check_callback (w, WIDGET_FOCUS, 0);
588 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
589 return MOU_NORMAL;
592 return MOU_NORMAL;
595 WCheck *
596 check_new (int y, int x, int state, const char *text)
598 WCheck *c = g_new (WCheck, 1);
600 c->text = parse_hotkey (text);
602 init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
603 c->state = state ? C_BOOL : 0;
604 widget_want_hotkey (c->widget, 1);
606 return c;
609 static gboolean
610 save_text_to_clip_file (const char *text)
612 int file;
613 char *fname = NULL;
614 ssize_t ret;
615 size_t str_len;
617 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
618 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
619 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
620 g_free (fname);
622 if (file == -1)
623 return FALSE;
625 str_len = strlen (text);
626 ret = mc_write (file, (char *) text, str_len);
627 mc_close (file);
628 return ret == (ssize_t) str_len;
631 static gboolean
632 load_text_from_clip_file (char **text)
634 char buf[BUF_LARGE];
635 FILE *f;
636 char *fname = NULL;
637 gboolean first = TRUE;
639 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
640 f = fopen (fname, "r");
641 g_free (fname);
643 if (f == NULL)
644 return FALSE;
646 *text = NULL;
648 while (fgets (buf, sizeof (buf), f))
650 size_t len;
652 len = strlen (buf);
653 if (len > 0)
655 if (buf[len - 1] == '\n')
656 buf[len - 1] = '\0';
658 if (first)
660 first = FALSE;
661 *text = g_strdup (buf);
663 else
665 /* remove \n on EOL */
666 char *tmp;
668 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
669 g_free (*text);
670 *text = tmp;
675 fclose (f);
677 return (*text != NULL);
680 static gboolean
681 panel_save_curent_file_to_clip_file (void)
683 gboolean res;
685 if (current_panel->marked == 0)
686 res = save_text_to_clip_file (selection (current_panel)->fname);
687 else
689 int i;
690 gboolean first = TRUE;
691 char *flist = NULL;
693 for (i = 0; i < current_panel->count; i++)
694 if (current_panel->dir.list[i].f.marked != 0)
695 { /* Skip the unmarked ones */
696 if (first)
698 flist = g_strdup (current_panel->dir.list[i].fname);
699 first = FALSE;
701 else
703 /* Add empty lines after the file */
704 char *tmp;
706 tmp =
707 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
708 g_free (flist);
709 flist = tmp;
713 if (flist != NULL)
715 res = save_text_to_clip_file (flist);
716 g_free (flist);
719 return res;
722 /* Label widget */
724 static cb_ret_t
725 label_callback (Widget * w, widget_msg_t msg, int parm)
727 WLabel *l = (WLabel *) w;
728 Dlg_head *h = l->widget.parent;
730 switch (msg)
732 case WIDGET_INIT:
733 return MSG_HANDLED;
735 /* We don't want to get the focus */
736 case WIDGET_FOCUS:
737 return MSG_NOT_HANDLED;
739 case WIDGET_DRAW:
741 char *p = l->text, *q, c = 0;
742 int y = 0;
744 if (!l->text)
745 return MSG_HANDLED;
747 if (l->transparent)
748 tty_setcolor (DEFAULT_COLOR);
749 else
750 tty_setcolor (DLG_NORMALC (h));
752 for (;;)
754 q = strchr (p, '\n');
755 if (q != NULL)
757 c = q[0];
758 q[0] = '\0';
761 widget_move (&l->widget, y, 0);
762 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
764 if (q == NULL)
765 break;
766 q[0] = c;
767 p = q + 1;
768 y++;
770 return MSG_HANDLED;
773 case WIDGET_DESTROY:
774 g_free (l->text);
775 return MSG_HANDLED;
777 default:
778 return default_proc (msg, parm);
782 void
783 label_set_text (WLabel * label, const char *text)
785 int newcols = label->widget.cols;
786 int newlines;
788 if (label->text && text && !strcmp (label->text, text))
789 return; /* Flickering is not nice */
791 g_free (label->text);
793 if (text != NULL)
795 label->text = g_strdup (text);
796 if (label->auto_adjust_cols)
798 str_msg_term_size (text, &newlines, &newcols);
799 if (newcols > label->widget.cols)
800 label->widget.cols = newcols;
801 if (newlines > label->widget.lines)
802 label->widget.lines = newlines;
805 else
806 label->text = NULL;
808 if (label->widget.parent)
809 label_callback ((Widget *) label, WIDGET_DRAW, 0);
811 if (newcols < label->widget.cols)
812 label->widget.cols = newcols;
815 WLabel *
816 label_new (int y, int x, const char *text)
818 WLabel *l;
819 int cols = 1;
820 int lines = 1;
822 if (text != NULL)
823 str_msg_term_size (text, &lines, &cols);
825 l = g_new (WLabel, 1);
826 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
827 l->text = (text != NULL) ? g_strdup (text) : NULL;
828 l->auto_adjust_cols = 1;
829 l->transparent = 0;
830 widget_want_cursor (l->widget, 0);
831 return l;
834 static cb_ret_t
835 hline_callback (Widget * w, widget_msg_t msg, int parm)
837 WHLine *l = (WHLine *) w;
838 Dlg_head *h = l->widget.parent;
840 switch (msg)
842 case WIDGET_INIT:
843 case WIDGET_RESIZED:
844 if (l->auto_adjust_cols)
846 if (((w->parent->flags & DLG_COMPACT) != 0))
848 w->x = w->parent->x;
849 w->cols = w->parent->cols;
851 else
853 w->x = w->parent->x + 1;
854 w->cols = w->parent->cols - 2;
858 case WIDGET_FOCUS:
859 /* We don't want to get the focus */
860 return MSG_NOT_HANDLED;
862 case WIDGET_DRAW:
863 if (l->transparent)
864 tty_setcolor (DEFAULT_COLOR);
865 else
866 tty_setcolor (DLG_NORMALC (h));
868 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
870 if (l->auto_adjust_cols)
872 widget_move (w, 0, 0);
873 tty_print_alt_char (ACS_LTEE, FALSE);
874 widget_move (w, 0, w->cols - 1);
875 tty_print_alt_char (ACS_RTEE, FALSE);
877 return MSG_HANDLED;
879 default:
880 return default_proc (msg, parm);
885 WHLine *
886 hline_new (int y, int x, int width)
888 WHLine *l;
889 int cols = width;
890 int lines = 1;
892 l = g_new (WHLine, 1);
893 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
894 l->auto_adjust_cols = (width < 0);
895 l->transparent = FALSE;
896 widget_want_cursor (l->widget, 0);
897 return l;
900 /* Gauge widget (progress indicator) */
901 /* Currently width is hardcoded here for text mode */
902 #define gauge_len 47
904 static cb_ret_t
905 gauge_callback (Widget * w, widget_msg_t msg, int parm)
907 WGauge *g = (WGauge *) w;
908 Dlg_head *h = g->widget.parent;
910 if (msg == WIDGET_INIT)
911 return MSG_HANDLED;
913 /* We don't want to get the focus */
914 if (msg == WIDGET_FOCUS)
915 return MSG_NOT_HANDLED;
917 if (msg == WIDGET_DRAW)
919 widget_move (&g->widget, 0, 0);
920 tty_setcolor (DLG_NORMALC (h));
921 if (!g->shown)
922 tty_printf ("%*s", gauge_len, "");
923 else
925 int percentage, columns;
926 long total = g->max, done = g->current;
928 if (total <= 0 || done < 0)
930 done = 0;
931 total = 100;
933 if (done > total)
934 done = total;
935 while (total > 65535)
937 total /= 256;
938 done /= 256;
940 percentage = (200 * done / total + 1) / 2;
941 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
942 tty_print_char ('[');
943 if (g->from_left_to_right)
945 tty_setcolor (GAUGE_COLOR);
946 tty_printf ("%*s", (int) columns, "");
947 tty_setcolor (DLG_NORMALC (h));
948 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
950 else
952 tty_setcolor (DLG_NORMALC (h));
953 tty_printf ("%*s", gauge_len - columns - 7, "");
954 tty_setcolor (GAUGE_COLOR);
955 tty_printf ("%*s", columns, "");
956 tty_setcolor (DLG_NORMALC (h));
957 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
960 return MSG_HANDLED;
963 return default_proc (msg, parm);
966 void
967 gauge_set_value (WGauge * g, int max, int current)
969 if (g->current == current && g->max == max)
970 return; /* Do not flicker */
971 if (max == 0)
972 max = 1; /* I do not like division by zero :) */
974 g->current = current;
975 g->max = max;
976 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
979 void
980 gauge_show (WGauge * g, int shown)
982 if (g->shown == shown)
983 return;
984 g->shown = shown;
985 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
988 WGauge *
989 gauge_new (int y, int x, int shown, int max, int current)
991 WGauge *g = g_new (WGauge, 1);
993 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
994 g->shown = shown;
995 if (max == 0)
996 max = 1; /* I do not like division by zero :) */
997 g->max = max;
998 g->current = current;
999 g->from_left_to_right = TRUE;
1000 widget_want_cursor (g->widget, 0);
1001 return g;
1005 /* Input widget */
1007 /* {{{ history button */
1009 #define LARGE_HISTORY_BUTTON 1
1011 #ifdef LARGE_HISTORY_BUTTON
1012 # define HISTORY_BUTTON_WIDTH 3
1013 #else
1014 # define HISTORY_BUTTON_WIDTH 1
1015 #endif
1017 #define should_show_history_button(in) \
1018 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
1020 static void
1021 draw_history_button (WInput * in)
1023 char c;
1024 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1025 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1026 #ifdef LARGE_HISTORY_BUTTON
1028 Dlg_head *h;
1029 h = in->widget.parent;
1030 tty_setcolor (NORMAL_COLOR);
1031 tty_print_string ("[ ]");
1032 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1033 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1034 tty_print_char (c);
1036 #else
1037 tty_setcolor (MARKED_COLOR);
1038 tty_print_char (c);
1039 #endif
1042 /* }}} history button */
1045 /* Input widgets now have a global kill ring */
1046 /* Pointer to killed data */
1047 static char *kill_buffer = NULL;
1049 static void
1050 input_set_markers (WInput * in, long m1)
1052 in->mark = m1;
1055 static void
1056 input_mark_cmd (WInput * in, gboolean mark)
1058 if (!mark)
1060 in->highlight = FALSE;
1061 input_set_markers (in, 0);
1063 else
1065 in->highlight = TRUE;
1066 input_set_markers (in, in->point);
1070 static gboolean
1071 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
1073 if (in->highlight)
1075 *start_mark = min (in->mark, in->point);
1076 *end_mark = max (in->mark, in->point);
1077 return TRUE;
1079 else
1081 *start_mark = *end_mark = 0;
1082 return FALSE;
1086 static void
1087 delete_region (WInput * in, int x_first, int x_last)
1089 int first = min (x_first, x_last);
1090 int last = max (x_first, x_last);
1091 size_t len;
1093 input_mark_cmd (in, FALSE);
1094 in->point = first;
1095 last = str_offset_to_pos (in->buffer, last);
1096 first = str_offset_to_pos (in->buffer, first);
1097 len = strlen (&in->buffer[last]) + 1;
1098 memmove (&in->buffer[first], &in->buffer[last], len);
1099 in->charpoint = 0;
1100 in->need_push = 1;
1103 void
1104 update_input (WInput * in, int clear_first)
1106 int has_history = 0;
1107 int i;
1108 int buf_len = str_length (in->buffer);
1109 const char *cp;
1110 int pw;
1112 if (should_show_history_button (in))
1113 has_history = HISTORY_BUTTON_WIDTH;
1115 if (in->disable_update)
1116 return;
1118 pw = str_term_width2 (in->buffer, in->point);
1120 /* Make the point visible */
1121 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1124 in->term_first_shown = pw - (in->field_width / 3);
1125 if (in->term_first_shown < 0)
1126 in->term_first_shown = 0;
1129 /* Adjust the mark */
1130 if (in->mark > buf_len)
1131 in->mark = buf_len;
1133 if (has_history)
1134 draw_history_button (in);
1136 if (in->first)
1137 tty_setcolor (in->unchanged_color);
1138 else
1139 tty_setcolor (in->color);
1141 widget_move (&in->widget, 0, 0);
1143 if (!in->is_password)
1145 if (!in->highlight)
1147 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1148 in->field_width - has_history));
1150 else
1152 long m1, m2;
1153 if (input_eval_marks (in, &m1, &m2))
1155 tty_setcolor (in->color);
1156 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1157 tty_print_string (cp);
1158 tty_setcolor (in->mark_color);
1159 if (m1 < in->term_first_shown)
1161 widget_move (&in->widget, 0, 0);
1162 tty_print_string (str_term_substring (in->buffer, in->term_first_shown, m2 - in->term_first_shown));
1164 else
1166 int sel_width;
1167 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1168 sel_width = min (m2 - m1, (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) - in->term_first_shown));
1169 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1174 else
1176 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1177 for (i = 0; i < in->field_width - has_history; i++)
1179 if (i >= 0)
1181 tty_setcolor (in->color);
1182 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1184 if (cp[0] != '\0')
1185 str_cnext_char (&cp);
1189 if (clear_first)
1190 in->first = FALSE;
1193 void
1194 winput_set_origin (WInput * in, int x, int field_width)
1196 in->widget.x = x;
1197 in->field_width = in->widget.cols = field_width;
1198 update_input (in, 0);
1201 /* {{{ history saving and loading */
1203 int num_history_items_recorded = 60;
1206 This loads and saves the history of an input line to and from the
1207 widget. It is called with the widgets history name on creation of the
1208 widget, and returns the GList list. It stores histories in the file
1209 ~/.mc/history in using the profile code.
1211 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1212 function) then input_new assigns the default text to be the last text
1213 entered, or "" if not found.
1216 GList *
1217 history_get (const char *input_name)
1219 size_t i;
1220 GList *hist = NULL;
1221 char *profile;
1222 mc_config_t *cfg;
1223 char **keys;
1224 size_t keys_num = 0;
1225 char *this_entry;
1227 if (num_history_items_recorded == 0) /* this is how to disable */
1228 return NULL;
1229 if ((input_name == NULL) || (*input_name == '\0'))
1230 return NULL;
1232 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1233 cfg = mc_config_init (profile);
1235 /* get number of keys */
1236 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1237 g_strfreev (keys);
1239 for (i = 0; i < keys_num; i++)
1241 char key[BUF_TINY];
1242 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1243 this_entry = mc_config_get_string (cfg, input_name, key, "");
1245 if (this_entry != NULL)
1246 hist = list_append_unique (hist, this_entry);
1249 mc_config_deinit (cfg);
1250 g_free (profile);
1252 /* return pointer to the last entry in the list */
1253 return g_list_last (hist);
1256 void
1257 history_put (const char *input_name, GList * h)
1259 int i;
1260 char *profile;
1261 mc_config_t *cfg;
1263 if (num_history_items_recorded == 0) /* this is how to disable */
1264 return;
1265 if ((input_name == NULL) || (*input_name == '\0'))
1266 return;
1267 if (h == NULL)
1268 return;
1270 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1272 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1273 if (i != -1)
1274 close (i);
1276 /* Make sure the history is only readable by the user */
1277 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1279 g_free (profile);
1280 return;
1283 /* go to end of list */
1284 h = g_list_last (h);
1286 /* go back 60 places */
1287 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1288 h = g_list_previous (h);
1290 cfg = mc_config_init (profile);
1292 if (input_name != NULL)
1293 mc_config_del_group (cfg, input_name);
1295 /* dump history into profile */
1296 for (i = 0; h != NULL; h = g_list_next (h))
1298 char *text = (char *) h->data;
1300 /* We shouldn't have null entries, but let's be sure */
1301 if (text != NULL)
1303 char key[BUF_TINY];
1304 g_snprintf (key, sizeof (key), "%d", i++);
1305 mc_config_set_string (cfg, input_name, key, text);
1309 mc_config_save_file (cfg, NULL);
1310 mc_config_deinit (cfg);
1311 g_free (profile);
1314 /* }}} history saving and loading */
1317 /* {{{ history display */
1319 static const char *
1320 i18n_htitle (void)
1322 return _(" History ");
1325 typedef struct
1327 Widget *widget;
1328 size_t count;
1329 size_t maxlen;
1330 } dlg_hist_data;
1332 static cb_ret_t
1333 dlg_hist_reposition (Dlg_head * dlg_head)
1335 dlg_hist_data *data;
1336 int x = 0, y, he, wi;
1338 /* guard checks */
1339 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1340 return MSG_NOT_HANDLED;
1342 data = (dlg_hist_data *) dlg_head->data;
1344 y = data->widget->y;
1345 he = data->count + 2;
1347 if (he <= y || y > (LINES - 6))
1349 he = min (he, y - 1);
1350 y -= he;
1352 else
1354 y++;
1355 he = min (he, LINES - y);
1358 if (data->widget->x > 2)
1359 x = data->widget->x - 2;
1361 wi = data->maxlen + 4;
1363 if ((wi + x) > COLS)
1365 wi = min (wi, COLS);
1366 x = COLS - wi;
1369 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1371 return MSG_HANDLED;
1374 static cb_ret_t
1375 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1377 switch (msg)
1379 case DLG_RESIZE:
1380 return dlg_hist_reposition (h);
1382 default:
1383 return default_dlg_callback (h, sender, msg, parm, data);
1387 char *
1388 show_hist (GList ** history, Widget * widget)
1390 GList *z, *hlist = NULL, *hi;
1391 size_t maxlen, i, count = 0;
1392 char *r = NULL;
1393 Dlg_head *query_dlg;
1394 WListbox *query_list;
1395 dlg_hist_data hist_data;
1397 if (*history == NULL)
1398 return NULL;
1400 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1402 for (z = *history; z != NULL; z = g_list_previous (z))
1404 WLEntry *entry;
1406 i = str_term_width1 ((char *) z->data);
1407 maxlen = max (maxlen, i);
1408 count++;
1410 entry = g_new0 (WLEntry, 1);
1411 /* history is being reverted here */
1412 entry->text = g_strdup ((char *) z->data);
1413 hlist = g_list_prepend (hlist, entry);
1416 hist_data.widget = widget;
1417 hist_data.count = count;
1418 hist_data.maxlen = maxlen;
1420 query_dlg =
1421 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1422 "[History-query]", i18n_htitle (), DLG_COMPACT);
1423 query_dlg->data = &hist_data;
1425 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1427 /* this call makes list stick to all sides of dialog, effectively make
1428 it be resized with dialog */
1429 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1431 /* to avoid diplicating of (calculating sizes in two places)
1432 code, call dlg_hist_callback function here, to set dialog and
1433 controls positions.
1434 The main idea - create 4x4 dialog and add 2x2 list in
1435 center of it, and let dialog function resize it to needed
1436 size. */
1437 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1439 if (query_dlg->y < widget->y)
1441 /* draw list entries from bottom upto top */
1442 listbox_set_list (query_list, hlist);
1443 listbox_select_last (query_list);
1445 else
1447 /* draw list entries from top downto bottom */
1448 /* revert history direction */
1449 hlist = g_list_reverse (hlist);
1450 listbox_set_list (query_list, hlist);
1453 if (run_dlg (query_dlg) != B_CANCEL)
1455 char *q;
1457 listbox_get_current (query_list, &q, NULL);
1458 if (q != NULL)
1459 r = g_strdup (q);
1462 /* get modified history from dialog */
1463 z = NULL;
1464 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1466 WLEntry *entry;
1468 entry = (WLEntry *) hi->data;
1469 /* history is being reverted here again */
1470 z = g_list_prepend (z, entry->text);
1471 entry->text = NULL;
1474 destroy_dlg (query_dlg);
1476 /* restore history direction */
1477 if (query_dlg->y < widget->y)
1478 z = g_list_reverse (z);
1480 g_list_foreach (*history, (GFunc) g_free, NULL);
1481 g_list_free (*history);
1482 *history = g_list_last (z);
1484 return r;
1487 static void
1488 do_show_hist (WInput * in)
1490 char *r;
1492 r = show_hist (&in->history, &in->widget);
1493 if (r != NULL)
1495 assign_text (in, r);
1496 g_free (r);
1500 /* }}} history display */
1502 static void
1503 input_destroy (WInput * in)
1505 if (in == NULL)
1507 fprintf (stderr, "Internal error: null Input *\n");
1508 exit (EXIT_FAILURE);
1511 new_input (in);
1513 if (in->history != NULL)
1515 if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
1516 history_put (in->history_name, in->history);
1518 in->history = g_list_first (in->history);
1519 g_list_foreach (in->history, (GFunc) g_free, NULL);
1520 g_list_free (in->history);
1523 g_free (in->buffer);
1524 free_completions (in);
1525 g_free (in->history_name);
1527 g_free (kill_buffer);
1528 kill_buffer = NULL;
1531 void
1532 input_disable_update (WInput * in)
1534 in->disable_update++;
1537 void
1538 input_enable_update (WInput * in)
1540 in->disable_update--;
1541 update_input (in, 0);
1545 static void
1546 push_history (WInput * in, const char *text)
1548 /* input widget where urls with passwords are entered without any
1549 vfs prefix */
1550 const char *password_input_fields[] = {
1551 N_(" Link to a remote machine "),
1552 N_(" FTP to machine "),
1553 N_(" SMB link to machine ")
1555 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1557 char *t;
1558 size_t i;
1559 gboolean empty;
1561 if (text == NULL)
1562 return;
1564 #ifdef ENABLE_NLS
1565 for (i = 0; i < ELEMENTS; i++)
1566 password_input_fields[i] = _(password_input_fields[i]);
1567 #endif
1569 t = g_strstrip (g_strdup (text));
1570 empty = *t == '\0';
1571 g_free (t);
1572 t = g_strdup (empty ? "" : text);
1574 if (in->history_name != NULL)
1576 const char *p = in->history_name + 3;
1578 for (i = 0; i < ELEMENTS; i++)
1579 if (strcmp (p, password_input_fields[i]) == 0)
1580 break;
1582 strip_password (t, i >= ELEMENTS);
1585 in->history = list_append_unique (in->history, t);
1586 in->need_push = 0;
1589 /* Cleans the input line and adds the current text to the history */
1590 void
1591 new_input (WInput * in)
1593 push_history (in, in->buffer);
1594 in->need_push = 1;
1595 in->buffer[0] = '\0';
1596 in->point = 0;
1597 in->charpoint = 0;
1598 in->mark = 0;
1599 in->highlight = FALSE;
1600 free_completions (in);
1601 update_input (in, 0);
1604 static void
1605 move_buffer_backward (WInput * in, int start, int end)
1607 int i, pos, len;
1608 int str_len = str_length (in->buffer);
1609 if (start >= str_len || end > str_len + 1)
1610 return;
1612 pos = str_offset_to_pos (in->buffer, start);
1613 len = str_offset_to_pos (in->buffer, end) - pos;
1615 for (i = pos; in->buffer[i + len - 1]; i++)
1616 in->buffer[i] = in->buffer[i + len];
1619 static cb_ret_t
1620 insert_char (WInput * in, int c_code)
1622 size_t i;
1623 int res;
1625 if (in->highlight)
1627 long m1, m2;
1628 if (input_eval_marks (in, &m1, &m2))
1629 delete_region (in, m1, m2);
1631 if (c_code == -1)
1632 return MSG_NOT_HANDLED;
1634 if (in->charpoint >= MB_LEN_MAX)
1635 return MSG_HANDLED;
1637 in->charbuf[in->charpoint] = c_code;
1638 in->charpoint++;
1640 res = str_is_valid_char (in->charbuf, in->charpoint);
1641 if (res < 0)
1643 if (res != -2)
1644 in->charpoint = 0; /* broken multibyte char, skip */
1645 return MSG_HANDLED;
1648 in->need_push = 1;
1649 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1651 /* Expand the buffer */
1652 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1653 char *narea = g_try_renew (char, in->buffer, new_length);
1654 if (narea)
1656 in->buffer = narea;
1657 in->current_max_size = new_length;
1661 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1663 /* bytes from begin */
1664 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1665 /* move chars */
1666 size_t rest_bytes = strlen (in->buffer + ins_point);
1668 for (i = rest_bytes + 1; i > 0; i--)
1669 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1671 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1672 in->point++;
1675 in->charpoint = 0;
1676 return MSG_HANDLED;
1679 static void
1680 beginning_of_line (WInput * in)
1682 in->point = 0;
1683 in->charpoint = 0;
1686 static void
1687 end_of_line (WInput * in)
1689 in->point = str_length (in->buffer);
1690 in->charpoint = 0;
1693 static void
1694 backward_char (WInput * in)
1696 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1698 if (in->point > 0)
1700 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1702 in->charpoint = 0;
1705 static void
1706 forward_char (WInput * in)
1708 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1709 if (act[0] != '\0')
1711 in->point += str_cnext_noncomb_char (&act);
1713 in->charpoint = 0;
1716 static void
1717 forward_word (WInput * in)
1719 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1721 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1723 str_cnext_char (&p);
1724 in->point++;
1726 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1728 str_cnext_char (&p);
1729 in->point++;
1733 static void
1734 backward_word (WInput * in)
1736 const char *p;
1737 const char *p_tmp;
1739 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1740 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1742 while (p != in->buffer)
1744 p_tmp = p;
1745 str_cprev_char (&p);
1746 if (!str_isspace (p) && !str_ispunct (p))
1748 p = p_tmp;
1749 break;
1751 in->point--;
1753 while (p != in->buffer)
1755 str_cprev_char (&p);
1756 if (str_isspace (p) || str_ispunct (p))
1757 break;
1759 in->point--;
1763 static void
1764 key_left (WInput * in)
1766 backward_char (in);
1769 static void
1770 key_ctrl_left (WInput * in)
1772 backward_word (in);
1775 static void
1776 key_right (WInput * in)
1778 forward_char (in);
1781 static void
1782 key_ctrl_right (WInput * in)
1784 forward_word (in);
1786 static void
1787 backward_delete (WInput * in)
1789 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1790 int start;
1792 if (in->point == 0)
1793 return;
1795 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1796 move_buffer_backward (in, start, in->point);
1797 in->charpoint = 0;
1798 in->need_push = 1;
1799 in->point = start;
1802 static void
1803 delete_char (WInput * in)
1805 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1806 int end = in->point;
1808 end += str_cnext_noncomb_char (&act);
1810 move_buffer_backward (in, in->point, end);
1811 in->charpoint = 0;
1812 in->need_push = 1;
1815 static void
1816 copy_region (WInput * in, int x_first, int x_last)
1818 int first = min (x_first, x_last);
1819 int last = max (x_first, x_last);
1821 if (last == first)
1823 /* Copy selected files to clipboard */
1824 panel_save_curent_file_to_clip_file ();
1825 return;
1828 g_free (kill_buffer);
1830 first = str_offset_to_pos (in->buffer, first);
1831 last = str_offset_to_pos (in->buffer, last);
1833 kill_buffer = g_strndup (in->buffer + first, last - first);
1834 save_text_to_clip_file (kill_buffer);
1837 static void
1838 kill_word (WInput * in)
1840 int old_point = in->point;
1841 int new_point;
1843 forward_word (in);
1844 new_point = in->point;
1845 in->point = old_point;
1847 copy_region (in, old_point, new_point);
1848 delete_region (in, old_point, new_point);
1849 in->need_push = 1;
1850 in->charpoint = 0;
1851 in->charpoint = 0;
1854 static void
1855 back_kill_word (WInput * in)
1857 int old_point = in->point;
1858 int new_point;
1860 backward_word (in);
1861 new_point = in->point;
1862 in->point = old_point;
1864 copy_region (in, old_point, new_point);
1865 delete_region (in, old_point, new_point);
1866 in->need_push = 1;
1869 static void
1870 set_mark (WInput * in)
1872 input_mark_cmd (in, TRUE);
1875 static void
1876 kill_save (WInput * in)
1878 copy_region (in, in->mark, in->point);
1881 static void
1882 kill_region (WInput * in)
1884 kill_save (in);
1885 delete_region (in, in->point, in->mark);
1888 static void
1889 clear_region (WInput * in)
1891 delete_region (in, in->point, in->mark);
1894 static void
1895 yank (WInput * in)
1897 if (kill_buffer != NULL)
1899 char *p;
1901 in->charpoint = 0;
1902 for (p = kill_buffer; *p != '\0'; p++)
1903 insert_char (in, *p);
1904 in->charpoint = 0;
1908 static void
1909 kill_line (WInput * in)
1911 int chp = str_offset_to_pos (in->buffer, in->point);
1912 g_free (kill_buffer);
1913 kill_buffer = g_strdup (&in->buffer[chp]);
1914 in->buffer[chp] = '\0';
1915 in->charpoint = 0;
1918 static void
1919 ins_from_clip (WInput * in)
1921 char *p = NULL;
1923 if (load_text_from_clip_file (&p))
1925 char *pp;
1927 for (pp = p; *pp != '\0'; pp++)
1928 insert_char (in, *pp);
1930 g_free (p);
1934 void
1935 assign_text (WInput * in, const char *text)
1937 free_completions (in);
1938 g_free (in->buffer);
1939 in->buffer = g_strdup (text); /* was in->buffer->text */
1940 in->current_max_size = strlen (in->buffer) + 1;
1941 in->point = str_length (in->buffer);
1942 in->mark = 0;
1943 in->need_push = 1;
1944 in->charpoint = 0;
1947 static void
1948 hist_prev (WInput * in)
1950 GList *prev;
1952 if (!in->history)
1953 return;
1955 if (in->need_push)
1956 push_history (in, in->buffer);
1958 prev = g_list_previous (in->history);
1959 if (prev != NULL)
1961 in->history = prev;
1962 assign_text (in, (char *) prev->data);
1963 in->need_push = 0;
1967 static void
1968 hist_next (WInput * in)
1970 if (in->need_push)
1972 push_history (in, in->buffer);
1973 assign_text (in, "");
1974 return;
1977 if (!in->history)
1978 return;
1980 if (!in->history->next)
1982 assign_text (in, "");
1983 return;
1986 in->history = g_list_next (in->history);
1987 assign_text (in, (char *) in->history->data);
1988 in->need_push = 0;
1991 static void
1992 port_region_marked_for_delete (WInput * in)
1994 in->buffer[0] = '\0';
1995 in->point = 0;
1996 in->first = FALSE;
1997 in->charpoint = 0;
2000 static cb_ret_t
2001 input_execute_cmd (WInput * in, unsigned long command)
2003 cb_ret_t res = MSG_HANDLED;
2005 /* a highlight command like shift-arrow */
2006 if (command == CK_InputLeftHighlight ||
2007 command == CK_InputRightHighlight ||
2008 command == CK_InputWordLeftHighlight ||
2009 command == CK_InputWordRightHighlight ||
2010 command == CK_InputBolHighlight ||
2011 command == CK_InputEolHighlight)
2013 if (!in->highlight)
2015 input_mark_cmd (in, FALSE); /* clear */
2016 input_mark_cmd (in, TRUE); /* marking on */
2020 switch (command)
2022 case CK_InputForwardWord:
2023 case CK_InputBackwardWord:
2024 case CK_InputForwardChar:
2025 case CK_InputBackwardChar:
2026 if (in->highlight)
2027 input_mark_cmd (in, FALSE);
2030 switch (command)
2032 case CK_InputBol:
2033 case CK_InputBolHighlight:
2034 beginning_of_line (in);
2035 break;
2036 case CK_InputEol:
2037 case CK_InputEolHighlight:
2038 end_of_line (in);
2039 break;
2040 case CK_InputMoveLeft:
2041 case CK_InputLeftHighlight:
2042 key_left (in);
2043 break;
2044 case CK_InputWordLeft:
2045 case CK_InputWordLeftHighlight:
2046 key_ctrl_left (in);
2047 break;
2048 case CK_InputMoveRight:
2049 case CK_InputRightHighlight:
2050 key_right (in);
2051 break;
2052 case CK_InputWordRight:
2053 case CK_InputWordRightHighlight:
2054 key_ctrl_right (in);
2055 break;
2056 case CK_InputBackwardChar:
2057 backward_char (in);
2058 break;
2059 case CK_InputBackwardWord:
2060 backward_word (in);
2061 break;
2062 case CK_InputForwardChar:
2063 forward_char (in);
2064 break;
2065 case CK_InputForwardWord:
2066 forward_word (in);
2067 break;
2068 case CK_InputBackwardDelete:
2069 if (in->highlight)
2071 long m1, m2;
2072 if (input_eval_marks (in, &m1, &m2))
2073 delete_region (in, m1, m2);
2075 else
2077 backward_delete (in);
2079 break;
2080 case CK_InputDeleteChar:
2081 if (in->first)
2082 port_region_marked_for_delete (in);
2083 else if (in->highlight)
2085 long m1, m2;
2086 if (input_eval_marks (in, &m1, &m2))
2087 delete_region (in, m1, m2);
2089 else
2090 delete_char (in);
2091 break;
2092 case CK_InputKillWord:
2093 kill_word (in);
2094 break;
2095 case CK_InputBackwardKillWord:
2096 back_kill_word (in);
2097 break;
2098 case CK_InputSetMark:
2099 set_mark (in);
2100 break;
2101 case CK_InputKillRegion:
2102 kill_region (in);
2103 break;
2104 case CK_InputClearLine:
2105 clear_region (in);
2106 break;
2107 case CK_InputKillSave:
2108 kill_save (in);
2109 break;
2110 case CK_InputYank:
2111 yank (in);
2112 break;
2113 case CK_InputPaste:
2114 ins_from_clip (in);
2115 break;
2116 case CK_InputKillLine:
2117 kill_line (in);
2118 break;
2119 case CK_InputHistoryPrev:
2120 hist_prev (in);
2121 break;
2122 case CK_InputHistoryNext:
2123 hist_next (in);
2124 break;
2125 case CK_InputHistoryShow:
2126 do_show_hist (in);
2127 break;
2128 case CK_InputComplete:
2129 complete (in);
2130 break;
2131 default:
2132 res = MSG_NOT_HANDLED;
2135 if (command != CK_InputLeftHighlight &&
2136 command != CK_InputRightHighlight &&
2137 command != CK_InputWordLeftHighlight &&
2138 command != CK_InputWordRightHighlight &&
2139 command != CK_InputBolHighlight &&
2140 command != CK_InputEolHighlight)
2142 in->highlight = FALSE;
2145 return res;
2148 /* This function is a test for a special input key used in complete.c */
2149 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2150 and 2 if it is a complete key */
2152 is_in_input_map (WInput * in, int key)
2154 size_t i;
2155 for (i = 0; input_map[i].key != 0; i++)
2156 if (key == input_map[i].key)
2158 input_execute_cmd (in, input_map[i].command);
2159 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2161 return 0;
2164 cb_ret_t
2165 handle_char (WInput * in, int key)
2167 cb_ret_t v;
2168 int i;
2170 v = MSG_NOT_HANDLED;
2172 if (quote)
2174 free_completions (in);
2175 v = insert_char (in, key);
2176 update_input (in, 1);
2177 quote = 0;
2178 return v;
2180 for (i = 0; input_map[i].key; i++)
2182 if (key == input_map[i].key)
2184 if (input_map[i].command != CK_InputComplete)
2185 free_completions (in);
2186 input_execute_cmd (in, input_map[i].command);
2187 update_input (in, 1);
2188 v = MSG_HANDLED;
2189 break;
2192 if (input_map[i].command == 0)
2194 if (key > 255)
2195 return MSG_NOT_HANDLED;
2196 if (in->first)
2197 port_region_marked_for_delete (in);
2198 free_completions (in);
2199 v = insert_char (in, key);
2201 update_input (in, 1);
2202 return v;
2205 /* Inserts text in input line */
2206 void
2207 stuff (WInput * in, const char *text, int insert_extra_space)
2209 input_disable_update (in);
2210 while (*text != '\0')
2211 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2212 if (insert_extra_space)
2213 handle_char (in, ' ');
2214 input_enable_update (in);
2215 update_input (in, 1);
2218 void
2219 input_set_point (WInput * in, int pos)
2221 int max_pos = str_length (in->buffer);
2223 if (pos > max_pos)
2224 pos = max_pos;
2225 if (pos != in->point)
2226 free_completions (in);
2227 in->point = pos;
2228 in->charpoint = 0;
2229 update_input (in, 1);
2232 cb_ret_t
2233 input_callback (Widget * w, widget_msg_t msg, int parm)
2235 WInput *in = (WInput *) w;
2236 cb_ret_t v;
2238 switch (msg)
2240 case WIDGET_KEY:
2241 if (parm == XCTRL ('q'))
2243 quote = 1;
2244 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2245 quote = 0;
2246 return v;
2249 /* Keys we want others to handle */
2250 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2251 || parm == KEY_F (10) || parm == '\n')
2252 return MSG_NOT_HANDLED;
2254 /* When pasting multiline text, insert literal Enter */
2255 if ((parm & ~KEY_M_MASK) == '\n')
2257 quote = 1;
2258 v = handle_char (in, '\n');
2259 quote = 0;
2260 return v;
2263 return handle_char (in, parm);
2265 case WIDGET_COMMAND:
2266 return input_execute_cmd (in, parm);
2268 case WIDGET_FOCUS:
2269 case WIDGET_UNFOCUS:
2270 case WIDGET_DRAW:
2271 update_input (in, 0);
2272 return MSG_HANDLED;
2274 case WIDGET_CURSOR:
2275 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2276 - in->term_first_shown);
2277 return MSG_HANDLED;
2279 case WIDGET_DESTROY:
2280 input_destroy (in);
2281 return MSG_HANDLED;
2283 default:
2284 return default_proc (msg, parm);
2288 static int
2289 input_event (Gpm_Event * event, void *data)
2291 WInput *in = data;
2293 if (event->type & GPM_DOWN)
2295 in->first = FALSE;
2296 input_mark_cmd (in, FALSE);
2298 if (event->type & (GPM_DOWN | GPM_DRAG))
2300 dlg_select_widget (in);
2302 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2303 && should_show_history_button (in))
2305 do_show_hist (in);
2307 else
2309 in->point = str_length (in->buffer);
2310 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2311 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2313 update_input (in, 1);
2315 /* A lone up mustn't do anything */
2316 if (in->highlight && event->type & (GPM_UP | GPM_DRAG))
2317 return MOU_NORMAL;
2319 if (!(event->type & GPM_DRAG))
2320 input_mark_cmd (in, TRUE);
2322 return MOU_NORMAL;
2325 WInput *
2326 input_new (int y, int x, int *input_colors, int width, const char *def_text,
2327 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2329 WInput *in = g_new (WInput, 1);
2330 size_t initial_buffer_len;
2332 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2334 /* history setup */
2335 in->history_name = NULL;
2336 in->history = NULL;
2337 if ((histname != NULL) && (*histname != '\0'))
2339 in->history_name = g_strdup (histname);
2340 in->history = history_get (histname);
2343 if (def_text == NULL)
2344 def_text = "";
2345 else if (def_text == INPUT_LAST_TEXT)
2347 if ((in->history != NULL) && (in->history->data != NULL))
2348 def_text = (char *) in->history->data;
2349 else
2350 def_text = "";
2353 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2354 in->widget.options |= W_IS_INPUT;
2355 in->completions = NULL;
2356 in->completion_flags = completion_flags;
2357 in->current_max_size = initial_buffer_len;
2358 in->buffer = g_new (char, initial_buffer_len);
2359 in->color = input_colors[0];
2360 in->unchanged_color = input_colors[1];
2361 in->mark_color = input_colors[2];
2362 in->field_width = width;
2363 in->first = TRUE;
2364 in->highlight = FALSE;
2365 in->term_first_shown = 0;
2366 in->disable_update = 0;
2367 in->mark = 0;
2368 in->need_push = 1;
2369 in->is_password = 0;
2371 strcpy (in->buffer, def_text);
2372 in->point = str_length (in->buffer);
2373 in->charpoint = 0;
2375 return in;
2379 /* Listbox widget */
2381 /* Should draw the scrollbar, but currently draws only
2382 * indications that there is more information
2385 static void
2386 listbox_entry_free (void *data)
2388 WLEntry *e = data;
2389 g_free (e->text);
2390 g_free (e);
2393 static void
2394 listbox_drawscroll (WListbox * l)
2396 const int max_line = l->widget.lines - 1;
2397 int line = 0;
2398 int i;
2400 /* Are we at the top? */
2401 widget_move (&l->widget, 0, l->widget.cols);
2402 if (l->top == 0)
2403 tty_print_one_vline (TRUE);
2404 else
2405 tty_print_char ('^');
2407 /* Are we at the bottom? */
2408 widget_move (&l->widget, max_line, l->widget.cols);
2409 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2410 tty_print_one_vline (TRUE);
2411 else
2412 tty_print_char ('v');
2414 /* Now draw the nice relative pointer */
2415 if (l->count != 0)
2416 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2418 for (i = 1; i < max_line; i++)
2420 widget_move (&l->widget, i, l->widget.cols);
2421 if (i != line)
2422 tty_print_one_vline (TRUE);
2423 else
2424 tty_print_char ('*');
2428 static void
2429 listbox_draw (WListbox * l, gboolean focused)
2431 const Dlg_head *h = l->widget.parent;
2432 const int normalc = DLG_NORMALC (h);
2433 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2435 GList *le;
2436 int pos;
2437 int i;
2438 int sel_line = -1;
2440 le = g_list_nth (l->list, l->top);
2441 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2442 pos = (le == NULL) ? 0 : l->top;
2444 for (i = 0; i < l->widget.lines; i++)
2446 const char *text;
2448 /* Display the entry */
2449 if (pos == l->pos && sel_line == -1)
2451 sel_line = i;
2452 tty_setcolor (selc);
2454 else
2455 tty_setcolor (normalc);
2457 widget_move (&l->widget, i, 1);
2459 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2460 text = "";
2461 else
2463 WLEntry *e = (WLEntry *) le->data;
2464 text = e->text;
2465 le = g_list_next (le);
2466 pos++;
2469 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2472 l->cursor_y = sel_line;
2474 if (l->scrollbar && (l->count > l->widget.lines))
2476 tty_setcolor (normalc);
2477 listbox_drawscroll (l);
2481 static int
2482 listbox_check_hotkey (WListbox * l, int key)
2484 int i;
2485 GList *le;
2487 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2489 WLEntry *e = (WLEntry *) le->data;
2491 if (e->hotkey == key)
2492 return i;
2495 return (-1);
2498 /* Selects the last entry and scrolls the list to the bottom */
2499 void
2500 listbox_select_last (WListbox * l)
2502 l->pos = l->count - 1;
2503 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2506 /* Selects the first entry and scrolls the list to the top */
2507 void
2508 listbox_select_first (WListbox * l)
2510 l->pos = l->top = 0;
2513 void
2514 listbox_set_list (WListbox * l, GList * list)
2516 listbox_remove_list (l);
2518 if (l != NULL)
2520 l->list = list;
2521 l->top = l->pos = 0;
2522 l->count = g_list_length (list);
2526 void
2527 listbox_remove_list (WListbox * l)
2529 if ((l != NULL) && (l->count != 0))
2531 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2532 g_list_free (l->list);
2533 l->list = NULL;
2534 l->count = l->pos = l->top = 0;
2538 void
2539 listbox_remove_current (WListbox * l)
2541 if ((l != NULL) && (l->count != 0))
2543 GList *current;
2545 current = g_list_nth (l->list, l->pos);
2546 l->list = g_list_remove_link (l->list, current);
2547 listbox_entry_free ((WLEntry *) current->data);
2548 g_list_free_1 (current);
2549 l->count--;
2551 if (l->count == 0)
2552 l->top = l->pos = 0;
2553 else if (l->pos >= l->count)
2554 l->pos = l->count - 1;
2558 void
2559 listbox_select_entry (WListbox * l, int dest)
2561 GList *le;
2562 int pos;
2563 gboolean top_seen = FALSE;
2565 if (dest < 0)
2566 return;
2568 /* Special case */
2569 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2571 if (pos == l->top)
2572 top_seen = TRUE;
2574 if (pos == dest)
2576 l->pos = dest;
2577 if (!top_seen)
2578 l->top = l->pos;
2579 else if (l->pos - l->top >= l->widget.lines)
2580 l->top = l->pos - l->widget.lines + 1;
2581 return;
2585 /* If we are unable to find it, set decent values */
2586 l->pos = l->top = 0;
2589 /* Selects from base the pos element */
2590 static int
2591 listbox_select_pos (WListbox * l, int base, int pos)
2593 int last = l->count - 1;
2595 base += pos;
2596 if (base >= last)
2597 base = last;
2599 return base;
2602 static void
2603 listbox_fwd (WListbox * l)
2605 if (l->pos + 1 >= l->count)
2606 listbox_select_first (l);
2607 else
2608 listbox_select_entry (l, l->pos + 1);
2611 static void
2612 listbox_back (WListbox * l)
2614 if (l->pos <= 0)
2615 listbox_select_last (l);
2616 else
2617 listbox_select_entry (l, l->pos - 1);
2620 /* Return MSG_HANDLED if we want a redraw */
2621 static cb_ret_t
2622 listbox_key (WListbox * l, int key)
2624 int i;
2626 cb_ret_t j = MSG_NOT_HANDLED;
2628 if (l->list == NULL)
2629 return MSG_NOT_HANDLED;
2631 /* focus on listbox item N by '0'..'9' keys */
2632 if (key >= '0' && key <= '9')
2634 int oldpos = l->pos;
2635 listbox_select_entry (l, key - '0');
2637 /* need scroll to item? */
2638 if (abs (oldpos - l->pos) > l->widget.lines)
2639 l->top = l->pos;
2641 return MSG_HANDLED;
2644 switch (key)
2646 case KEY_HOME:
2647 case KEY_A1:
2648 case ALT ('<'):
2649 listbox_select_first (l);
2650 return MSG_HANDLED;
2652 case KEY_END:
2653 case KEY_C1:
2654 case ALT ('>'):
2655 listbox_select_last (l);
2656 return MSG_HANDLED;
2658 case XCTRL ('p'):
2659 case KEY_UP:
2660 listbox_back (l);
2661 return MSG_HANDLED;
2663 case XCTRL ('n'):
2664 case KEY_DOWN:
2665 listbox_fwd (l);
2666 return MSG_HANDLED;
2668 case KEY_NPAGE:
2669 case XCTRL ('v'):
2670 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2672 listbox_fwd (l);
2673 j = MSG_HANDLED;
2675 break;
2677 case KEY_PPAGE:
2678 case ALT ('v'):
2679 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2681 listbox_back (l);
2682 j = MSG_HANDLED;
2684 break;
2686 case KEY_DC:
2687 case 'd':
2688 if (l->deletable)
2690 gboolean is_last = (l->pos + 1 >= l->count);
2691 gboolean is_more = (l->top + l->widget.lines >= l->count);
2693 listbox_remove_current (l);
2694 if ((l->top > 0) && (is_last || is_more))
2695 l->top--;
2697 return MSG_HANDLED;
2699 case (KEY_M_SHIFT | KEY_DC):
2700 case 'D':
2701 if (l->deletable && confirm_history_cleanup
2702 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2703 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2704 _("Do you want clean this history?"),
2705 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2707 listbox_remove_list (l);
2708 j = MSG_HANDLED;
2710 break;
2712 default:
2713 break;
2716 return j;
2719 static inline void
2720 listbox_destroy (WListbox * l)
2722 /* don't delete list in modifable listbox */
2723 if (!l->deletable)
2724 listbox_remove_list (l);
2727 static cb_ret_t
2728 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2730 WListbox *l = (WListbox *) w;
2731 Dlg_head *h = l->widget.parent;
2732 cb_ret_t ret_code;
2734 switch (msg)
2736 case WIDGET_INIT:
2737 return MSG_HANDLED;
2739 case WIDGET_HOTKEY:
2741 int pos, action;
2743 pos = listbox_check_hotkey (l, parm);
2744 if (pos < 0)
2745 return MSG_NOT_HANDLED;
2747 listbox_select_entry (l, pos);
2748 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2750 if (l->cback != NULL)
2751 action = l->cback (l);
2752 else
2753 action = LISTBOX_DONE;
2755 if (action == LISTBOX_DONE)
2757 h->ret_value = B_ENTER;
2758 dlg_stop (h);
2761 return MSG_HANDLED;
2764 case WIDGET_KEY:
2765 ret_code = listbox_key (l, parm);
2766 if (ret_code != MSG_NOT_HANDLED)
2768 listbox_draw (l, TRUE);
2769 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2771 return ret_code;
2773 case WIDGET_CURSOR:
2774 widget_move (&l->widget, l->cursor_y, 0);
2775 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2776 return MSG_HANDLED;
2778 case WIDGET_FOCUS:
2779 case WIDGET_UNFOCUS:
2780 case WIDGET_DRAW:
2781 listbox_draw (l, msg != WIDGET_UNFOCUS);
2782 return MSG_HANDLED;
2784 case WIDGET_DESTROY:
2785 listbox_destroy (l);
2786 return MSG_HANDLED;
2788 case WIDGET_RESIZED:
2789 return MSG_HANDLED;
2791 default:
2792 return default_proc (msg, parm);
2796 static int
2797 listbox_event (Gpm_Event * event, void *data)
2799 WListbox *l = data;
2800 int i;
2802 Dlg_head *h = l->widget.parent;
2804 /* Single click */
2805 if (event->type & GPM_DOWN)
2806 dlg_select_widget (l);
2808 if (l->list == NULL)
2809 return MOU_NORMAL;
2811 if (event->type & (GPM_DOWN | GPM_DRAG))
2813 int ret = MOU_REPEAT;
2815 if (event->x < 0 || event->x > l->widget.cols)
2816 return ret;
2818 if (event->y < 1)
2819 for (i = -event->y; i >= 0; i--)
2820 listbox_back (l);
2821 else if (event->y > l->widget.lines)
2822 for (i = event->y - l->widget.lines; i > 0; i--)
2823 listbox_fwd (l);
2824 else if (event->buttons & GPM_B_UP)
2826 listbox_back (l);
2827 ret = MOU_NORMAL;
2829 else if (event->buttons & GPM_B_DOWN)
2831 listbox_fwd (l);
2832 ret = MOU_NORMAL;
2834 else
2835 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2837 /* We need to refresh ourselves since the dialog manager doesn't */
2838 /* know about this event */
2839 listbox_draw (l, TRUE);
2840 return ret;
2843 /* Double click */
2844 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2846 int action;
2848 if (event->x < 0 || event->x >= l->widget.cols
2849 || event->y < 1 || event->y > l->widget.lines)
2850 return MOU_NORMAL;
2852 dlg_select_widget (l);
2853 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2855 if (l->cback != NULL)
2856 action = l->cback (l);
2857 else
2858 action = LISTBOX_DONE;
2860 if (action == LISTBOX_DONE)
2862 h->ret_value = B_ENTER;
2863 dlg_stop (h);
2864 return MOU_NORMAL;
2867 return MOU_NORMAL;
2870 WListbox *
2871 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2873 WListbox *l = g_new (WListbox, 1);
2875 if (height <= 0)
2876 height = 1;
2878 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2880 l->list = NULL;
2881 l->top = l->pos = 0;
2882 l->count = 0;
2883 l->deletable = deletable;
2884 l->cback = callback;
2885 l->allow_duplicates = TRUE;
2886 l->scrollbar = !tty_is_slow ();
2887 widget_want_hotkey (l->widget, 1);
2888 widget_want_cursor (l->widget, 0);
2890 return l;
2893 static int
2894 listbox_entry_cmp (const void *a, const void *b)
2896 const WLEntry *ea = (const WLEntry *) a;
2897 const WLEntry *eb = (const WLEntry *) b;
2899 return strcmp (ea->text, eb->text);
2902 /* Listbox item adding function */
2903 static inline void
2904 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2906 switch (pos)
2908 case LISTBOX_APPEND_AT_END:
2909 l->list = g_list_append (l->list, e);
2910 break;
2912 case LISTBOX_APPEND_BEFORE:
2913 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2914 if (l->pos > 0)
2915 l->pos--;
2916 break;
2918 case LISTBOX_APPEND_AFTER:
2919 l->list = g_list_insert (l->list, e, l->pos + 1);
2920 break;
2922 case LISTBOX_APPEND_SORTED:
2923 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2924 break;
2926 default:
2927 return;
2930 l->count++;
2933 char *
2934 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2936 WLEntry *entry;
2938 if (l == NULL)
2939 return NULL;
2941 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2942 return NULL;
2944 entry = g_new (WLEntry, 1);
2945 entry->text = g_strdup (text);
2946 entry->data = data;
2947 entry->hotkey = hotkey;
2949 listbox_append_item (l, entry, pos);
2951 return entry->text;
2955 listbox_search_text (WListbox * l, const char *text)
2957 if (l != NULL)
2959 int i;
2960 GList *le;
2962 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2964 WLEntry *e = (WLEntry *) le->data;
2966 if (strcmp (e->text, text) == 0)
2967 return i;
2971 return (-1);
2974 /* Returns the current string text as well as the associated extra data */
2975 void
2976 listbox_get_current (WListbox * l, char **string, void **extra)
2978 WLEntry *e = NULL;
2979 gboolean ok;
2981 if (l != NULL)
2982 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2984 ok = (e != NULL);
2986 if (string != NULL)
2987 *string = ok ? e->text : NULL;
2989 if (extra != NULL)
2990 *extra = ok ? e->data : NULL;
2994 /* ButtonBar widget */
2996 /* returns TRUE if a function has been called, FALSE otherwise. */
2997 static gboolean
2998 buttonbar_call (WButtonBar * bb, int i)
3000 cb_ret_t ret = MSG_NOT_HANDLED;
3002 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
3003 ret = bb->widget.parent->callback (bb->widget.parent,
3004 (Widget *) bb, DLG_ACTION,
3005 bb->labels[i].command, bb->labels[i].receiver);
3006 return ret;
3009 /* calculate width of one button, width is never lesser than 7 */
3010 static int
3011 buttonbat_get_button_width (void)
3013 int result = COLS / BUTTONBAR_LABELS_NUM;
3014 return (result >= 7) ? result : 7;
3017 static cb_ret_t
3018 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
3020 WButtonBar *bb = (WButtonBar *) w;
3021 int i;
3022 const char *text;
3024 switch (msg)
3026 case WIDGET_FOCUS:
3027 return MSG_NOT_HANDLED;
3029 case WIDGET_HOTKEY:
3030 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3031 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
3032 return MSG_HANDLED;
3033 return MSG_NOT_HANDLED;
3035 case WIDGET_DRAW:
3036 if (bb->visible)
3038 int offset = 0;
3039 int count_free_positions;
3041 widget_move (&bb->widget, 0, 0);
3042 tty_setcolor (DEFAULT_COLOR);
3043 bb->btn_width = buttonbat_get_button_width ();
3044 tty_printf ("%-*s", bb->widget.cols, "");
3045 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
3047 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++)
3049 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
3050 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
3051 tty_printf ("%2d", i + 1);
3052 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
3053 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
3054 tty_print_string (str_fit_to_term (text,
3055 bb->btn_width - 2 + (int) (offset <
3056 count_free_positions),
3057 J_LEFT_FIT));
3059 if (count_free_positions != 0 && offset < count_free_positions)
3060 offset++;
3063 return MSG_HANDLED;
3065 case WIDGET_DESTROY:
3066 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3067 g_free (bb->labels[i].text);
3068 return MSG_HANDLED;
3070 default:
3071 return default_proc (msg, parm);
3075 static int
3076 buttonbar_event (Gpm_Event * event, void *data)
3078 WButtonBar *bb = data;
3079 int button;
3081 if (!(event->type & GPM_UP))
3082 return MOU_NORMAL;
3083 if (event->y == 2)
3084 return MOU_NORMAL;
3085 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
3086 if (button < BUTTONBAR_LABELS_NUM)
3087 buttonbar_call (bb, button);
3088 return MOU_NORMAL;
3091 WButtonBar *
3092 buttonbar_new (gboolean visible)
3094 WButtonBar *bb;
3096 bb = g_new0 (WButtonBar, 1);
3098 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
3099 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
3100 bb->visible = visible;
3101 widget_want_hotkey (bb->widget, 1);
3102 widget_want_cursor (bb->widget, 0);
3103 bb->btn_width = buttonbat_get_button_width ();
3105 return bb;
3108 static void
3109 set_label_text (WButtonBar * bb, int lc_index, const char *text)
3111 g_free (bb->labels[lc_index - 1].text);
3112 bb->labels[lc_index - 1].text = g_strdup (text);
3115 /* Find ButtonBar widget in the dialog */
3116 WButtonBar *
3117 find_buttonbar (const Dlg_head * h)
3119 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
3122 void
3123 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
3124 const struct global_keymap_t *keymap, const Widget * receiver)
3126 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
3128 unsigned long command = CK_Ignore_Key;
3130 if (keymap != NULL)
3131 command = lookup_keymap_command (keymap, KEY_F (idx));
3133 if ((text == NULL) || (text[0] == '\0'))
3134 set_label_text (bb, idx, "");
3135 else
3136 set_label_text (bb, idx, text);
3138 bb->labels[idx - 1].command = command;
3139 bb->labels[idx - 1].receiver = (Widget *) receiver;
3143 void
3144 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
3146 bb->visible = visible;
3149 void
3150 buttonbar_redraw (WButtonBar * bb)
3152 if (bb != NULL)
3153 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3156 static cb_ret_t
3157 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3159 WGroupbox *g = (WGroupbox *) w;
3161 switch (msg)
3163 case WIDGET_INIT:
3164 return MSG_HANDLED;
3166 case WIDGET_FOCUS:
3167 return MSG_NOT_HANDLED;
3169 case WIDGET_DRAW:
3170 tty_setcolor (COLOR_NORMAL);
3171 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
3172 g->widget.x - g->widget.parent->x, g->widget.lines, g->widget.cols, TRUE);
3174 tty_setcolor (COLOR_HOT_NORMAL);
3175 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
3176 g->widget.x - g->widget.parent->x + 1);
3177 tty_print_string (g->title);
3178 return MSG_HANDLED;
3180 case WIDGET_DESTROY:
3181 g_free (g->title);
3182 return MSG_HANDLED;
3184 default:
3185 return default_proc (msg, parm);
3189 WGroupbox *
3190 groupbox_new (int y, int x, int height, int width, const char *title)
3192 WGroupbox *g = g_new (WGroupbox, 1);
3194 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3196 g->widget.options &= ~W_WANT_CURSOR;
3197 widget_want_hotkey (g->widget, 0);
3199 /* Strip existing spaces, add one space before and after the title */
3200 if (title)
3202 char *t;
3203 t = g_strstrip (g_strdup (title));
3204 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3205 g_free (t);
3208 return g;