Ticket #2237: Automatic date and version substitution for man pages
[midnight-commander.git] / src / widget.c
blob406eb02a43274073608cd69f54ec52ab51b39111
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 */
64 #include "clipboard.h" /* copy_file_to_ext_clip, paste_to_file_from_ext_clip */
66 static void
67 widget_selectcolor (Widget * w, gboolean focused, gboolean hotkey)
69 Dlg_head *h = w->owner;
70 int color;
72 if ((w->options & W_DISABLED) != 0)
73 color = DISABLED_COLOR;
74 else if (hotkey)
76 if (focused)
77 color = DLG_HOT_FOCUSC (h);
78 else
79 color = DLG_HOT_NORMALC (h);
81 else
83 if (focused)
84 color = DLG_FOCUSC (h);
85 else
86 color = DLG_NORMALC (h);
89 tty_setcolor (color);
92 struct hotkey_t
93 parse_hotkey (const char *text)
95 struct hotkey_t result;
96 const char *cp, *p;
98 /* search for '&', that is not on the of text */
99 cp = strchr (text, '&');
100 if (cp != NULL && cp[1] != '\0')
102 result.start = g_strndup (text, cp - text);
104 /* skip '&' */
105 cp++;
106 p = str_cget_next_char (cp);
107 result.hotkey = g_strndup (cp, p - cp);
109 cp = p;
110 result.end = g_strdup (cp);
112 else
114 result.start = g_strdup (text);
115 result.hotkey = NULL;
116 result.end = NULL;
119 return result;
122 void
123 release_hotkey (const struct hotkey_t hotkey)
125 g_free (hotkey.start);
126 g_free (hotkey.hotkey);
127 g_free (hotkey.end);
131 hotkey_width (const struct hotkey_t hotkey)
133 int result;
135 result = str_term_width1 (hotkey.start);
136 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
137 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
138 return result;
141 static void
142 draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
144 widget_selectcolor (w, focused, FALSE);
145 tty_print_string (hotkey.start);
147 if (hotkey.hotkey != NULL)
149 widget_selectcolor (w, focused, TRUE);
150 tty_print_string (hotkey.hotkey);
151 widget_selectcolor (w, focused, FALSE);
154 if (hotkey.end != NULL)
155 tty_print_string (hotkey.end);
159 /* Default callback for widgets */
160 cb_ret_t
161 default_proc (widget_msg_t msg, int parm)
163 (void) parm;
165 switch (msg)
167 case WIDGET_INIT:
168 case WIDGET_FOCUS:
169 case WIDGET_UNFOCUS:
170 case WIDGET_DRAW:
171 case WIDGET_DESTROY:
172 case WIDGET_CURSOR:
173 case WIDGET_IDLE:
174 return MSG_HANDLED;
176 default:
177 return MSG_NOT_HANDLED;
181 static int button_event (Gpm_Event * event, void *);
183 int quote = 0;
185 static cb_ret_t
186 button_callback (Widget * w, widget_msg_t msg, int parm)
188 WButton *b = (WButton *) w;
189 int stop = 0;
190 int off = 0;
191 Dlg_head *h = b->widget.owner;
193 switch (msg)
195 case WIDGET_HOTKEY:
197 * Don't let the default button steal Enter from the current
198 * button. This is a workaround for the flawed event model
199 * when hotkeys are sent to all widgets before the key is
200 * handled by the current widget.
202 if (parm == '\n' && (Widget *) h->current->data == &b->widget)
204 button_callback (w, WIDGET_KEY, ' ');
205 return MSG_HANDLED;
208 if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
210 button_callback (w, WIDGET_KEY, ' ');
211 return MSG_HANDLED;
214 if (b->text.hotkey != NULL)
216 if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
218 button_callback (w, WIDGET_KEY, ' ');
219 return MSG_HANDLED;
222 return MSG_NOT_HANDLED;
224 case WIDGET_KEY:
225 if (parm != ' ' && parm != '\n')
226 return MSG_NOT_HANDLED;
228 if (b->callback)
229 stop = (*b->callback) (b, b->action);
230 if (!b->callback || stop)
232 h->ret_value = b->action;
233 dlg_stop (h);
235 return MSG_HANDLED;
237 case WIDGET_CURSOR:
238 switch (b->flags)
240 case DEFPUSH_BUTTON:
241 off = 3;
242 break;
243 case NORMAL_BUTTON:
244 off = 2;
245 break;
246 case NARROW_BUTTON:
247 off = 1;
248 break;
249 case HIDDEN_BUTTON:
250 default:
251 off = 0;
252 break;
254 widget_move (&b->widget, 0, b->hotpos + off);
255 return MSG_HANDLED;
257 case WIDGET_UNFOCUS:
258 case WIDGET_FOCUS:
259 case WIDGET_DRAW:
260 if (msg == WIDGET_UNFOCUS)
261 b->selected = 0;
262 else if (msg == WIDGET_FOCUS)
263 b->selected = 1;
265 widget_selectcolor (w, b->selected, FALSE);
266 widget_move (w, 0, 0);
268 switch (b->flags)
270 case DEFPUSH_BUTTON:
271 tty_print_string ("[< ");
272 break;
273 case NORMAL_BUTTON:
274 tty_print_string ("[ ");
275 break;
276 case NARROW_BUTTON:
277 tty_print_string ("[");
278 break;
279 case HIDDEN_BUTTON:
280 default:
281 return MSG_HANDLED;
284 draw_hotkey (w, b->text, b->selected);
286 switch (b->flags)
288 case DEFPUSH_BUTTON:
289 tty_print_string (" >]");
290 break;
291 case NORMAL_BUTTON:
292 tty_print_string (" ]");
293 break;
294 case NARROW_BUTTON:
295 tty_print_string ("]");
296 break;
298 return MSG_HANDLED;
300 case WIDGET_DESTROY:
301 release_hotkey (b->text);
302 return MSG_HANDLED;
304 default:
305 return default_proc (msg, parm);
309 static int
310 button_event (Gpm_Event * event, void *data)
312 WButton *b = data;
314 if (event->type & (GPM_DOWN | GPM_UP))
316 Dlg_head *h = b->widget.owner;
317 dlg_select_widget (b);
318 if (event->type & GPM_UP)
320 button_callback ((Widget *) data, WIDGET_KEY, ' ');
321 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
322 return MOU_NORMAL;
325 return MOU_NORMAL;
329 button_get_len (const WButton * b)
331 int ret = hotkey_width (b->text);
332 switch (b->flags)
334 case DEFPUSH_BUTTON:
335 ret += 6;
336 break;
337 case NORMAL_BUTTON:
338 ret += 4;
339 break;
340 case NARROW_BUTTON:
341 ret += 2;
342 break;
343 case HIDDEN_BUTTON:
344 default:
345 return 0;
347 return ret;
350 WButton *
351 button_new (int y, int x, int action, int flags, const char *text, bcback callback)
353 WButton *b = g_new (WButton, 1);
355 b->action = action;
356 b->flags = flags;
357 b->text = parse_hotkey (text);
359 init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
361 b->selected = 0;
362 b->callback = callback;
363 widget_want_hotkey (b->widget, 1);
364 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
366 return b;
369 const char *
370 button_get_text (const WButton * b)
372 if (b->text.hotkey != NULL)
373 return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
374 else
375 return g_strdup (b->text.start);
378 void
379 button_set_text (WButton * b, const char *text)
381 release_hotkey (b->text);
382 b->text = parse_hotkey (text);
383 b->widget.cols = button_get_len (b);
384 dlg_redraw (b->widget.owner);
388 /* Radio button widget */
389 static int radio_event (Gpm_Event * event, void *);
391 static cb_ret_t
392 radio_callback (Widget * w, widget_msg_t msg, int parm)
394 WRadio *r = (WRadio *) w;
395 int i;
396 Dlg_head *h = r->widget.owner;
398 switch (msg)
400 case WIDGET_HOTKEY:
402 int lp = g_ascii_tolower ((gchar) parm);
404 for (i = 0; i < r->count; i++)
406 if (r->texts[i].hotkey != NULL)
408 int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
410 if (c != lp)
411 continue;
412 r->pos = i;
414 /* Take action */
415 radio_callback (w, WIDGET_KEY, ' ');
416 return MSG_HANDLED;
420 return MSG_NOT_HANDLED;
422 case WIDGET_KEY:
423 switch (parm)
425 case ' ':
426 r->sel = r->pos;
427 h->callback (h, w, DLG_ACTION, 0, NULL);
428 radio_callback (w, WIDGET_FOCUS, ' ');
429 return MSG_HANDLED;
431 case KEY_UP:
432 case KEY_LEFT:
433 if (r->pos > 0)
435 r->pos--;
436 return MSG_HANDLED;
438 return MSG_NOT_HANDLED;
440 case KEY_DOWN:
441 case KEY_RIGHT:
442 if (r->count - 1 > r->pos)
444 r->pos++;
445 return MSG_HANDLED;
448 return MSG_NOT_HANDLED;
450 case WIDGET_CURSOR:
451 h->callback (h, w, DLG_ACTION, 0, NULL);
452 radio_callback (w, WIDGET_FOCUS, ' ');
453 widget_move (&r->widget, r->pos, 1);
454 return MSG_HANDLED;
456 case WIDGET_UNFOCUS:
457 case WIDGET_FOCUS:
458 case WIDGET_DRAW:
459 for (i = 0; i < r->count; i++)
461 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
462 widget_selectcolor (w, focused, FALSE);
463 widget_move (&r->widget, i, 0);
464 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
465 draw_hotkey (w, r->texts[i], focused);
467 return MSG_HANDLED;
469 case WIDGET_DESTROY:
470 for (i = 0; i < r->count; i++)
472 release_hotkey (r->texts[i]);
474 g_free (r->texts);
475 return MSG_HANDLED;
477 default:
478 return default_proc (msg, parm);
482 static int
483 radio_event (Gpm_Event * event, void *data)
485 WRadio *r = data;
486 Widget *w = data;
488 if (event->type & (GPM_DOWN | GPM_UP))
490 Dlg_head *h = r->widget.owner;
492 r->pos = event->y - 1;
493 dlg_select_widget (r);
494 if (event->type & GPM_UP)
496 radio_callback (w, WIDGET_KEY, ' ');
497 radio_callback (w, WIDGET_FOCUS, 0);
498 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
499 return MOU_NORMAL;
502 return MOU_NORMAL;
505 WRadio *
506 radio_new (int y, int x, int count, const char **texts)
508 WRadio *result = g_new (WRadio, 1);
509 int i, max, m;
511 /* Compute the longest string */
512 result->texts = g_new (struct hotkey_t, count);
514 max = 0;
515 for (i = 0; i < count; i++)
517 result->texts[i] = parse_hotkey (texts[i]);
518 m = hotkey_width (result->texts[i]);
519 if (m > max)
520 max = m;
523 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
524 result->state = 1;
525 result->pos = 0;
526 result->sel = 0;
527 result->count = count;
528 widget_want_hotkey (result->widget, 1);
530 return result;
534 /* Checkbutton widget */
536 static int check_event (Gpm_Event * event, void *);
538 static cb_ret_t
539 check_callback (Widget * w, widget_msg_t msg, int parm)
541 WCheck *c = (WCheck *) w;
542 Dlg_head *h = c->widget.owner;
544 switch (msg)
546 case WIDGET_HOTKEY:
547 if (c->text.hotkey != NULL)
549 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
552 check_callback (w, WIDGET_KEY, ' '); /* make action */
553 return MSG_HANDLED;
556 return MSG_NOT_HANDLED;
558 case WIDGET_KEY:
559 if (parm != ' ')
560 return MSG_NOT_HANDLED;
561 c->state ^= C_BOOL;
562 c->state ^= C_CHANGE;
563 h->callback (h, w, DLG_ACTION, 0, NULL);
564 check_callback (w, WIDGET_FOCUS, ' ');
565 return MSG_HANDLED;
567 case WIDGET_CURSOR:
568 widget_move (&c->widget, 0, 1);
569 return MSG_HANDLED;
571 case WIDGET_FOCUS:
572 case WIDGET_UNFOCUS:
573 case WIDGET_DRAW:
574 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
575 widget_move (&c->widget, 0, 0);
576 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
577 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
578 return MSG_HANDLED;
580 case WIDGET_DESTROY:
581 release_hotkey (c->text);
582 return MSG_HANDLED;
584 default:
585 return default_proc (msg, parm);
589 static int
590 check_event (Gpm_Event * event, void *data)
592 WCheck *c = data;
593 Widget *w = data;
595 if (event->type & (GPM_DOWN | GPM_UP))
597 Dlg_head *h = c->widget.owner;
599 dlg_select_widget (c);
600 if (event->type & GPM_UP)
602 check_callback (w, WIDGET_KEY, ' ');
603 check_callback (w, WIDGET_FOCUS, 0);
604 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
605 return MOU_NORMAL;
608 return MOU_NORMAL;
611 WCheck *
612 check_new (int y, int x, int state, const char *text)
614 WCheck *c = g_new (WCheck, 1);
616 c->text = parse_hotkey (text);
618 init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
619 c->state = state ? C_BOOL : 0;
620 widget_want_hotkey (c->widget, 1);
622 return c;
625 static gboolean
626 save_text_to_clip_file (const char *text)
628 int file;
629 char *fname = NULL;
630 ssize_t ret;
631 size_t str_len;
633 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
634 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
635 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
636 g_free (fname);
638 if (file == -1)
639 return FALSE;
641 str_len = strlen (text);
642 ret = mc_write (file, (char *) text, str_len);
643 mc_close (file);
644 return ret == (ssize_t) str_len;
647 static gboolean
648 load_text_from_clip_file (char **text)
650 char buf[BUF_LARGE];
651 FILE *f;
652 char *fname = NULL;
653 gboolean first = TRUE;
655 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
656 f = fopen (fname, "r");
657 g_free (fname);
659 if (f == NULL)
660 return FALSE;
662 *text = NULL;
664 while (fgets (buf, sizeof (buf), f))
666 size_t len;
668 len = strlen (buf);
669 if (len > 0)
671 if (buf[len - 1] == '\n')
672 buf[len - 1] = '\0';
674 if (first)
676 first = FALSE;
677 *text = g_strdup (buf);
679 else
681 /* remove \n on EOL */
682 char *tmp;
684 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
685 g_free (*text);
686 *text = tmp;
691 fclose (f);
693 return (*text != NULL);
696 static gboolean
697 panel_save_curent_file_to_clip_file (void)
699 gboolean res;
701 if (current_panel->marked == 0)
702 res = save_text_to_clip_file (selection (current_panel)->fname);
703 else
705 int i;
706 gboolean first = TRUE;
707 char *flist = NULL;
709 for (i = 0; i < current_panel->count; i++)
710 if (current_panel->dir.list[i].f.marked != 0)
711 { /* Skip the unmarked ones */
712 if (first)
714 flist = g_strdup (current_panel->dir.list[i].fname);
715 first = FALSE;
717 else
719 /* Add empty lines after the file */
720 char *tmp;
722 tmp =
723 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
724 g_free (flist);
725 flist = tmp;
729 if (flist != NULL)
731 res = save_text_to_clip_file (flist);
732 g_free (flist);
735 return res;
738 /* Label widget */
740 static cb_ret_t
741 label_callback (Widget * w, widget_msg_t msg, int parm)
743 WLabel *l = (WLabel *) w;
744 Dlg_head *h = l->widget.owner;
746 switch (msg)
748 case WIDGET_INIT:
749 return MSG_HANDLED;
751 /* We don't want to get the focus */
752 case WIDGET_FOCUS:
753 return MSG_NOT_HANDLED;
755 case WIDGET_DRAW:
757 char *p = l->text, *q, c = 0;
758 int y = 0;
759 gboolean disabled = (w->options & W_DISABLED) != 0;
761 if (!l->text)
762 return MSG_HANDLED;
764 if (l->transparent)
765 tty_setcolor (disabled ? DISABLED_COLOR : DEFAULT_COLOR);
766 else
767 tty_setcolor (disabled ? DISABLED_COLOR : DLG_NORMALC (h));
769 for (;;)
771 q = strchr (p, '\n');
772 if (q != NULL)
774 c = q[0];
775 q[0] = '\0';
778 widget_move (&l->widget, y, 0);
779 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
781 if (q == NULL)
782 break;
783 q[0] = c;
784 p = q + 1;
785 y++;
787 return MSG_HANDLED;
790 case WIDGET_DESTROY:
791 g_free (l->text);
792 return MSG_HANDLED;
794 default:
795 return default_proc (msg, parm);
799 void
800 label_set_text (WLabel * label, const char *text)
802 int newcols = label->widget.cols;
803 int newlines;
805 if (label->text && text && !strcmp (label->text, text))
806 return; /* Flickering is not nice */
808 g_free (label->text);
810 if (text != NULL)
812 label->text = g_strdup (text);
813 if (label->auto_adjust_cols)
815 str_msg_term_size (text, &newlines, &newcols);
816 if (newcols > label->widget.cols)
817 label->widget.cols = newcols;
818 if (newlines > label->widget.lines)
819 label->widget.lines = newlines;
822 else
823 label->text = NULL;
825 if (label->widget.owner)
826 label_callback ((Widget *) label, WIDGET_DRAW, 0);
828 if (newcols < label->widget.cols)
829 label->widget.cols = newcols;
832 WLabel *
833 label_new (int y, int x, const char *text)
835 WLabel *l;
836 int cols = 1;
837 int lines = 1;
839 if (text != NULL)
840 str_msg_term_size (text, &lines, &cols);
842 l = g_new (WLabel, 1);
843 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
844 l->text = (text != NULL) ? g_strdup (text) : NULL;
845 l->auto_adjust_cols = 1;
846 l->transparent = 0;
847 widget_want_cursor (l->widget, 0);
848 return l;
851 static cb_ret_t
852 hline_callback (Widget * w, widget_msg_t msg, int parm)
854 WHLine *l = (WHLine *) w;
855 Dlg_head *h = l->widget.owner;
857 switch (msg)
859 case WIDGET_INIT:
860 case WIDGET_RESIZED:
861 if (l->auto_adjust_cols)
863 if (((w->owner->flags & DLG_COMPACT) != 0))
865 w->x = w->owner->x;
866 w->cols = w->owner->cols;
868 else
870 w->x = w->owner->x + 1;
871 w->cols = w->owner->cols - 2;
875 case WIDGET_FOCUS:
876 /* We don't want to get the focus */
877 return MSG_NOT_HANDLED;
879 case WIDGET_DRAW:
880 if (l->transparent)
881 tty_setcolor (DEFAULT_COLOR);
882 else
883 tty_setcolor (DLG_NORMALC (h));
885 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
887 if (l->auto_adjust_cols)
889 widget_move (w, 0, 0);
890 tty_print_alt_char (ACS_LTEE, FALSE);
891 widget_move (w, 0, w->cols - 1);
892 tty_print_alt_char (ACS_RTEE, FALSE);
894 return MSG_HANDLED;
896 default:
897 return default_proc (msg, parm);
902 WHLine *
903 hline_new (int y, int x, int width)
905 WHLine *l;
906 int cols = width;
907 int lines = 1;
909 l = g_new (WHLine, 1);
910 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
911 l->auto_adjust_cols = (width < 0);
912 l->transparent = FALSE;
913 widget_want_cursor (l->widget, 0);
914 return l;
917 /* Gauge widget (progress indicator) */
918 /* Currently width is hardcoded here for text mode */
919 #define gauge_len 47
921 static cb_ret_t
922 gauge_callback (Widget * w, widget_msg_t msg, int parm)
924 WGauge *g = (WGauge *) w;
925 Dlg_head *h = g->widget.owner;
927 if (msg == WIDGET_INIT)
928 return MSG_HANDLED;
930 /* We don't want to get the focus */
931 if (msg == WIDGET_FOCUS)
932 return MSG_NOT_HANDLED;
934 if (msg == WIDGET_DRAW)
936 widget_move (&g->widget, 0, 0);
937 tty_setcolor (DLG_NORMALC (h));
938 if (!g->shown)
939 tty_printf ("%*s", gauge_len, "");
940 else
942 int percentage, columns;
943 long total = g->max, done = g->current;
945 if (total <= 0 || done < 0)
947 done = 0;
948 total = 100;
950 if (done > total)
951 done = total;
952 while (total > 65535)
954 total /= 256;
955 done /= 256;
957 percentage = (200 * done / total + 1) / 2;
958 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
959 tty_print_char ('[');
960 if (g->from_left_to_right)
962 tty_setcolor (GAUGE_COLOR);
963 tty_printf ("%*s", (int) columns, "");
964 tty_setcolor (DLG_NORMALC (h));
965 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
967 else
969 tty_setcolor (DLG_NORMALC (h));
970 tty_printf ("%*s", gauge_len - columns - 7, "");
971 tty_setcolor (GAUGE_COLOR);
972 tty_printf ("%*s", columns, "");
973 tty_setcolor (DLG_NORMALC (h));
974 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
977 return MSG_HANDLED;
980 return default_proc (msg, parm);
983 void
984 gauge_set_value (WGauge * g, int max, int current)
986 if (g->current == current && g->max == max)
987 return; /* Do not flicker */
988 if (max == 0)
989 max = 1; /* I do not like division by zero :) */
991 g->current = current;
992 g->max = max;
993 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
996 void
997 gauge_show (WGauge * g, int shown)
999 if (g->shown == shown)
1000 return;
1001 g->shown = shown;
1002 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
1005 WGauge *
1006 gauge_new (int y, int x, int shown, int max, int current)
1008 WGauge *g = g_new (WGauge, 1);
1010 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
1011 g->shown = shown;
1012 if (max == 0)
1013 max = 1; /* I do not like division by zero :) */
1014 g->max = max;
1015 g->current = current;
1016 g->from_left_to_right = TRUE;
1017 widget_want_cursor (g->widget, 0);
1018 return g;
1022 /* Input widget */
1024 /* {{{ history button */
1026 #define LARGE_HISTORY_BUTTON 1
1028 #ifdef LARGE_HISTORY_BUTTON
1029 # define HISTORY_BUTTON_WIDTH 3
1030 #else
1031 # define HISTORY_BUTTON_WIDTH 1
1032 #endif
1034 #define should_show_history_button(in) \
1035 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1037 static void
1038 draw_history_button (WInput * in)
1040 char c;
1041 gboolean disabled = (((Widget *) in)->options & W_DISABLED) != 0;
1043 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1044 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1045 #ifdef LARGE_HISTORY_BUTTON
1047 Dlg_head *h;
1048 h = in->widget.owner;
1049 tty_setcolor (disabled ? DISABLED_COLOR : NORMAL_COLOR);
1050 tty_print_string ("[ ]");
1051 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1052 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1053 tty_print_char (c);
1055 #else
1056 tty_setcolor (disabled ? DISABLED_COLOR : MARKED_COLOR);
1057 tty_print_char (c);
1058 #endif
1061 /* }}} history button */
1064 /* Input widgets now have a global kill ring */
1065 /* Pointer to killed data */
1066 static char *kill_buffer = NULL;
1068 static void
1069 input_set_markers (WInput * in, long m1)
1071 in->mark = m1;
1074 static void
1075 input_mark_cmd (WInput * in, gboolean mark)
1077 if (!mark)
1079 in->highlight = FALSE;
1080 input_set_markers (in, 0);
1082 else
1084 in->highlight = TRUE;
1085 input_set_markers (in, in->point);
1089 static gboolean
1090 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
1092 if (in->highlight)
1094 *start_mark = min (in->mark, in->point);
1095 *end_mark = max (in->mark, in->point);
1096 return TRUE;
1098 else
1100 *start_mark = *end_mark = 0;
1101 return FALSE;
1105 static void
1106 delete_region (WInput * in, int x_first, int x_last)
1108 int first = min (x_first, x_last);
1109 int last = max (x_first, x_last);
1110 size_t len;
1112 input_mark_cmd (in, FALSE);
1113 in->point = first;
1114 last = str_offset_to_pos (in->buffer, last);
1115 first = str_offset_to_pos (in->buffer, first);
1116 len = strlen (&in->buffer[last]) + 1;
1117 memmove (&in->buffer[first], &in->buffer[last], len);
1118 in->charpoint = 0;
1119 in->need_push = 1;
1122 void
1123 update_input (WInput * in, int clear_first)
1125 int has_history = 0;
1126 int i;
1127 int buf_len = str_length (in->buffer);
1128 const char *cp;
1129 int pw;
1131 if (should_show_history_button (in))
1132 has_history = HISTORY_BUTTON_WIDTH;
1134 if (in->disable_update)
1135 return;
1137 pw = str_term_width2 (in->buffer, in->point);
1139 /* Make the point visible */
1140 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1143 in->term_first_shown = pw - (in->field_width / 3);
1144 if (in->term_first_shown < 0)
1145 in->term_first_shown = 0;
1148 /* Adjust the mark */
1149 if (in->mark > buf_len)
1150 in->mark = buf_len;
1152 if (has_history)
1153 draw_history_button (in);
1155 if ((((Widget *) in)->options & W_DISABLED) != 0)
1156 tty_setcolor (DISABLED_COLOR);
1157 else if (in->first)
1158 tty_setcolor (in->unchanged_color);
1159 else
1160 tty_setcolor (in->color);
1162 widget_move (&in->widget, 0, 0);
1164 if (!in->is_password)
1166 if (!in->highlight)
1168 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1169 in->field_width - has_history));
1171 else
1173 long m1, m2;
1174 if (input_eval_marks (in, &m1, &m2))
1176 tty_setcolor (in->color);
1177 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1178 tty_print_string (cp);
1179 tty_setcolor (in->mark_color);
1180 if (m1 < in->term_first_shown)
1182 widget_move (&in->widget, 0, 0);
1183 tty_print_string (str_term_substring (in->buffer, in->term_first_shown, m2 - in->term_first_shown));
1185 else
1187 int sel_width;
1188 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1189 sel_width = min (m2 - m1, (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) - in->term_first_shown));
1190 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1195 else
1197 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1198 for (i = 0; i < in->field_width - has_history; i++)
1200 if (i >= 0)
1202 tty_setcolor (in->color);
1203 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1205 if (cp[0] != '\0')
1206 str_cnext_char (&cp);
1210 if (clear_first)
1211 in->first = FALSE;
1214 void
1215 winput_set_origin (WInput * in, int x, int field_width)
1217 in->widget.x = x;
1218 in->field_width = in->widget.cols = field_width;
1219 update_input (in, 0);
1222 /* {{{ history saving and loading */
1224 int num_history_items_recorded = 60;
1227 This loads and saves the history of an input line to and from the
1228 widget. It is called with the widgets history name on creation of the
1229 widget, and returns the GList list. It stores histories in the file
1230 ~/.mc/history in using the profile code.
1232 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1233 function) then input_new assigns the default text to be the last text
1234 entered, or "" if not found.
1237 GList *
1238 history_get (const char *input_name)
1240 size_t i;
1241 GList *hist = NULL;
1242 char *profile;
1243 mc_config_t *cfg;
1244 char **keys;
1245 size_t keys_num = 0;
1246 char *this_entry;
1248 if (num_history_items_recorded == 0) /* this is how to disable */
1249 return NULL;
1250 if ((input_name == NULL) || (*input_name == '\0'))
1251 return NULL;
1253 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1254 cfg = mc_config_init (profile);
1256 /* get number of keys */
1257 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1258 g_strfreev (keys);
1260 for (i = 0; i < keys_num; i++)
1262 char key[BUF_TINY];
1263 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1264 this_entry = mc_config_get_string (cfg, input_name, key, "");
1266 if (this_entry != NULL)
1267 hist = list_append_unique (hist, this_entry);
1270 mc_config_deinit (cfg);
1271 g_free (profile);
1273 /* return pointer to the last entry in the list */
1274 return g_list_last (hist);
1277 void
1278 history_put (const char *input_name, GList * h)
1280 int i;
1281 char *profile;
1282 mc_config_t *cfg;
1284 if (num_history_items_recorded == 0) /* this is how to disable */
1285 return;
1286 if ((input_name == NULL) || (*input_name == '\0'))
1287 return;
1288 if (h == NULL)
1289 return;
1291 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1293 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1294 if (i != -1)
1295 close (i);
1297 /* Make sure the history is only readable by the user */
1298 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1300 g_free (profile);
1301 return;
1304 /* go to end of list */
1305 h = g_list_last (h);
1307 /* go back 60 places */
1308 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1309 h = g_list_previous (h);
1311 cfg = mc_config_init (profile);
1313 if (input_name != NULL)
1314 mc_config_del_group (cfg, input_name);
1316 /* dump history into profile */
1317 for (i = 0; h != NULL; h = g_list_next (h))
1319 char *text = (char *) h->data;
1321 /* We shouldn't have null entries, but let's be sure */
1322 if (text != NULL)
1324 char key[BUF_TINY];
1325 g_snprintf (key, sizeof (key), "%d", i++);
1326 mc_config_set_string (cfg, input_name, key, text);
1330 mc_config_save_file (cfg, NULL);
1331 mc_config_deinit (cfg);
1332 g_free (profile);
1335 /* }}} history saving and loading */
1338 /* {{{ history display */
1340 static const char *
1341 i18n_htitle (void)
1343 return _("History");
1346 typedef struct
1348 Widget *widget;
1349 size_t count;
1350 size_t maxlen;
1351 } dlg_hist_data;
1353 static cb_ret_t
1354 dlg_hist_reposition (Dlg_head * dlg_head)
1356 dlg_hist_data *data;
1357 int x = 0, y, he, wi;
1359 /* guard checks */
1360 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1361 return MSG_NOT_HANDLED;
1363 data = (dlg_hist_data *) dlg_head->data;
1365 y = data->widget->y;
1366 he = data->count + 2;
1368 if (he <= y || y > (LINES - 6))
1370 he = min (he, y - 1);
1371 y -= he;
1373 else
1375 y++;
1376 he = min (he, LINES - y);
1379 if (data->widget->x > 2)
1380 x = data->widget->x - 2;
1382 wi = data->maxlen + 4;
1384 if ((wi + x) > COLS)
1386 wi = min (wi, COLS);
1387 x = COLS - wi;
1390 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1392 return MSG_HANDLED;
1395 static cb_ret_t
1396 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1398 switch (msg)
1400 case DLG_RESIZE:
1401 return dlg_hist_reposition (h);
1403 default:
1404 return default_dlg_callback (h, sender, msg, parm, data);
1408 char *
1409 show_hist (GList ** history, Widget * widget)
1411 GList *z, *hlist = NULL, *hi;
1412 size_t maxlen, i, count = 0;
1413 char *r = NULL;
1414 Dlg_head *query_dlg;
1415 WListbox *query_list;
1416 dlg_hist_data hist_data;
1418 if (*history == NULL)
1419 return NULL;
1421 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1423 for (z = *history; z != NULL; z = g_list_previous (z))
1425 WLEntry *entry;
1427 i = str_term_width1 ((char *) z->data);
1428 maxlen = max (maxlen, i);
1429 count++;
1431 entry = g_new0 (WLEntry, 1);
1432 /* history is being reverted here */
1433 entry->text = g_strdup ((char *) z->data);
1434 hlist = g_list_prepend (hlist, entry);
1437 hist_data.widget = widget;
1438 hist_data.count = count;
1439 hist_data.maxlen = maxlen;
1441 query_dlg =
1442 create_dlg (TRUE, 0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1443 "[History-query]", i18n_htitle (), DLG_COMPACT);
1444 query_dlg->data = &hist_data;
1446 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1448 /* this call makes list stick to all sides of dialog, effectively make
1449 it be resized with dialog */
1450 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1452 /* to avoid diplicating of (calculating sizes in two places)
1453 code, call dlg_hist_callback function here, to set dialog and
1454 controls positions.
1455 The main idea - create 4x4 dialog and add 2x2 list in
1456 center of it, and let dialog function resize it to needed
1457 size. */
1458 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1460 if (query_dlg->y < widget->y)
1462 /* draw list entries from bottom upto top */
1463 listbox_set_list (query_list, hlist);
1464 listbox_select_last (query_list);
1466 else
1468 /* draw list entries from top downto bottom */
1469 /* revert history direction */
1470 hlist = g_list_reverse (hlist);
1471 listbox_set_list (query_list, hlist);
1474 if (run_dlg (query_dlg) != B_CANCEL)
1476 char *q;
1478 listbox_get_current (query_list, &q, NULL);
1479 if (q != NULL)
1480 r = g_strdup (q);
1483 /* get modified history from dialog */
1484 z = NULL;
1485 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1487 WLEntry *entry;
1489 entry = (WLEntry *) hi->data;
1490 /* history is being reverted here again */
1491 z = g_list_prepend (z, entry->text);
1492 entry->text = NULL;
1495 /* restore history direction */
1496 if (query_dlg->y < widget->y)
1497 z = g_list_reverse (z);
1499 destroy_dlg (query_dlg);
1501 g_list_foreach (*history, (GFunc) g_free, NULL);
1502 g_list_free (*history);
1503 *history = g_list_last (z);
1505 return r;
1508 static void
1509 do_show_hist (WInput * in)
1511 char *r;
1513 r = show_hist (&in->history, &in->widget);
1514 if (r != NULL)
1516 assign_text (in, r);
1517 g_free (r);
1521 /* }}} history display */
1523 static void
1524 input_destroy (WInput * in)
1526 if (in == NULL)
1528 fprintf (stderr, "Internal error: null Input *\n");
1529 exit (EXIT_FAILURE);
1532 new_input (in);
1534 if (in->history != NULL)
1536 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
1537 history_put (in->history_name, in->history);
1539 in->history = g_list_first (in->history);
1540 g_list_foreach (in->history, (GFunc) g_free, NULL);
1541 g_list_free (in->history);
1544 g_free (in->buffer);
1545 free_completions (in);
1546 g_free (in->history_name);
1548 g_free (kill_buffer);
1549 kill_buffer = NULL;
1552 void
1553 input_disable_update (WInput * in)
1555 in->disable_update++;
1558 void
1559 input_enable_update (WInput * in)
1561 in->disable_update--;
1562 update_input (in, 0);
1566 static void
1567 push_history (WInput * in, const char *text)
1569 /* input widget where urls with passwords are entered without any
1570 vfs prefix */
1571 const char *password_input_fields[] = {
1572 " Link to a remote machine ",
1573 " FTP to machine ",
1574 " SMB link to machine "
1576 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1578 char *t;
1579 size_t i;
1580 gboolean empty;
1582 if (text == NULL)
1583 return;
1585 #ifdef ENABLE_NLS
1586 for (i = 0; i < ELEMENTS; i++)
1587 password_input_fields[i] = _(password_input_fields[i]);
1588 #endif
1590 t = g_strstrip (g_strdup (text));
1591 empty = *t == '\0';
1592 g_free (t);
1593 t = g_strdup (empty ? "" : text);
1595 if (in->history_name != NULL)
1597 /* FIXME: It is the strange code. Rewrite is needed. */
1599 const char *p = in->history_name + 3;
1601 for (i = 0; i < ELEMENTS; i++)
1602 if (strcmp (p, password_input_fields[i]) == 0)
1603 break;
1605 strip_password (t, i >= ELEMENTS);
1608 in->history = list_append_unique (in->history, t);
1609 in->need_push = 0;
1612 /* Cleans the input line and adds the current text to the history */
1613 void
1614 new_input (WInput * in)
1616 push_history (in, in->buffer);
1617 in->need_push = 1;
1618 in->buffer[0] = '\0';
1619 in->point = 0;
1620 in->charpoint = 0;
1621 in->mark = 0;
1622 in->highlight = FALSE;
1623 free_completions (in);
1624 update_input (in, 0);
1627 static void
1628 move_buffer_backward (WInput * in, int start, int end)
1630 int i, pos, len;
1631 int str_len = str_length (in->buffer);
1632 if (start >= str_len || end > str_len + 1)
1633 return;
1635 pos = str_offset_to_pos (in->buffer, start);
1636 len = str_offset_to_pos (in->buffer, end) - pos;
1638 for (i = pos; in->buffer[i + len - 1]; i++)
1639 in->buffer[i] = in->buffer[i + len];
1642 static cb_ret_t
1643 insert_char (WInput * in, int c_code)
1645 size_t i;
1646 int res;
1648 if (in->highlight)
1650 long m1, m2;
1651 if (input_eval_marks (in, &m1, &m2))
1652 delete_region (in, m1, m2);
1654 if (c_code == -1)
1655 return MSG_NOT_HANDLED;
1657 if (in->charpoint >= MB_LEN_MAX)
1658 return MSG_HANDLED;
1660 in->charbuf[in->charpoint] = c_code;
1661 in->charpoint++;
1663 res = str_is_valid_char (in->charbuf, in->charpoint);
1664 if (res < 0)
1666 if (res != -2)
1667 in->charpoint = 0; /* broken multibyte char, skip */
1668 return MSG_HANDLED;
1671 in->need_push = 1;
1672 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1674 /* Expand the buffer */
1675 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1676 char *narea = g_try_renew (char, in->buffer, new_length);
1677 if (narea)
1679 in->buffer = narea;
1680 in->current_max_size = new_length;
1684 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1686 /* bytes from begin */
1687 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1688 /* move chars */
1689 size_t rest_bytes = strlen (in->buffer + ins_point);
1691 for (i = rest_bytes + 1; i > 0; i--)
1692 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1694 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1695 in->point++;
1698 in->charpoint = 0;
1699 return MSG_HANDLED;
1702 static void
1703 beginning_of_line (WInput * in)
1705 in->point = 0;
1706 in->charpoint = 0;
1709 static void
1710 end_of_line (WInput * in)
1712 in->point = str_length (in->buffer);
1713 in->charpoint = 0;
1716 static void
1717 backward_char (WInput * in)
1719 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1721 if (in->point > 0)
1723 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1725 in->charpoint = 0;
1728 static void
1729 forward_char (WInput * in)
1731 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1732 if (act[0] != '\0')
1734 in->point += str_cnext_noncomb_char (&act);
1736 in->charpoint = 0;
1739 static void
1740 forward_word (WInput * in)
1742 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1744 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1746 str_cnext_char (&p);
1747 in->point++;
1749 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1751 str_cnext_char (&p);
1752 in->point++;
1756 static void
1757 backward_word (WInput * in)
1759 const char *p;
1760 const char *p_tmp;
1762 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1763 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1765 while (p != in->buffer)
1767 p_tmp = p;
1768 str_cprev_char (&p);
1769 if (!str_isspace (p) && !str_ispunct (p))
1771 p = p_tmp;
1772 break;
1774 in->point--;
1776 while (p != in->buffer)
1778 str_cprev_char (&p);
1779 if (str_isspace (p) || str_ispunct (p))
1780 break;
1782 in->point--;
1786 static void
1787 key_left (WInput * in)
1789 backward_char (in);
1792 static void
1793 key_ctrl_left (WInput * in)
1795 backward_word (in);
1798 static void
1799 key_right (WInput * in)
1801 forward_char (in);
1804 static void
1805 key_ctrl_right (WInput * in)
1807 forward_word (in);
1809 static void
1810 backward_delete (WInput * in)
1812 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1813 int start;
1815 if (in->point == 0)
1816 return;
1818 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1819 move_buffer_backward (in, start, in->point);
1820 in->charpoint = 0;
1821 in->need_push = 1;
1822 in->point = start;
1825 static void
1826 delete_char (WInput * in)
1828 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1829 int end = in->point;
1831 end += str_cnext_noncomb_char (&act);
1833 move_buffer_backward (in, in->point, end);
1834 in->charpoint = 0;
1835 in->need_push = 1;
1838 static void
1839 copy_region (WInput * in, int x_first, int x_last)
1841 int first = min (x_first, x_last);
1842 int last = max (x_first, x_last);
1844 if (last == first)
1846 /* Copy selected files to clipboard */
1847 panel_save_curent_file_to_clip_file ();
1848 /* try use external clipboard utility */
1849 copy_file_to_ext_clip ();
1850 return;
1853 g_free (kill_buffer);
1855 first = str_offset_to_pos (in->buffer, first);
1856 last = str_offset_to_pos (in->buffer, last);
1858 kill_buffer = g_strndup (in->buffer + first, last - first);
1860 save_text_to_clip_file (kill_buffer);
1861 /* try use external clipboard utility */
1862 copy_file_to_ext_clip ();
1865 static void
1866 kill_word (WInput * in)
1868 int old_point = in->point;
1869 int new_point;
1871 forward_word (in);
1872 new_point = in->point;
1873 in->point = old_point;
1875 copy_region (in, old_point, new_point);
1876 delete_region (in, old_point, new_point);
1877 in->need_push = 1;
1878 in->charpoint = 0;
1879 in->charpoint = 0;
1882 static void
1883 back_kill_word (WInput * in)
1885 int old_point = in->point;
1886 int new_point;
1888 backward_word (in);
1889 new_point = in->point;
1890 in->point = old_point;
1892 copy_region (in, old_point, new_point);
1893 delete_region (in, old_point, new_point);
1894 in->need_push = 1;
1897 static void
1898 set_mark (WInput * in)
1900 input_mark_cmd (in, TRUE);
1903 static void
1904 kill_save (WInput * in)
1906 copy_region (in, in->mark, in->point);
1909 static void
1910 kill_region (WInput * in)
1912 kill_save (in);
1913 delete_region (in, in->point, in->mark);
1916 static void
1917 clear_region (WInput * in)
1919 delete_region (in, in->point, in->mark);
1922 static void
1923 yank (WInput * in)
1925 if (kill_buffer != NULL)
1927 char *p;
1929 in->charpoint = 0;
1930 for (p = kill_buffer; *p != '\0'; p++)
1931 insert_char (in, *p);
1932 in->charpoint = 0;
1936 static void
1937 kill_line (WInput * in)
1939 int chp = str_offset_to_pos (in->buffer, in->point);
1940 g_free (kill_buffer);
1941 kill_buffer = g_strdup (&in->buffer[chp]);
1942 in->buffer[chp] = '\0';
1943 in->charpoint = 0;
1946 static void
1947 ins_from_clip (WInput * in)
1949 char *p = NULL;
1951 /* try use external clipboard utility */
1952 paste_to_file_from_ext_clip ();
1954 if (load_text_from_clip_file (&p))
1956 char *pp;
1958 for (pp = p; *pp != '\0'; pp++)
1959 insert_char (in, *pp);
1961 g_free (p);
1965 void
1966 assign_text (WInput * in, const char *text)
1968 free_completions (in);
1969 g_free (in->buffer);
1970 in->buffer = g_strdup (text); /* was in->buffer->text */
1971 in->current_max_size = strlen (in->buffer) + 1;
1972 in->point = str_length (in->buffer);
1973 in->mark = 0;
1974 in->need_push = 1;
1975 in->charpoint = 0;
1978 static void
1979 hist_prev (WInput * in)
1981 GList *prev;
1983 if (!in->history)
1984 return;
1986 if (in->need_push)
1987 push_history (in, in->buffer);
1989 prev = g_list_previous (in->history);
1990 if (prev != NULL)
1992 in->history = prev;
1993 assign_text (in, (char *) prev->data);
1994 in->need_push = 0;
1998 static void
1999 hist_next (WInput * in)
2001 if (in->need_push)
2003 push_history (in, in->buffer);
2004 assign_text (in, "");
2005 return;
2008 if (!in->history)
2009 return;
2011 if (!in->history->next)
2013 assign_text (in, "");
2014 return;
2017 in->history = g_list_next (in->history);
2018 assign_text (in, (char *) in->history->data);
2019 in->need_push = 0;
2022 static void
2023 port_region_marked_for_delete (WInput * in)
2025 in->buffer[0] = '\0';
2026 in->point = 0;
2027 in->first = FALSE;
2028 in->charpoint = 0;
2031 static cb_ret_t
2032 input_execute_cmd (WInput * in, unsigned long command)
2034 cb_ret_t res = MSG_HANDLED;
2036 /* a highlight command like shift-arrow */
2037 if (command == CK_InputLeftHighlight ||
2038 command == CK_InputRightHighlight ||
2039 command == CK_InputWordLeftHighlight ||
2040 command == CK_InputWordRightHighlight ||
2041 command == CK_InputBolHighlight ||
2042 command == CK_InputEolHighlight)
2044 if (!in->highlight)
2046 input_mark_cmd (in, FALSE); /* clear */
2047 input_mark_cmd (in, TRUE); /* marking on */
2051 switch (command)
2053 case CK_InputForwardWord:
2054 case CK_InputBackwardWord:
2055 case CK_InputForwardChar:
2056 case CK_InputBackwardChar:
2057 if (in->highlight)
2058 input_mark_cmd (in, FALSE);
2061 switch (command)
2063 case CK_InputBol:
2064 case CK_InputBolHighlight:
2065 beginning_of_line (in);
2066 break;
2067 case CK_InputEol:
2068 case CK_InputEolHighlight:
2069 end_of_line (in);
2070 break;
2071 case CK_InputMoveLeft:
2072 case CK_InputLeftHighlight:
2073 key_left (in);
2074 break;
2075 case CK_InputWordLeft:
2076 case CK_InputWordLeftHighlight:
2077 key_ctrl_left (in);
2078 break;
2079 case CK_InputMoveRight:
2080 case CK_InputRightHighlight:
2081 key_right (in);
2082 break;
2083 case CK_InputWordRight:
2084 case CK_InputWordRightHighlight:
2085 key_ctrl_right (in);
2086 break;
2087 case CK_InputBackwardChar:
2088 backward_char (in);
2089 break;
2090 case CK_InputBackwardWord:
2091 backward_word (in);
2092 break;
2093 case CK_InputForwardChar:
2094 forward_char (in);
2095 break;
2096 case CK_InputForwardWord:
2097 forward_word (in);
2098 break;
2099 case CK_InputBackwardDelete:
2100 if (in->highlight)
2102 long m1, m2;
2103 if (input_eval_marks (in, &m1, &m2))
2104 delete_region (in, m1, m2);
2106 else
2108 backward_delete (in);
2110 break;
2111 case CK_InputDeleteChar:
2112 if (in->first)
2113 port_region_marked_for_delete (in);
2114 else if (in->highlight)
2116 long m1, m2;
2117 if (input_eval_marks (in, &m1, &m2))
2118 delete_region (in, m1, m2);
2120 else
2121 delete_char (in);
2122 break;
2123 case CK_InputKillWord:
2124 kill_word (in);
2125 break;
2126 case CK_InputBackwardKillWord:
2127 back_kill_word (in);
2128 break;
2129 case CK_InputSetMark:
2130 set_mark (in);
2131 break;
2132 case CK_InputKillRegion:
2133 kill_region (in);
2134 break;
2135 case CK_InputClearLine:
2136 clear_region (in);
2137 break;
2138 case CK_InputKillSave:
2139 kill_save (in);
2140 break;
2141 case CK_InputYank:
2142 yank (in);
2143 break;
2144 case CK_InputPaste:
2145 ins_from_clip (in);
2146 break;
2147 case CK_InputKillLine:
2148 kill_line (in);
2149 break;
2150 case CK_InputHistoryPrev:
2151 hist_prev (in);
2152 break;
2153 case CK_InputHistoryNext:
2154 hist_next (in);
2155 break;
2156 case CK_InputHistoryShow:
2157 do_show_hist (in);
2158 break;
2159 case CK_InputComplete:
2160 complete (in);
2161 break;
2162 default:
2163 res = MSG_NOT_HANDLED;
2166 if (command != CK_InputLeftHighlight &&
2167 command != CK_InputRightHighlight &&
2168 command != CK_InputWordLeftHighlight &&
2169 command != CK_InputWordRightHighlight &&
2170 command != CK_InputBolHighlight &&
2171 command != CK_InputEolHighlight)
2173 in->highlight = FALSE;
2176 return res;
2179 /* This function is a test for a special input key used in complete.c */
2180 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2181 and 2 if it is a complete key */
2183 is_in_input_map (WInput * in, int key)
2185 size_t i;
2186 for (i = 0; input_map[i].key != 0; i++)
2187 if (key == input_map[i].key)
2189 input_execute_cmd (in, input_map[i].command);
2190 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2192 return 0;
2195 cb_ret_t
2196 handle_char (WInput * in, int key)
2198 cb_ret_t v;
2199 int i;
2201 v = MSG_NOT_HANDLED;
2203 if (quote)
2205 free_completions (in);
2206 v = insert_char (in, key);
2207 update_input (in, 1);
2208 quote = 0;
2209 return v;
2211 for (i = 0; input_map[i].key; i++)
2213 if (key == input_map[i].key)
2215 if (input_map[i].command != CK_InputComplete)
2216 free_completions (in);
2217 input_execute_cmd (in, input_map[i].command);
2218 update_input (in, 1);
2219 v = MSG_HANDLED;
2220 break;
2223 if (input_map[i].command == CK_Ignore_Key)
2225 if (key > 255)
2226 return MSG_NOT_HANDLED;
2227 if (in->first)
2228 port_region_marked_for_delete (in);
2229 free_completions (in);
2230 v = insert_char (in, key);
2232 update_input (in, 1);
2233 return v;
2236 /* Inserts text in input line */
2237 void
2238 stuff (WInput * in, const char *text, int insert_extra_space)
2240 input_disable_update (in);
2241 while (*text != '\0')
2242 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2243 if (insert_extra_space)
2244 handle_char (in, ' ');
2245 input_enable_update (in);
2246 update_input (in, 1);
2249 void
2250 input_set_point (WInput * in, int pos)
2252 int max_pos = str_length (in->buffer);
2254 if (pos > max_pos)
2255 pos = max_pos;
2256 if (pos != in->point)
2257 free_completions (in);
2258 in->point = pos;
2259 in->charpoint = 0;
2260 update_input (in, 1);
2263 cb_ret_t
2264 input_callback (Widget * w, widget_msg_t msg, int parm)
2266 WInput *in = (WInput *) w;
2267 cb_ret_t v;
2269 switch (msg)
2271 case WIDGET_KEY:
2272 if (parm == XCTRL ('q'))
2274 quote = 1;
2275 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2276 quote = 0;
2277 return v;
2280 /* Keys we want others to handle */
2281 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2282 || parm == KEY_F (10) || parm == '\n')
2283 return MSG_NOT_HANDLED;
2285 /* When pasting multiline text, insert literal Enter */
2286 if ((parm & ~KEY_M_MASK) == '\n')
2288 quote = 1;
2289 v = handle_char (in, '\n');
2290 quote = 0;
2291 return v;
2294 return handle_char (in, parm);
2296 case WIDGET_COMMAND:
2297 return input_execute_cmd (in, parm);
2299 case WIDGET_FOCUS:
2300 case WIDGET_UNFOCUS:
2301 case WIDGET_DRAW:
2302 update_input (in, 0);
2303 return MSG_HANDLED;
2305 case WIDGET_CURSOR:
2306 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2307 - in->term_first_shown);
2308 return MSG_HANDLED;
2310 case WIDGET_DESTROY:
2311 input_destroy (in);
2312 return MSG_HANDLED;
2314 default:
2315 return default_proc (msg, parm);
2319 static int
2320 input_event (Gpm_Event * event, void *data)
2322 WInput *in = data;
2324 if (event->type & GPM_DOWN)
2326 in->first = FALSE;
2327 input_mark_cmd (in, FALSE);
2329 if (event->type & (GPM_DOWN | GPM_DRAG))
2331 dlg_select_widget (in);
2333 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2334 && should_show_history_button (in))
2336 do_show_hist (in);
2338 else
2340 in->point = str_length (in->buffer);
2341 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2342 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2344 update_input (in, 1);
2346 /* A lone up mustn't do anything */
2347 if (in->highlight && event->type & (GPM_UP | GPM_DRAG))
2348 return MOU_NORMAL;
2350 if (!(event->type & GPM_DRAG))
2351 input_mark_cmd (in, TRUE);
2353 return MOU_NORMAL;
2356 WInput *
2357 input_new (int y, int x, int *input_colors, int width, const char *def_text,
2358 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2360 WInput *in = g_new (WInput, 1);
2361 size_t initial_buffer_len;
2363 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2365 /* history setup */
2366 in->history_name = NULL;
2367 in->history = NULL;
2368 if ((histname != NULL) && (*histname != '\0'))
2370 in->history_name = g_strdup (histname);
2371 in->history = history_get (histname);
2374 if (def_text == NULL)
2375 def_text = "";
2376 else if (def_text == INPUT_LAST_TEXT)
2378 if ((in->history != NULL) && (in->history->data != NULL))
2379 def_text = (char *) in->history->data;
2380 else
2381 def_text = "";
2384 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2385 in->widget.options |= W_IS_INPUT;
2386 in->completions = NULL;
2387 in->completion_flags = completion_flags;
2388 in->current_max_size = initial_buffer_len;
2389 in->buffer = g_new (char, initial_buffer_len);
2390 in->color = input_colors[0];
2391 in->unchanged_color = input_colors[1];
2392 in->mark_color = input_colors[2];
2393 in->field_width = width;
2394 in->first = TRUE;
2395 in->highlight = FALSE;
2396 in->term_first_shown = 0;
2397 in->disable_update = 0;
2398 in->mark = 0;
2399 in->need_push = 1;
2400 in->is_password = 0;
2402 strcpy (in->buffer, def_text);
2403 in->point = str_length (in->buffer);
2404 in->charpoint = 0;
2406 return in;
2410 /* Listbox widget */
2412 /* Should draw the scrollbar, but currently draws only
2413 * indications that there is more information
2416 static void
2417 listbox_entry_free (void *data)
2419 WLEntry *e = data;
2420 g_free (e->text);
2421 g_free (e);
2424 static void
2425 listbox_drawscroll (WListbox * l)
2427 const int max_line = l->widget.lines - 1;
2428 int line = 0;
2429 int i;
2431 /* Are we at the top? */
2432 widget_move (&l->widget, 0, l->widget.cols);
2433 if (l->top == 0)
2434 tty_print_one_vline (TRUE);
2435 else
2436 tty_print_char ('^');
2438 /* Are we at the bottom? */
2439 widget_move (&l->widget, max_line, l->widget.cols);
2440 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2441 tty_print_one_vline (TRUE);
2442 else
2443 tty_print_char ('v');
2445 /* Now draw the nice relative pointer */
2446 if (l->count != 0)
2447 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2449 for (i = 1; i < max_line; i++)
2451 widget_move (&l->widget, i, l->widget.cols);
2452 if (i != line)
2453 tty_print_one_vline (TRUE);
2454 else
2455 tty_print_char ('*');
2459 static void
2460 listbox_draw (WListbox * l, gboolean focused)
2462 const Dlg_head *h = l->widget.owner;
2463 const gboolean disabled = (((Widget *) l)->options & W_DISABLED) != 0;
2464 const int normalc = disabled ? DISABLED_COLOR : DLG_NORMALC (h);
2465 int selc = disabled ? DISABLED_COLOR : focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2467 GList *le;
2468 int pos;
2469 int i;
2470 int sel_line = -1;
2472 le = g_list_nth (l->list, l->top);
2473 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2474 pos = (le == NULL) ? 0 : l->top;
2476 for (i = 0; i < l->widget.lines; i++)
2478 const char *text;
2480 /* Display the entry */
2481 if (pos == l->pos && sel_line == -1)
2483 sel_line = i;
2484 tty_setcolor (selc);
2486 else
2487 tty_setcolor (normalc);
2489 widget_move (&l->widget, i, 1);
2491 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2492 text = "";
2493 else
2495 WLEntry *e = (WLEntry *) le->data;
2496 text = e->text;
2497 le = g_list_next (le);
2498 pos++;
2501 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2504 l->cursor_y = sel_line;
2506 if (l->scrollbar && (l->count > l->widget.lines))
2508 tty_setcolor (normalc);
2509 listbox_drawscroll (l);
2513 static int
2514 listbox_check_hotkey (WListbox * l, int key)
2516 int i;
2517 GList *le;
2519 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2521 WLEntry *e = (WLEntry *) le->data;
2523 if (e->hotkey == key)
2524 return i;
2527 return (-1);
2530 /* Selects the last entry and scrolls the list to the bottom */
2531 void
2532 listbox_select_last (WListbox * l)
2534 l->pos = l->count - 1;
2535 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2538 /* Selects the first entry and scrolls the list to the top */
2539 void
2540 listbox_select_first (WListbox * l)
2542 l->pos = l->top = 0;
2545 void
2546 listbox_set_list (WListbox * l, GList * list)
2548 listbox_remove_list (l);
2550 if (l != NULL)
2552 l->list = list;
2553 l->top = l->pos = 0;
2554 l->count = g_list_length (list);
2558 void
2559 listbox_remove_list (WListbox * l)
2561 if ((l != NULL) && (l->count != 0))
2563 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2564 g_list_free (l->list);
2565 l->list = NULL;
2566 l->count = l->pos = l->top = 0;
2570 void
2571 listbox_remove_current (WListbox * l)
2573 if ((l != NULL) && (l->count != 0))
2575 GList *current;
2577 current = g_list_nth (l->list, l->pos);
2578 l->list = g_list_remove_link (l->list, current);
2579 listbox_entry_free ((WLEntry *) current->data);
2580 g_list_free_1 (current);
2581 l->count--;
2583 if (l->count == 0)
2584 l->top = l->pos = 0;
2585 else if (l->pos >= l->count)
2586 l->pos = l->count - 1;
2590 void
2591 listbox_select_entry (WListbox * l, int dest)
2593 GList *le;
2594 int pos;
2595 gboolean top_seen = FALSE;
2597 if (dest < 0)
2598 return;
2600 /* Special case */
2601 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2603 if (pos == l->top)
2604 top_seen = TRUE;
2606 if (pos == dest)
2608 l->pos = dest;
2609 if (!top_seen)
2610 l->top = l->pos;
2611 else if (l->pos - l->top >= l->widget.lines)
2612 l->top = l->pos - l->widget.lines + 1;
2613 return;
2617 /* If we are unable to find it, set decent values */
2618 l->pos = l->top = 0;
2621 /* Selects from base the pos element */
2622 static int
2623 listbox_select_pos (WListbox * l, int base, int pos)
2625 int last = l->count - 1;
2627 base += pos;
2628 if (base >= last)
2629 base = last;
2631 return base;
2634 static void
2635 listbox_fwd (WListbox * l)
2637 if (l->pos + 1 >= l->count)
2638 listbox_select_first (l);
2639 else
2640 listbox_select_entry (l, l->pos + 1);
2643 static void
2644 listbox_back (WListbox * l)
2646 if (l->pos <= 0)
2647 listbox_select_last (l);
2648 else
2649 listbox_select_entry (l, l->pos - 1);
2652 static cb_ret_t
2653 listbox_execute_cmd (WListbox * l, unsigned long command)
2655 cb_ret_t ret = MSG_HANDLED;
2656 int i;
2658 switch (command)
2660 case CK_ListboxMoveUp:
2661 listbox_back (l);
2662 break;
2663 case CK_ListboxMoveDown:
2664 listbox_fwd (l);
2665 break;
2666 case CK_ListboxMoveHome:
2667 listbox_select_first (l);
2668 break;
2669 case CK_ListboxMoveEnd:
2670 listbox_select_last (l);
2671 break;
2672 case CK_ListboxMovePgUp:
2673 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2674 listbox_back (l);
2675 break;
2676 case CK_ListboxMovePgDn:
2677 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2678 listbox_fwd (l);
2679 break;
2680 case CK_ListboxDeleteItem:
2681 if (l->deletable)
2683 gboolean is_last = (l->pos + 1 >= l->count);
2684 gboolean is_more = (l->top + l->widget.lines >= l->count);
2686 listbox_remove_current (l);
2687 if ((l->top > 0) && (is_last || is_more))
2688 l->top--;
2690 break;
2691 case CK_ListboxDeleteAll:
2692 if (l->deletable && confirm_history_cleanup
2693 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2694 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2695 _("Do you want clean this history?"),
2696 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2697 listbox_remove_list (l);
2698 break;
2699 default:
2700 ret = MSG_NOT_HANDLED;
2703 return ret;
2706 /* Return MSG_HANDLED if we want a redraw */
2707 static cb_ret_t
2708 listbox_key (WListbox * l, int key)
2710 unsigned long command;
2712 if (l->list == NULL)
2713 return MSG_NOT_HANDLED;
2715 /* focus on listbox item N by '0'..'9' keys */
2716 if (key >= '0' && key <= '9')
2718 int oldpos = l->pos;
2719 listbox_select_entry (l, key - '0');
2721 /* need scroll to item? */
2722 if (abs (oldpos - l->pos) > l->widget.lines)
2723 l->top = l->pos;
2725 return MSG_HANDLED;
2728 command = lookup_keymap_command (listbox_map, key);
2729 if (command == CK_Ignore_Key)
2730 return MSG_NOT_HANDLED;
2731 return listbox_execute_cmd (l, command);
2734 static inline void
2735 listbox_destroy (WListbox * l)
2737 listbox_remove_list (l);
2740 static cb_ret_t
2741 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2743 WListbox *l = (WListbox *) w;
2744 Dlg_head *h = l->widget.owner;
2745 cb_ret_t ret_code;
2747 switch (msg)
2749 case WIDGET_INIT:
2750 return MSG_HANDLED;
2752 case WIDGET_HOTKEY:
2754 int pos, action;
2756 pos = listbox_check_hotkey (l, parm);
2757 if (pos < 0)
2758 return MSG_NOT_HANDLED;
2760 listbox_select_entry (l, pos);
2761 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2763 if (l->cback != NULL)
2764 action = l->cback (l);
2765 else
2766 action = LISTBOX_DONE;
2768 if (action == LISTBOX_DONE)
2770 h->ret_value = B_ENTER;
2771 dlg_stop (h);
2774 return MSG_HANDLED;
2777 case WIDGET_KEY:
2778 ret_code = listbox_key (l, parm);
2779 if (ret_code != MSG_NOT_HANDLED)
2781 listbox_draw (l, TRUE);
2782 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2784 return ret_code;
2786 case WIDGET_COMMAND:
2787 return listbox_execute_cmd (l, parm);
2789 case WIDGET_CURSOR:
2790 widget_move (&l->widget, l->cursor_y, 0);
2791 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2792 return MSG_HANDLED;
2794 case WIDGET_FOCUS:
2795 case WIDGET_UNFOCUS:
2796 case WIDGET_DRAW:
2797 listbox_draw (l, msg != WIDGET_UNFOCUS);
2798 return MSG_HANDLED;
2800 case WIDGET_DESTROY:
2801 listbox_destroy (l);
2802 return MSG_HANDLED;
2804 case WIDGET_RESIZED:
2805 return MSG_HANDLED;
2807 default:
2808 return default_proc (msg, parm);
2812 static int
2813 listbox_event (Gpm_Event * event, void *data)
2815 WListbox *l = data;
2816 int i;
2818 Dlg_head *h = l->widget.owner;
2820 /* Single click */
2821 if (event->type & GPM_DOWN)
2822 dlg_select_widget (l);
2824 if (l->list == NULL)
2825 return MOU_NORMAL;
2827 if (event->type & (GPM_DOWN | GPM_DRAG))
2829 int ret = MOU_REPEAT;
2831 if (event->x < 0 || event->x > l->widget.cols)
2832 return ret;
2834 if (event->y < 1)
2835 for (i = -event->y; i >= 0; i--)
2836 listbox_back (l);
2837 else if (event->y > l->widget.lines)
2838 for (i = event->y - l->widget.lines; i > 0; i--)
2839 listbox_fwd (l);
2840 else if (event->buttons & GPM_B_UP)
2842 listbox_back (l);
2843 ret = MOU_NORMAL;
2845 else if (event->buttons & GPM_B_DOWN)
2847 listbox_fwd (l);
2848 ret = MOU_NORMAL;
2850 else
2851 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2853 /* We need to refresh ourselves since the dialog manager doesn't */
2854 /* know about this event */
2855 listbox_draw (l, TRUE);
2856 return ret;
2859 /* Double click */
2860 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2862 int action;
2864 if (event->x < 0 || event->x >= l->widget.cols
2865 || event->y < 1 || event->y > l->widget.lines)
2866 return MOU_NORMAL;
2868 dlg_select_widget (l);
2869 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2871 if (l->cback != NULL)
2872 action = l->cback (l);
2873 else
2874 action = LISTBOX_DONE;
2876 if (action == LISTBOX_DONE)
2878 h->ret_value = B_ENTER;
2879 dlg_stop (h);
2880 return MOU_NORMAL;
2883 return MOU_NORMAL;
2886 WListbox *
2887 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2889 WListbox *l = g_new (WListbox, 1);
2891 if (height <= 0)
2892 height = 1;
2894 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2896 l->list = NULL;
2897 l->top = l->pos = 0;
2898 l->count = 0;
2899 l->deletable = deletable;
2900 l->cback = callback;
2901 l->allow_duplicates = TRUE;
2902 l->scrollbar = !tty_is_slow ();
2903 widget_want_hotkey (l->widget, 1);
2904 widget_want_cursor (l->widget, 0);
2906 return l;
2909 static int
2910 listbox_entry_cmp (const void *a, const void *b)
2912 const WLEntry *ea = (const WLEntry *) a;
2913 const WLEntry *eb = (const WLEntry *) b;
2915 return strcmp (ea->text, eb->text);
2918 /* Listbox item adding function */
2919 static inline void
2920 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2922 switch (pos)
2924 case LISTBOX_APPEND_AT_END:
2925 l->list = g_list_append (l->list, e);
2926 break;
2928 case LISTBOX_APPEND_BEFORE:
2929 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2930 if (l->pos > 0)
2931 l->pos--;
2932 break;
2934 case LISTBOX_APPEND_AFTER:
2935 l->list = g_list_insert (l->list, e, l->pos + 1);
2936 break;
2938 case LISTBOX_APPEND_SORTED:
2939 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2940 break;
2942 default:
2943 return;
2946 l->count++;
2949 char *
2950 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2952 WLEntry *entry;
2954 if (l == NULL)
2955 return NULL;
2957 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2958 return NULL;
2960 entry = g_new (WLEntry, 1);
2961 entry->text = g_strdup (text);
2962 entry->data = data;
2963 entry->hotkey = hotkey;
2965 listbox_append_item (l, entry, pos);
2967 return entry->text;
2971 listbox_search_text (WListbox * l, const char *text)
2973 if (l != NULL)
2975 int i;
2976 GList *le;
2978 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2980 WLEntry *e = (WLEntry *) le->data;
2982 if (strcmp (e->text, text) == 0)
2983 return i;
2987 return (-1);
2990 /* Returns the current string text as well as the associated extra data */
2991 void
2992 listbox_get_current (WListbox * l, char **string, void **extra)
2994 WLEntry *e = NULL;
2995 gboolean ok;
2997 if (l != NULL)
2998 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
3000 ok = (e != NULL);
3002 if (string != NULL)
3003 *string = ok ? e->text : NULL;
3005 if (extra != NULL)
3006 *extra = ok ? e->data : NULL;
3010 /* ButtonBar widget */
3012 /* returns TRUE if a function has been called, FALSE otherwise. */
3013 static gboolean
3014 buttonbar_call (WButtonBar * bb, int i)
3016 cb_ret_t ret = MSG_NOT_HANDLED;
3018 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
3019 ret = bb->widget.owner->callback (bb->widget.owner,
3020 (Widget *) bb, DLG_ACTION,
3021 bb->labels[i].command, bb->labels[i].receiver);
3022 return ret;
3025 /* calculate positions of buttons; width is never less than 7 */
3026 static void
3027 buttonbar_init_button_positions (WButtonBar *bb)
3029 int i;
3030 int pos = 0;
3032 if (COLS < BUTTONBAR_LABELS_NUM * 7)
3034 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3036 if (pos + 7 <= COLS)
3037 pos += 7;
3039 bb->labels[i].end_coord = pos;
3042 else
3044 /* Distribute the extra width in a way that the middle vertical line
3045 (between F5 and F6) aligns with the two panels. The extra width
3046 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3047 int lc_div, mod;
3049 lc_div = COLS / BUTTONBAR_LABELS_NUM;
3050 mod = COLS % BUTTONBAR_LABELS_NUM;
3052 for (i = 0; i < BUTTONBAR_LABELS_NUM / 2; i++)
3054 pos += lc_div;
3055 if (BUTTONBAR_LABELS_NUM / 2 - 1 - i < mod / 2)
3056 pos++;
3058 bb->labels[i].end_coord = pos;
3061 for (; i < BUTTONBAR_LABELS_NUM; i++)
3063 pos += lc_div;
3064 if (BUTTONBAR_LABELS_NUM - 1 - i < (mod + 1) / 2)
3065 pos++;
3067 bb->labels[i].end_coord = pos;
3072 /* return width of one button */
3073 static int
3074 buttonbar_get_button_width (const WButtonBar *bb, int i)
3076 if (i == 0)
3077 return bb->labels[0].end_coord;
3078 return bb->labels[i].end_coord - bb->labels[i - 1].end_coord;
3081 static int
3082 buttonbar_get_button_by_x_coord (const WButtonBar *bb, int x)
3084 int i;
3086 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3087 if (bb->labels[i].end_coord > x)
3088 return i;
3090 return (-1);
3093 static cb_ret_t
3094 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
3096 WButtonBar *bb = (WButtonBar *) w;
3097 int i;
3098 const char *text;
3100 switch (msg)
3102 case WIDGET_FOCUS:
3103 return MSG_NOT_HANDLED;
3105 case WIDGET_HOTKEY:
3106 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3107 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
3108 return MSG_HANDLED;
3109 return MSG_NOT_HANDLED;
3111 case WIDGET_DRAW:
3112 if (bb->visible)
3114 buttonbar_init_button_positions (bb);
3115 widget_move (&bb->widget, 0, 0);
3116 tty_setcolor (DEFAULT_COLOR);
3117 tty_printf ("%-*s", bb->widget.cols, "");
3118 widget_move (&bb->widget, 0, 0);
3120 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3122 int width;
3124 width = buttonbar_get_button_width (bb, i);
3125 if (width <= 0)
3126 break;
3128 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
3129 /* don't show num of undefined button */
3130 if ((bb->labels[i].text == NULL) || (bb->labels[i].text[0] == '\0'))
3131 tty_print_string (" ");
3132 else
3133 tty_printf ("%2d", i + 1);
3135 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
3136 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
3137 tty_print_string (str_fit_to_term (text, width - 2, J_LEFT_FIT));
3140 return MSG_HANDLED;
3142 case WIDGET_DESTROY:
3143 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3144 g_free (bb->labels[i].text);
3145 return MSG_HANDLED;
3147 default:
3148 return default_proc (msg, parm);
3152 static int
3153 buttonbar_event (Gpm_Event * event, void *data)
3155 WButtonBar *bb = data;
3156 int button;
3158 if (!(event->type & GPM_UP))
3159 return MOU_NORMAL;
3160 if (event->y == 2)
3161 return MOU_NORMAL;
3162 button = buttonbar_get_button_by_x_coord (bb, event->x - 1);
3163 if (button >= 0)
3164 buttonbar_call (bb, button);
3165 return MOU_NORMAL;
3168 WButtonBar *
3169 buttonbar_new (gboolean visible)
3171 WButtonBar *bb;
3173 bb = g_new0 (WButtonBar, 1);
3175 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
3176 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
3177 bb->visible = visible;
3178 widget_want_hotkey (bb->widget, 1);
3179 widget_want_cursor (bb->widget, 0);
3181 return bb;
3184 static void
3185 set_label_text (WButtonBar * bb, int lc_index, const char *text)
3187 g_free (bb->labels[lc_index - 1].text);
3188 bb->labels[lc_index - 1].text = g_strdup (text);
3191 /* Find ButtonBar widget in the dialog */
3192 WButtonBar *
3193 find_buttonbar (const Dlg_head * h)
3195 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
3198 void
3199 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
3200 const struct global_keymap_t *keymap, const Widget * receiver)
3202 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
3204 unsigned long command = CK_Ignore_Key;
3206 if (keymap != NULL)
3207 command = lookup_keymap_command (keymap, KEY_F (idx));
3209 if ((text == NULL) || (text[0] == '\0'))
3210 set_label_text (bb, idx, "");
3211 else
3212 set_label_text (bb, idx, text);
3214 bb->labels[idx - 1].command = command;
3215 bb->labels[idx - 1].receiver = (Widget *) receiver;
3219 void
3220 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
3222 bb->visible = visible;
3225 void
3226 buttonbar_redraw (WButtonBar * bb)
3228 if (bb != NULL)
3229 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3232 static cb_ret_t
3233 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3235 WGroupbox *g = (WGroupbox *) w;
3237 switch (msg)
3239 case WIDGET_INIT:
3240 return MSG_HANDLED;
3242 case WIDGET_FOCUS:
3243 return MSG_NOT_HANDLED;
3245 case WIDGET_DRAW:
3247 gboolean disabled = (w->options & W_DISABLED) != 0;
3248 tty_setcolor (disabled ? DISABLED_COLOR : COLOR_NORMAL);
3249 draw_box (g->widget.owner, g->widget.y - g->widget.owner->y,
3250 g->widget.x - g->widget.owner->x, g->widget.lines, g->widget.cols, TRUE);
3252 tty_setcolor (disabled ? DISABLED_COLOR : COLOR_HOT_NORMAL);
3253 dlg_move (g->widget.owner, g->widget.y - g->widget.owner->y,
3254 g->widget.x - g->widget.owner->x + 1);
3255 tty_print_string (g->title);
3256 return MSG_HANDLED;
3259 case WIDGET_DESTROY:
3260 g_free (g->title);
3261 return MSG_HANDLED;
3263 default:
3264 return default_proc (msg, parm);
3268 WGroupbox *
3269 groupbox_new (int y, int x, int height, int width, const char *title)
3271 WGroupbox *g = g_new (WGroupbox, 1);
3273 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3275 g->widget.options &= ~W_WANT_CURSOR;
3276 widget_want_hotkey (g->widget, 0);
3278 /* Strip existing spaces, add one space before and after the title */
3279 if (title)
3281 char *t;
3282 t = g_strstrip (g_strdup (title));
3283 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3284 g_free (t);
3287 return g;