Merge branch '2153_wrong_selection_fix'
[midnight-commander/osp/pkrayzel.git] / src / widget.c
blob5ef99169e78d2f73324610409a55776ac5f855fc
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;
71 tty_setcolor (hotkey
72 ? (focused
73 ? DLG_HOT_FOCUSC (h)
74 : DLG_HOT_NORMALC (h)) : (focused ? DLG_FOCUSC (h) : DLG_NORMALC (h)));
77 struct hotkey_t
78 parse_hotkey (const char *text)
80 struct hotkey_t result;
81 const char *cp, *p;
83 /* search for '&', that is not on the of text */
84 cp = strchr (text, '&');
85 if (cp != NULL && cp[1] != '\0')
87 result.start = g_strndup (text, cp - text);
89 /* skip '&' */
90 cp++;
91 p = str_cget_next_char (cp);
92 result.hotkey = g_strndup (cp, p - cp);
94 cp = p;
95 result.end = g_strdup (cp);
97 else
99 result.start = g_strdup (text);
100 result.hotkey = NULL;
101 result.end = NULL;
104 return result;
107 void
108 release_hotkey (const struct hotkey_t hotkey)
110 g_free (hotkey.start);
111 g_free (hotkey.hotkey);
112 g_free (hotkey.end);
116 hotkey_width (const struct hotkey_t hotkey)
118 int result;
120 result = str_term_width1 (hotkey.start);
121 result += (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
122 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
123 return result;
126 static void
127 draw_hotkey (Widget * w, const struct hotkey_t hotkey, gboolean focused)
129 widget_selectcolor (w, focused, FALSE);
130 tty_print_string (hotkey.start);
132 if (hotkey.hotkey != NULL)
134 widget_selectcolor (w, focused, TRUE);
135 tty_print_string (hotkey.hotkey);
136 widget_selectcolor (w, focused, FALSE);
139 if (hotkey.end != NULL)
140 tty_print_string (hotkey.end);
144 /* Default callback for widgets */
145 cb_ret_t
146 default_proc (widget_msg_t msg, int parm)
148 (void) parm;
150 switch (msg)
152 case WIDGET_INIT:
153 case WIDGET_FOCUS:
154 case WIDGET_UNFOCUS:
155 case WIDGET_DRAW:
156 case WIDGET_DESTROY:
157 case WIDGET_CURSOR:
158 case WIDGET_IDLE:
159 return MSG_HANDLED;
161 default:
162 return MSG_NOT_HANDLED;
166 static int button_event (Gpm_Event * event, void *);
168 int quote = 0;
170 static cb_ret_t
171 button_callback (Widget * w, widget_msg_t msg, int parm)
173 WButton *b = (WButton *) w;
174 int stop = 0;
175 int off = 0;
176 Dlg_head *h = b->widget.owner;
178 switch (msg)
180 case WIDGET_HOTKEY:
182 * Don't let the default button steal Enter from the current
183 * button. This is a workaround for the flawed event model
184 * when hotkeys are sent to all widgets before the key is
185 * handled by the current widget.
187 if (parm == '\n' && (Widget *) h->current->data == &b->widget)
189 button_callback (w, WIDGET_KEY, ' ');
190 return MSG_HANDLED;
193 if (parm == '\n' && b->flags == DEFPUSH_BUTTON)
195 button_callback (w, WIDGET_KEY, ' ');
196 return MSG_HANDLED;
199 if (b->text.hotkey != NULL)
201 if (g_ascii_tolower ((gchar) b->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
203 button_callback (w, WIDGET_KEY, ' ');
204 return MSG_HANDLED;
207 return MSG_NOT_HANDLED;
209 case WIDGET_KEY:
210 if (parm != ' ' && parm != '\n')
211 return MSG_NOT_HANDLED;
213 if (b->callback)
214 stop = (*b->callback) (b->action);
215 if (!b->callback || stop)
217 h->ret_value = b->action;
218 dlg_stop (h);
220 return MSG_HANDLED;
222 case WIDGET_CURSOR:
223 switch (b->flags)
225 case DEFPUSH_BUTTON:
226 off = 3;
227 break;
228 case NORMAL_BUTTON:
229 off = 2;
230 break;
231 case NARROW_BUTTON:
232 off = 1;
233 break;
234 case HIDDEN_BUTTON:
235 default:
236 off = 0;
237 break;
239 widget_move (&b->widget, 0, b->hotpos + off);
240 return MSG_HANDLED;
242 case WIDGET_UNFOCUS:
243 case WIDGET_FOCUS:
244 case WIDGET_DRAW:
245 if (msg == WIDGET_UNFOCUS)
246 b->selected = 0;
247 else if (msg == WIDGET_FOCUS)
248 b->selected = 1;
250 widget_selectcolor (w, b->selected, FALSE);
251 widget_move (w, 0, 0);
253 switch (b->flags)
255 case DEFPUSH_BUTTON:
256 tty_print_string ("[< ");
257 break;
258 case NORMAL_BUTTON:
259 tty_print_string ("[ ");
260 break;
261 case NARROW_BUTTON:
262 tty_print_string ("[");
263 break;
264 case HIDDEN_BUTTON:
265 default:
266 return MSG_HANDLED;
269 draw_hotkey (w, b->text, b->selected);
271 switch (b->flags)
273 case DEFPUSH_BUTTON:
274 tty_print_string (" >]");
275 break;
276 case NORMAL_BUTTON:
277 tty_print_string (" ]");
278 break;
279 case NARROW_BUTTON:
280 tty_print_string ("]");
281 break;
283 return MSG_HANDLED;
285 case WIDGET_DESTROY:
286 release_hotkey (b->text);
287 return MSG_HANDLED;
289 default:
290 return default_proc (msg, parm);
294 static int
295 button_event (Gpm_Event * event, void *data)
297 WButton *b = data;
299 if (event->type & (GPM_DOWN | GPM_UP))
301 Dlg_head *h = b->widget.owner;
302 dlg_select_widget (b);
303 if (event->type & GPM_UP)
305 button_callback ((Widget *) data, WIDGET_KEY, ' ');
306 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
307 return MOU_NORMAL;
310 return MOU_NORMAL;
314 button_get_len (const WButton * b)
316 int ret = hotkey_width (b->text);
317 switch (b->flags)
319 case DEFPUSH_BUTTON:
320 ret += 6;
321 break;
322 case NORMAL_BUTTON:
323 ret += 4;
324 break;
325 case NARROW_BUTTON:
326 ret += 2;
327 break;
328 case HIDDEN_BUTTON:
329 default:
330 return 0;
332 return ret;
335 WButton *
336 button_new (int y, int x, int action, int flags, const char *text, bcback callback)
338 WButton *b = g_new (WButton, 1);
340 b->action = action;
341 b->flags = flags;
342 b->text = parse_hotkey (text);
344 init_widget (&b->widget, y, x, 1, button_get_len (b), button_callback, button_event);
346 b->selected = 0;
347 b->callback = callback;
348 widget_want_hotkey (b->widget, 1);
349 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
351 return b;
354 const char *
355 button_get_text (const WButton * b)
357 if (b->text.hotkey != NULL)
358 return g_strconcat (b->text.start, "&", b->text.hotkey, b->text.end, (char *) NULL);
359 else
360 return g_strdup (b->text.start);
363 void
364 button_set_text (WButton * b, const char *text)
366 release_hotkey (b->text);
367 b->text = parse_hotkey (text);
368 b->widget.cols = button_get_len (b);
369 dlg_redraw (b->widget.owner);
373 /* Radio button widget */
374 static int radio_event (Gpm_Event * event, void *);
376 static cb_ret_t
377 radio_callback (Widget * w, widget_msg_t msg, int parm)
379 WRadio *r = (WRadio *) w;
380 int i;
381 Dlg_head *h = r->widget.owner;
383 switch (msg)
385 case WIDGET_HOTKEY:
387 int lp = g_ascii_tolower ((gchar) parm);
389 for (i = 0; i < r->count; i++)
391 if (r->texts[i].hotkey != NULL)
393 int c = g_ascii_tolower ((gchar) r->texts[i].hotkey[0]);
395 if (c != lp)
396 continue;
397 r->pos = i;
399 /* Take action */
400 radio_callback (w, WIDGET_KEY, ' ');
401 return MSG_HANDLED;
405 return MSG_NOT_HANDLED;
407 case WIDGET_KEY:
408 switch (parm)
410 case ' ':
411 r->sel = r->pos;
412 h->callback (h, w, DLG_ACTION, 0, NULL);
413 radio_callback (w, WIDGET_FOCUS, ' ');
414 return MSG_HANDLED;
416 case KEY_UP:
417 case KEY_LEFT:
418 if (r->pos > 0)
420 r->pos--;
421 return MSG_HANDLED;
423 return MSG_NOT_HANDLED;
425 case KEY_DOWN:
426 case KEY_RIGHT:
427 if (r->count - 1 > r->pos)
429 r->pos++;
430 return MSG_HANDLED;
433 return MSG_NOT_HANDLED;
435 case WIDGET_CURSOR:
436 h->callback (h, w, DLG_ACTION, 0, NULL);
437 radio_callback (w, WIDGET_FOCUS, ' ');
438 widget_move (&r->widget, r->pos, 1);
439 return MSG_HANDLED;
441 case WIDGET_UNFOCUS:
442 case WIDGET_FOCUS:
443 case WIDGET_DRAW:
444 for (i = 0; i < r->count; i++)
446 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
447 widget_selectcolor (w, focused, FALSE);
448 widget_move (&r->widget, i, 0);
449 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
450 draw_hotkey (w, r->texts[i], focused);
452 return MSG_HANDLED;
454 case WIDGET_DESTROY:
455 for (i = 0; i < r->count; i++)
457 release_hotkey (r->texts[i]);
459 g_free (r->texts);
460 return MSG_HANDLED;
462 default:
463 return default_proc (msg, parm);
467 static int
468 radio_event (Gpm_Event * event, void *data)
470 WRadio *r = data;
471 Widget *w = data;
473 if (event->type & (GPM_DOWN | GPM_UP))
475 Dlg_head *h = r->widget.owner;
477 r->pos = event->y - 1;
478 dlg_select_widget (r);
479 if (event->type & GPM_UP)
481 radio_callback (w, WIDGET_KEY, ' ');
482 radio_callback (w, WIDGET_FOCUS, 0);
483 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
484 return MOU_NORMAL;
487 return MOU_NORMAL;
490 WRadio *
491 radio_new (int y, int x, int count, const char **texts)
493 WRadio *result = g_new (WRadio, 1);
494 int i, max, m;
496 /* Compute the longest string */
497 result->texts = g_new (struct hotkey_t, count);
499 max = 0;
500 for (i = 0; i < count; i++)
502 result->texts[i] = parse_hotkey (texts[i]);
503 m = hotkey_width (result->texts[i]);
504 if (m > max)
505 max = m;
508 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
509 result->state = 1;
510 result->pos = 0;
511 result->sel = 0;
512 result->count = count;
513 widget_want_hotkey (result->widget, 1);
515 return result;
519 /* Checkbutton widget */
521 static int check_event (Gpm_Event * event, void *);
523 static cb_ret_t
524 check_callback (Widget * w, widget_msg_t msg, int parm)
526 WCheck *c = (WCheck *) w;
527 Dlg_head *h = c->widget.owner;
529 switch (msg)
531 case WIDGET_HOTKEY:
532 if (c->text.hotkey != NULL)
534 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
537 check_callback (w, WIDGET_KEY, ' '); /* make action */
538 return MSG_HANDLED;
541 return MSG_NOT_HANDLED;
543 case WIDGET_KEY:
544 if (parm != ' ')
545 return MSG_NOT_HANDLED;
546 c->state ^= C_BOOL;
547 c->state ^= C_CHANGE;
548 h->callback (h, w, DLG_ACTION, 0, NULL);
549 check_callback (w, WIDGET_FOCUS, ' ');
550 return MSG_HANDLED;
552 case WIDGET_CURSOR:
553 widget_move (&c->widget, 0, 1);
554 return MSG_HANDLED;
556 case WIDGET_FOCUS:
557 case WIDGET_UNFOCUS:
558 case WIDGET_DRAW:
559 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
560 widget_move (&c->widget, 0, 0);
561 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
562 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
563 return MSG_HANDLED;
565 case WIDGET_DESTROY:
566 release_hotkey (c->text);
567 return MSG_HANDLED;
569 default:
570 return default_proc (msg, parm);
574 static int
575 check_event (Gpm_Event * event, void *data)
577 WCheck *c = data;
578 Widget *w = data;
580 if (event->type & (GPM_DOWN | GPM_UP))
582 Dlg_head *h = c->widget.owner;
584 dlg_select_widget (c);
585 if (event->type & GPM_UP)
587 check_callback (w, WIDGET_KEY, ' ');
588 check_callback (w, WIDGET_FOCUS, 0);
589 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
590 return MOU_NORMAL;
593 return MOU_NORMAL;
596 WCheck *
597 check_new (int y, int x, int state, const char *text)
599 WCheck *c = g_new (WCheck, 1);
601 c->text = parse_hotkey (text);
603 init_widget (&c->widget, y, x, 1, hotkey_width (c->text), check_callback, check_event);
604 c->state = state ? C_BOOL : 0;
605 widget_want_hotkey (c->widget, 1);
607 return c;
610 static gboolean
611 save_text_to_clip_file (const char *text)
613 int file;
614 char *fname = NULL;
615 ssize_t ret;
616 size_t str_len;
618 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
619 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
620 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
621 g_free (fname);
623 if (file == -1)
624 return FALSE;
626 str_len = strlen (text);
627 ret = mc_write (file, (char *) text, str_len);
628 mc_close (file);
629 return ret == (ssize_t) str_len;
632 static gboolean
633 load_text_from_clip_file (char **text)
635 char buf[BUF_LARGE];
636 FILE *f;
637 char *fname = NULL;
638 gboolean first = TRUE;
640 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
641 f = fopen (fname, "r");
642 g_free (fname);
644 if (f == NULL)
645 return FALSE;
647 *text = NULL;
649 while (fgets (buf, sizeof (buf), f))
651 size_t len;
653 len = strlen (buf);
654 if (len > 0)
656 if (buf[len - 1] == '\n')
657 buf[len - 1] = '\0';
659 if (first)
661 first = FALSE;
662 *text = g_strdup (buf);
664 else
666 /* remove \n on EOL */
667 char *tmp;
669 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
670 g_free (*text);
671 *text = tmp;
676 fclose (f);
678 return (*text != NULL);
681 static gboolean
682 panel_save_curent_file_to_clip_file (void)
684 gboolean res;
686 if (current_panel->marked == 0)
687 res = save_text_to_clip_file (selection (current_panel)->fname);
688 else
690 int i;
691 gboolean first = TRUE;
692 char *flist = NULL;
694 for (i = 0; i < current_panel->count; i++)
695 if (current_panel->dir.list[i].f.marked != 0)
696 { /* Skip the unmarked ones */
697 if (first)
699 flist = g_strdup (current_panel->dir.list[i].fname);
700 first = FALSE;
702 else
704 /* Add empty lines after the file */
705 char *tmp;
707 tmp =
708 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
709 g_free (flist);
710 flist = tmp;
714 if (flist != NULL)
716 res = save_text_to_clip_file (flist);
717 g_free (flist);
720 return res;
723 /* Label widget */
725 static cb_ret_t
726 label_callback (Widget * w, widget_msg_t msg, int parm)
728 WLabel *l = (WLabel *) w;
729 Dlg_head *h = l->widget.owner;
731 switch (msg)
733 case WIDGET_INIT:
734 return MSG_HANDLED;
736 /* We don't want to get the focus */
737 case WIDGET_FOCUS:
738 return MSG_NOT_HANDLED;
740 case WIDGET_DRAW:
742 char *p = l->text, *q, c = 0;
743 int y = 0;
745 if (!l->text)
746 return MSG_HANDLED;
748 if (l->transparent)
749 tty_setcolor (DEFAULT_COLOR);
750 else
751 tty_setcolor (DLG_NORMALC (h));
753 for (;;)
755 q = strchr (p, '\n');
756 if (q != NULL)
758 c = q[0];
759 q[0] = '\0';
762 widget_move (&l->widget, y, 0);
763 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
765 if (q == NULL)
766 break;
767 q[0] = c;
768 p = q + 1;
769 y++;
771 return MSG_HANDLED;
774 case WIDGET_DESTROY:
775 g_free (l->text);
776 return MSG_HANDLED;
778 default:
779 return default_proc (msg, parm);
783 void
784 label_set_text (WLabel * label, const char *text)
786 int newcols = label->widget.cols;
787 int newlines;
789 if (label->text && text && !strcmp (label->text, text))
790 return; /* Flickering is not nice */
792 g_free (label->text);
794 if (text != NULL)
796 label->text = g_strdup (text);
797 if (label->auto_adjust_cols)
799 str_msg_term_size (text, &newlines, &newcols);
800 if (newcols > label->widget.cols)
801 label->widget.cols = newcols;
802 if (newlines > label->widget.lines)
803 label->widget.lines = newlines;
806 else
807 label->text = NULL;
809 if (label->widget.owner)
810 label_callback ((Widget *) label, WIDGET_DRAW, 0);
812 if (newcols < label->widget.cols)
813 label->widget.cols = newcols;
816 WLabel *
817 label_new (int y, int x, const char *text)
819 WLabel *l;
820 int cols = 1;
821 int lines = 1;
823 if (text != NULL)
824 str_msg_term_size (text, &lines, &cols);
826 l = g_new (WLabel, 1);
827 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
828 l->text = (text != NULL) ? g_strdup (text) : NULL;
829 l->auto_adjust_cols = 1;
830 l->transparent = 0;
831 widget_want_cursor (l->widget, 0);
832 return l;
835 static cb_ret_t
836 hline_callback (Widget * w, widget_msg_t msg, int parm)
838 WHLine *l = (WHLine *) w;
839 Dlg_head *h = l->widget.owner;
841 switch (msg)
843 case WIDGET_INIT:
844 case WIDGET_RESIZED:
845 if (l->auto_adjust_cols)
847 if (((w->owner->flags & DLG_COMPACT) != 0))
849 w->x = w->owner->x;
850 w->cols = w->owner->cols;
852 else
854 w->x = w->owner->x + 1;
855 w->cols = w->owner->cols - 2;
859 case WIDGET_FOCUS:
860 /* We don't want to get the focus */
861 return MSG_NOT_HANDLED;
863 case WIDGET_DRAW:
864 if (l->transparent)
865 tty_setcolor (DEFAULT_COLOR);
866 else
867 tty_setcolor (DLG_NORMALC (h));
869 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
871 if (l->auto_adjust_cols)
873 widget_move (w, 0, 0);
874 tty_print_alt_char (ACS_LTEE, FALSE);
875 widget_move (w, 0, w->cols - 1);
876 tty_print_alt_char (ACS_RTEE, FALSE);
878 return MSG_HANDLED;
880 default:
881 return default_proc (msg, parm);
886 WHLine *
887 hline_new (int y, int x, int width)
889 WHLine *l;
890 int cols = width;
891 int lines = 1;
893 l = g_new (WHLine, 1);
894 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
895 l->auto_adjust_cols = (width < 0);
896 l->transparent = FALSE;
897 widget_want_cursor (l->widget, 0);
898 return l;
901 /* Gauge widget (progress indicator) */
902 /* Currently width is hardcoded here for text mode */
903 #define gauge_len 47
905 static cb_ret_t
906 gauge_callback (Widget * w, widget_msg_t msg, int parm)
908 WGauge *g = (WGauge *) w;
909 Dlg_head *h = g->widget.owner;
911 if (msg == WIDGET_INIT)
912 return MSG_HANDLED;
914 /* We don't want to get the focus */
915 if (msg == WIDGET_FOCUS)
916 return MSG_NOT_HANDLED;
918 if (msg == WIDGET_DRAW)
920 widget_move (&g->widget, 0, 0);
921 tty_setcolor (DLG_NORMALC (h));
922 if (!g->shown)
923 tty_printf ("%*s", gauge_len, "");
924 else
926 int percentage, columns;
927 long total = g->max, done = g->current;
929 if (total <= 0 || done < 0)
931 done = 0;
932 total = 100;
934 if (done > total)
935 done = total;
936 while (total > 65535)
938 total /= 256;
939 done /= 256;
941 percentage = (200 * done / total + 1) / 2;
942 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
943 tty_print_char ('[');
944 if (g->from_left_to_right)
946 tty_setcolor (GAUGE_COLOR);
947 tty_printf ("%*s", (int) columns, "");
948 tty_setcolor (DLG_NORMALC (h));
949 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
951 else
953 tty_setcolor (DLG_NORMALC (h));
954 tty_printf ("%*s", gauge_len - columns - 7, "");
955 tty_setcolor (GAUGE_COLOR);
956 tty_printf ("%*s", columns, "");
957 tty_setcolor (DLG_NORMALC (h));
958 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
961 return MSG_HANDLED;
964 return default_proc (msg, parm);
967 void
968 gauge_set_value (WGauge * g, int max, int current)
970 if (g->current == current && g->max == max)
971 return; /* Do not flicker */
972 if (max == 0)
973 max = 1; /* I do not like division by zero :) */
975 g->current = current;
976 g->max = max;
977 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
980 void
981 gauge_show (WGauge * g, int shown)
983 if (g->shown == shown)
984 return;
985 g->shown = shown;
986 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
989 WGauge *
990 gauge_new (int y, int x, int shown, int max, int current)
992 WGauge *g = g_new (WGauge, 1);
994 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
995 g->shown = shown;
996 if (max == 0)
997 max = 1; /* I do not like division by zero :) */
998 g->max = max;
999 g->current = current;
1000 g->from_left_to_right = TRUE;
1001 widget_want_cursor (g->widget, 0);
1002 return g;
1006 /* Input widget */
1008 /* {{{ history button */
1010 #define LARGE_HISTORY_BUTTON 1
1012 #ifdef LARGE_HISTORY_BUTTON
1013 # define HISTORY_BUTTON_WIDTH 3
1014 #else
1015 # define HISTORY_BUTTON_WIDTH 1
1016 #endif
1018 #define should_show_history_button(in) \
1019 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1021 static void
1022 draw_history_button (WInput * in)
1024 char c;
1025 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1026 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1027 #ifdef LARGE_HISTORY_BUTTON
1029 Dlg_head *h;
1030 h = in->widget.owner;
1031 tty_setcolor (NORMAL_COLOR);
1032 tty_print_string ("[ ]");
1033 /* Too distracting: tty_setcolor (MARKED_COLOR); */
1034 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1035 tty_print_char (c);
1037 #else
1038 tty_setcolor (MARKED_COLOR);
1039 tty_print_char (c);
1040 #endif
1043 /* }}} history button */
1046 /* Input widgets now have a global kill ring */
1047 /* Pointer to killed data */
1048 static char *kill_buffer = NULL;
1050 static void
1051 input_set_markers (WInput * in, long m1)
1053 in->mark = m1;
1056 static void
1057 input_mark_cmd (WInput * in, gboolean mark)
1059 if (!mark)
1061 in->highlight = FALSE;
1062 input_set_markers (in, 0);
1064 else
1066 in->highlight = TRUE;
1067 input_set_markers (in, in->point);
1071 static gboolean
1072 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
1074 if (in->highlight)
1076 *start_mark = min (in->mark, in->point);
1077 *end_mark = max (in->mark, in->point);
1078 return TRUE;
1080 else
1082 *start_mark = *end_mark = 0;
1083 return FALSE;
1087 static void
1088 delete_region (WInput * in, int x_first, int x_last)
1090 int first = min (x_first, x_last);
1091 int last = max (x_first, x_last);
1092 size_t len;
1094 input_mark_cmd (in, FALSE);
1095 in->point = first;
1096 last = str_offset_to_pos (in->buffer, last);
1097 first = str_offset_to_pos (in->buffer, first);
1098 len = strlen (&in->buffer[last]) + 1;
1099 memmove (&in->buffer[first], &in->buffer[last], len);
1100 in->charpoint = 0;
1101 in->need_push = 1;
1104 void
1105 update_input (WInput * in, int clear_first)
1107 int has_history = 0;
1108 int i;
1109 int buf_len = str_length (in->buffer);
1110 const char *cp;
1111 int pw;
1113 if (should_show_history_button (in))
1114 has_history = HISTORY_BUTTON_WIDTH;
1116 if (in->disable_update)
1117 return;
1119 pw = str_term_width2 (in->buffer, in->point);
1121 /* Make the point visible */
1122 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1125 in->term_first_shown = pw - (in->field_width / 3);
1126 if (in->term_first_shown < 0)
1127 in->term_first_shown = 0;
1130 /* Adjust the mark */
1131 if (in->mark > buf_len)
1132 in->mark = buf_len;
1134 if (has_history)
1135 draw_history_button (in);
1137 if (in->first)
1138 tty_setcolor (in->unchanged_color);
1139 else
1140 tty_setcolor (in->color);
1142 widget_move (&in->widget, 0, 0);
1144 if (!in->is_password)
1146 if (!in->highlight)
1148 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1149 in->field_width - has_history));
1151 else
1153 long m1, m2;
1154 if (input_eval_marks (in, &m1, &m2))
1156 tty_setcolor (in->color);
1157 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1158 tty_print_string (cp);
1159 tty_setcolor (in->mark_color);
1160 if (m1 < in->term_first_shown)
1162 widget_move (&in->widget, 0, 0);
1163 tty_print_string (str_term_substring (in->buffer, in->term_first_shown, m2 - in->term_first_shown));
1165 else
1167 int sel_width;
1168 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1169 sel_width = min (m2 - m1, (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) - in->term_first_shown));
1170 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1175 else
1177 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1178 for (i = 0; i < in->field_width - has_history; i++)
1180 if (i >= 0)
1182 tty_setcolor (in->color);
1183 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1185 if (cp[0] != '\0')
1186 str_cnext_char (&cp);
1190 if (clear_first)
1191 in->first = FALSE;
1194 void
1195 winput_set_origin (WInput * in, int x, int field_width)
1197 in->widget.x = x;
1198 in->field_width = in->widget.cols = field_width;
1199 update_input (in, 0);
1202 /* {{{ history saving and loading */
1204 int num_history_items_recorded = 60;
1207 This loads and saves the history of an input line to and from the
1208 widget. It is called with the widgets history name on creation of the
1209 widget, and returns the GList list. It stores histories in the file
1210 ~/.mc/history in using the profile code.
1212 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1213 function) then input_new assigns the default text to be the last text
1214 entered, or "" if not found.
1217 GList *
1218 history_get (const char *input_name)
1220 size_t i;
1221 GList *hist = NULL;
1222 char *profile;
1223 mc_config_t *cfg;
1224 char **keys;
1225 size_t keys_num = 0;
1226 char *this_entry;
1228 if (num_history_items_recorded == 0) /* this is how to disable */
1229 return NULL;
1230 if ((input_name == NULL) || (*input_name == '\0'))
1231 return NULL;
1233 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1234 cfg = mc_config_init (profile);
1236 /* get number of keys */
1237 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1238 g_strfreev (keys);
1240 for (i = 0; i < keys_num; i++)
1242 char key[BUF_TINY];
1243 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1244 this_entry = mc_config_get_string (cfg, input_name, key, "");
1246 if (this_entry != NULL)
1247 hist = list_append_unique (hist, this_entry);
1250 mc_config_deinit (cfg);
1251 g_free (profile);
1253 /* return pointer to the last entry in the list */
1254 return g_list_last (hist);
1257 void
1258 history_put (const char *input_name, GList * h)
1260 int i;
1261 char *profile;
1262 mc_config_t *cfg;
1264 if (num_history_items_recorded == 0) /* this is how to disable */
1265 return;
1266 if ((input_name == NULL) || (*input_name == '\0'))
1267 return;
1268 if (h == NULL)
1269 return;
1271 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1273 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1274 if (i != -1)
1275 close (i);
1277 /* Make sure the history is only readable by the user */
1278 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1280 g_free (profile);
1281 return;
1284 /* go to end of list */
1285 h = g_list_last (h);
1287 /* go back 60 places */
1288 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1289 h = g_list_previous (h);
1291 cfg = mc_config_init (profile);
1293 if (input_name != NULL)
1294 mc_config_del_group (cfg, input_name);
1296 /* dump history into profile */
1297 for (i = 0; h != NULL; h = g_list_next (h))
1299 char *text = (char *) h->data;
1301 /* We shouldn't have null entries, but let's be sure */
1302 if (text != NULL)
1304 char key[BUF_TINY];
1305 g_snprintf (key, sizeof (key), "%d", i++);
1306 mc_config_set_string (cfg, input_name, key, text);
1310 mc_config_save_file (cfg, NULL);
1311 mc_config_deinit (cfg);
1312 g_free (profile);
1315 /* }}} history saving and loading */
1318 /* {{{ history display */
1320 static const char *
1321 i18n_htitle (void)
1323 return _("History");
1326 typedef struct
1328 Widget *widget;
1329 size_t count;
1330 size_t maxlen;
1331 } dlg_hist_data;
1333 static cb_ret_t
1334 dlg_hist_reposition (Dlg_head * dlg_head)
1336 dlg_hist_data *data;
1337 int x = 0, y, he, wi;
1339 /* guard checks */
1340 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1341 return MSG_NOT_HANDLED;
1343 data = (dlg_hist_data *) dlg_head->data;
1345 y = data->widget->y;
1346 he = data->count + 2;
1348 if (he <= y || y > (LINES - 6))
1350 he = min (he, y - 1);
1351 y -= he;
1353 else
1355 y++;
1356 he = min (he, LINES - y);
1359 if (data->widget->x > 2)
1360 x = data->widget->x - 2;
1362 wi = data->maxlen + 4;
1364 if ((wi + x) > COLS)
1366 wi = min (wi, COLS);
1367 x = COLS - wi;
1370 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1372 return MSG_HANDLED;
1375 static cb_ret_t
1376 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1378 switch (msg)
1380 case DLG_RESIZE:
1381 return dlg_hist_reposition (h);
1383 default:
1384 return default_dlg_callback (h, sender, msg, parm, data);
1388 char *
1389 show_hist (GList ** history, Widget * widget)
1391 GList *z, *hlist = NULL, *hi;
1392 size_t maxlen, i, count = 0;
1393 char *r = NULL;
1394 Dlg_head *query_dlg;
1395 WListbox *query_list;
1396 dlg_hist_data hist_data;
1398 if (*history == NULL)
1399 return NULL;
1401 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1403 for (z = *history; z != NULL; z = g_list_previous (z))
1405 WLEntry *entry;
1407 i = str_term_width1 ((char *) z->data);
1408 maxlen = max (maxlen, i);
1409 count++;
1411 entry = g_new0 (WLEntry, 1);
1412 /* history is being reverted here */
1413 entry->text = g_strdup ((char *) z->data);
1414 hlist = g_list_prepend (hlist, entry);
1417 hist_data.widget = widget;
1418 hist_data.count = count;
1419 hist_data.maxlen = maxlen;
1421 query_dlg =
1422 create_dlg (TRUE, 0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1423 "[History-query]", i18n_htitle (), DLG_COMPACT);
1424 query_dlg->data = &hist_data;
1426 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1428 /* this call makes list stick to all sides of dialog, effectively make
1429 it be resized with dialog */
1430 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1432 /* to avoid diplicating of (calculating sizes in two places)
1433 code, call dlg_hist_callback function here, to set dialog and
1434 controls positions.
1435 The main idea - create 4x4 dialog and add 2x2 list in
1436 center of it, and let dialog function resize it to needed
1437 size. */
1438 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1440 if (query_dlg->y < widget->y)
1442 /* draw list entries from bottom upto top */
1443 listbox_set_list (query_list, hlist);
1444 listbox_select_last (query_list);
1446 else
1448 /* draw list entries from top downto bottom */
1449 /* revert history direction */
1450 hlist = g_list_reverse (hlist);
1451 listbox_set_list (query_list, hlist);
1454 if (run_dlg (query_dlg) != B_CANCEL)
1456 char *q;
1458 listbox_get_current (query_list, &q, NULL);
1459 if (q != NULL)
1460 r = g_strdup (q);
1463 /* get modified history from dialog */
1464 z = NULL;
1465 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1467 WLEntry *entry;
1469 entry = (WLEntry *) hi->data;
1470 /* history is being reverted here again */
1471 z = g_list_prepend (z, entry->text);
1472 entry->text = NULL;
1475 destroy_dlg (query_dlg);
1477 /* restore history direction */
1478 if (query_dlg->y < widget->y)
1479 z = g_list_reverse (z);
1481 g_list_foreach (*history, (GFunc) g_free, NULL);
1482 g_list_free (*history);
1483 *history = g_list_last (z);
1485 return r;
1488 static void
1489 do_show_hist (WInput * in)
1491 char *r;
1493 r = show_hist (&in->history, &in->widget);
1494 if (r != NULL)
1496 assign_text (in, r);
1497 g_free (r);
1501 /* }}} history display */
1503 static void
1504 input_destroy (WInput * in)
1506 if (in == NULL)
1508 fprintf (stderr, "Internal error: null Input *\n");
1509 exit (EXIT_FAILURE);
1512 new_input (in);
1514 if (in->history != NULL)
1516 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
1517 history_put (in->history_name, in->history);
1519 in->history = g_list_first (in->history);
1520 g_list_foreach (in->history, (GFunc) g_free, NULL);
1521 g_list_free (in->history);
1524 g_free (in->buffer);
1525 free_completions (in);
1526 g_free (in->history_name);
1528 g_free (kill_buffer);
1529 kill_buffer = NULL;
1532 void
1533 input_disable_update (WInput * in)
1535 in->disable_update++;
1538 void
1539 input_enable_update (WInput * in)
1541 in->disable_update--;
1542 update_input (in, 0);
1546 static void
1547 push_history (WInput * in, const char *text)
1549 /* input widget where urls with passwords are entered without any
1550 vfs prefix */
1551 const char *password_input_fields[] = {
1552 " Link to a remote machine ",
1553 " FTP to machine ",
1554 " SMB link to machine "
1556 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1558 char *t;
1559 size_t i;
1560 gboolean empty;
1562 if (text == NULL)
1563 return;
1565 #ifdef ENABLE_NLS
1566 for (i = 0; i < ELEMENTS; i++)
1567 password_input_fields[i] = _(password_input_fields[i]);
1568 #endif
1570 t = g_strstrip (g_strdup (text));
1571 empty = *t == '\0';
1572 g_free (t);
1573 t = g_strdup (empty ? "" : text);
1575 if (in->history_name != NULL)
1577 /* FIXME: It is the strange code. Rewrite is needed. */
1579 const char *p = in->history_name + 3;
1581 for (i = 0; i < ELEMENTS; i++)
1582 if (strcmp (p, password_input_fields[i]) == 0)
1583 break;
1585 strip_password (t, i >= ELEMENTS);
1588 in->history = list_append_unique (in->history, t);
1589 in->need_push = 0;
1592 /* Cleans the input line and adds the current text to the history */
1593 void
1594 new_input (WInput * in)
1596 push_history (in, in->buffer);
1597 in->need_push = 1;
1598 in->buffer[0] = '\0';
1599 in->point = 0;
1600 in->charpoint = 0;
1601 in->mark = 0;
1602 in->highlight = FALSE;
1603 free_completions (in);
1604 update_input (in, 0);
1607 static void
1608 move_buffer_backward (WInput * in, int start, int end)
1610 int i, pos, len;
1611 int str_len = str_length (in->buffer);
1612 if (start >= str_len || end > str_len + 1)
1613 return;
1615 pos = str_offset_to_pos (in->buffer, start);
1616 len = str_offset_to_pos (in->buffer, end) - pos;
1618 for (i = pos; in->buffer[i + len - 1]; i++)
1619 in->buffer[i] = in->buffer[i + len];
1622 static cb_ret_t
1623 insert_char (WInput * in, int c_code)
1625 size_t i;
1626 int res;
1628 if (in->highlight)
1630 long m1, m2;
1631 if (input_eval_marks (in, &m1, &m2))
1632 delete_region (in, m1, m2);
1634 if (c_code == -1)
1635 return MSG_NOT_HANDLED;
1637 if (in->charpoint >= MB_LEN_MAX)
1638 return MSG_HANDLED;
1640 in->charbuf[in->charpoint] = c_code;
1641 in->charpoint++;
1643 res = str_is_valid_char (in->charbuf, in->charpoint);
1644 if (res < 0)
1646 if (res != -2)
1647 in->charpoint = 0; /* broken multibyte char, skip */
1648 return MSG_HANDLED;
1651 in->need_push = 1;
1652 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1654 /* Expand the buffer */
1655 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1656 char *narea = g_try_renew (char, in->buffer, new_length);
1657 if (narea)
1659 in->buffer = narea;
1660 in->current_max_size = new_length;
1664 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1666 /* bytes from begin */
1667 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1668 /* move chars */
1669 size_t rest_bytes = strlen (in->buffer + ins_point);
1671 for (i = rest_bytes + 1; i > 0; i--)
1672 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1674 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1675 in->point++;
1678 in->charpoint = 0;
1679 return MSG_HANDLED;
1682 static void
1683 beginning_of_line (WInput * in)
1685 in->point = 0;
1686 in->charpoint = 0;
1689 static void
1690 end_of_line (WInput * in)
1692 in->point = str_length (in->buffer);
1693 in->charpoint = 0;
1696 static void
1697 backward_char (WInput * in)
1699 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1701 if (in->point > 0)
1703 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1705 in->charpoint = 0;
1708 static void
1709 forward_char (WInput * in)
1711 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1712 if (act[0] != '\0')
1714 in->point += str_cnext_noncomb_char (&act);
1716 in->charpoint = 0;
1719 static void
1720 forward_word (WInput * in)
1722 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1724 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1726 str_cnext_char (&p);
1727 in->point++;
1729 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1731 str_cnext_char (&p);
1732 in->point++;
1736 static void
1737 backward_word (WInput * in)
1739 const char *p;
1740 const char *p_tmp;
1742 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1743 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1745 while (p != in->buffer)
1747 p_tmp = p;
1748 str_cprev_char (&p);
1749 if (!str_isspace (p) && !str_ispunct (p))
1751 p = p_tmp;
1752 break;
1754 in->point--;
1756 while (p != in->buffer)
1758 str_cprev_char (&p);
1759 if (str_isspace (p) || str_ispunct (p))
1760 break;
1762 in->point--;
1766 static void
1767 key_left (WInput * in)
1769 backward_char (in);
1772 static void
1773 key_ctrl_left (WInput * in)
1775 backward_word (in);
1778 static void
1779 key_right (WInput * in)
1781 forward_char (in);
1784 static void
1785 key_ctrl_right (WInput * in)
1787 forward_word (in);
1789 static void
1790 backward_delete (WInput * in)
1792 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1793 int start;
1795 if (in->point == 0)
1796 return;
1798 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1799 move_buffer_backward (in, start, in->point);
1800 in->charpoint = 0;
1801 in->need_push = 1;
1802 in->point = start;
1805 static void
1806 delete_char (WInput * in)
1808 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1809 int end = in->point;
1811 end += str_cnext_noncomb_char (&act);
1813 move_buffer_backward (in, in->point, end);
1814 in->charpoint = 0;
1815 in->need_push = 1;
1818 static void
1819 copy_region (WInput * in, int x_first, int x_last)
1821 int first = min (x_first, x_last);
1822 int last = max (x_first, x_last);
1824 if (last == first)
1826 /* Copy selected files to clipboard */
1827 panel_save_curent_file_to_clip_file ();
1828 /* try use external clipboard utility */
1829 copy_file_to_ext_clip ();
1830 return;
1833 g_free (kill_buffer);
1835 first = str_offset_to_pos (in->buffer, first);
1836 last = str_offset_to_pos (in->buffer, last);
1838 kill_buffer = g_strndup (in->buffer + first, last - first);
1840 save_text_to_clip_file (kill_buffer);
1841 /* try use external clipboard utility */
1842 copy_file_to_ext_clip ();
1845 static void
1846 kill_word (WInput * in)
1848 int old_point = in->point;
1849 int new_point;
1851 forward_word (in);
1852 new_point = in->point;
1853 in->point = old_point;
1855 copy_region (in, old_point, new_point);
1856 delete_region (in, old_point, new_point);
1857 in->need_push = 1;
1858 in->charpoint = 0;
1859 in->charpoint = 0;
1862 static void
1863 back_kill_word (WInput * in)
1865 int old_point = in->point;
1866 int new_point;
1868 backward_word (in);
1869 new_point = in->point;
1870 in->point = old_point;
1872 copy_region (in, old_point, new_point);
1873 delete_region (in, old_point, new_point);
1874 in->need_push = 1;
1877 static void
1878 set_mark (WInput * in)
1880 input_mark_cmd (in, TRUE);
1883 static void
1884 kill_save (WInput * in)
1886 copy_region (in, in->mark, in->point);
1889 static void
1890 kill_region (WInput * in)
1892 kill_save (in);
1893 delete_region (in, in->point, in->mark);
1896 static void
1897 clear_region (WInput * in)
1899 delete_region (in, in->point, in->mark);
1902 static void
1903 yank (WInput * in)
1905 if (kill_buffer != NULL)
1907 char *p;
1909 in->charpoint = 0;
1910 for (p = kill_buffer; *p != '\0'; p++)
1911 insert_char (in, *p);
1912 in->charpoint = 0;
1916 static void
1917 kill_line (WInput * in)
1919 int chp = str_offset_to_pos (in->buffer, in->point);
1920 g_free (kill_buffer);
1921 kill_buffer = g_strdup (&in->buffer[chp]);
1922 in->buffer[chp] = '\0';
1923 in->charpoint = 0;
1926 static void
1927 ins_from_clip (WInput * in)
1929 char *p = NULL;
1931 /* try use external clipboard utility */
1932 paste_to_file_from_ext_clip ();
1934 if (load_text_from_clip_file (&p))
1936 char *pp;
1938 for (pp = p; *pp != '\0'; pp++)
1939 insert_char (in, *pp);
1941 g_free (p);
1945 void
1946 assign_text (WInput * in, const char *text)
1948 free_completions (in);
1949 g_free (in->buffer);
1950 in->buffer = g_strdup (text); /* was in->buffer->text */
1951 in->current_max_size = strlen (in->buffer) + 1;
1952 in->point = str_length (in->buffer);
1953 in->mark = 0;
1954 in->need_push = 1;
1955 in->charpoint = 0;
1958 static void
1959 hist_prev (WInput * in)
1961 GList *prev;
1963 if (!in->history)
1964 return;
1966 if (in->need_push)
1967 push_history (in, in->buffer);
1969 prev = g_list_previous (in->history);
1970 if (prev != NULL)
1972 in->history = prev;
1973 assign_text (in, (char *) prev->data);
1974 in->need_push = 0;
1978 static void
1979 hist_next (WInput * in)
1981 if (in->need_push)
1983 push_history (in, in->buffer);
1984 assign_text (in, "");
1985 return;
1988 if (!in->history)
1989 return;
1991 if (!in->history->next)
1993 assign_text (in, "");
1994 return;
1997 in->history = g_list_next (in->history);
1998 assign_text (in, (char *) in->history->data);
1999 in->need_push = 0;
2002 static void
2003 port_region_marked_for_delete (WInput * in)
2005 in->buffer[0] = '\0';
2006 in->point = 0;
2007 in->first = FALSE;
2008 in->charpoint = 0;
2011 static cb_ret_t
2012 input_execute_cmd (WInput * in, unsigned long command)
2014 cb_ret_t res = MSG_HANDLED;
2016 /* a highlight command like shift-arrow */
2017 if (command == CK_InputLeftHighlight ||
2018 command == CK_InputRightHighlight ||
2019 command == CK_InputWordLeftHighlight ||
2020 command == CK_InputWordRightHighlight ||
2021 command == CK_InputBolHighlight ||
2022 command == CK_InputEolHighlight)
2024 if (!in->highlight)
2026 input_mark_cmd (in, FALSE); /* clear */
2027 input_mark_cmd (in, TRUE); /* marking on */
2031 switch (command)
2033 case CK_InputForwardWord:
2034 case CK_InputBackwardWord:
2035 case CK_InputForwardChar:
2036 case CK_InputBackwardChar:
2037 if (in->highlight)
2038 input_mark_cmd (in, FALSE);
2041 switch (command)
2043 case CK_InputBol:
2044 case CK_InputBolHighlight:
2045 beginning_of_line (in);
2046 break;
2047 case CK_InputEol:
2048 case CK_InputEolHighlight:
2049 end_of_line (in);
2050 break;
2051 case CK_InputMoveLeft:
2052 case CK_InputLeftHighlight:
2053 key_left (in);
2054 break;
2055 case CK_InputWordLeft:
2056 case CK_InputWordLeftHighlight:
2057 key_ctrl_left (in);
2058 break;
2059 case CK_InputMoveRight:
2060 case CK_InputRightHighlight:
2061 key_right (in);
2062 break;
2063 case CK_InputWordRight:
2064 case CK_InputWordRightHighlight:
2065 key_ctrl_right (in);
2066 break;
2067 case CK_InputBackwardChar:
2068 backward_char (in);
2069 break;
2070 case CK_InputBackwardWord:
2071 backward_word (in);
2072 break;
2073 case CK_InputForwardChar:
2074 forward_char (in);
2075 break;
2076 case CK_InputForwardWord:
2077 forward_word (in);
2078 break;
2079 case CK_InputBackwardDelete:
2080 if (in->highlight)
2082 long m1, m2;
2083 if (input_eval_marks (in, &m1, &m2))
2084 delete_region (in, m1, m2);
2086 else
2088 backward_delete (in);
2090 break;
2091 case CK_InputDeleteChar:
2092 if (in->first)
2093 port_region_marked_for_delete (in);
2094 else if (in->highlight)
2096 long m1, m2;
2097 if (input_eval_marks (in, &m1, &m2))
2098 delete_region (in, m1, m2);
2100 else
2101 delete_char (in);
2102 break;
2103 case CK_InputKillWord:
2104 kill_word (in);
2105 break;
2106 case CK_InputBackwardKillWord:
2107 back_kill_word (in);
2108 break;
2109 case CK_InputSetMark:
2110 set_mark (in);
2111 break;
2112 case CK_InputKillRegion:
2113 kill_region (in);
2114 break;
2115 case CK_InputClearLine:
2116 clear_region (in);
2117 break;
2118 case CK_InputKillSave:
2119 kill_save (in);
2120 break;
2121 case CK_InputYank:
2122 yank (in);
2123 break;
2124 case CK_InputPaste:
2125 ins_from_clip (in);
2126 break;
2127 case CK_InputKillLine:
2128 kill_line (in);
2129 break;
2130 case CK_InputHistoryPrev:
2131 hist_prev (in);
2132 break;
2133 case CK_InputHistoryNext:
2134 hist_next (in);
2135 break;
2136 case CK_InputHistoryShow:
2137 do_show_hist (in);
2138 break;
2139 case CK_InputComplete:
2140 complete (in);
2141 break;
2142 default:
2143 res = MSG_NOT_HANDLED;
2146 if (command != CK_InputLeftHighlight &&
2147 command != CK_InputRightHighlight &&
2148 command != CK_InputWordLeftHighlight &&
2149 command != CK_InputWordRightHighlight &&
2150 command != CK_InputBolHighlight &&
2151 command != CK_InputEolHighlight)
2153 in->highlight = FALSE;
2156 return res;
2159 /* This function is a test for a special input key used in complete.c */
2160 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2161 and 2 if it is a complete key */
2163 is_in_input_map (WInput * in, int key)
2165 size_t i;
2166 for (i = 0; input_map[i].key != 0; i++)
2167 if (key == input_map[i].key)
2169 input_execute_cmd (in, input_map[i].command);
2170 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2172 return 0;
2175 cb_ret_t
2176 handle_char (WInput * in, int key)
2178 cb_ret_t v;
2179 int i;
2181 v = MSG_NOT_HANDLED;
2183 if (quote)
2185 free_completions (in);
2186 v = insert_char (in, key);
2187 update_input (in, 1);
2188 quote = 0;
2189 return v;
2191 for (i = 0; input_map[i].key; i++)
2193 if (key == input_map[i].key)
2195 if (input_map[i].command != CK_InputComplete)
2196 free_completions (in);
2197 input_execute_cmd (in, input_map[i].command);
2198 update_input (in, 1);
2199 v = MSG_HANDLED;
2200 break;
2203 if (input_map[i].command == 0)
2205 if (key > 255)
2206 return MSG_NOT_HANDLED;
2207 if (in->first)
2208 port_region_marked_for_delete (in);
2209 free_completions (in);
2210 v = insert_char (in, key);
2212 update_input (in, 1);
2213 return v;
2216 /* Inserts text in input line */
2217 void
2218 stuff (WInput * in, const char *text, int insert_extra_space)
2220 input_disable_update (in);
2221 while (*text != '\0')
2222 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2223 if (insert_extra_space)
2224 handle_char (in, ' ');
2225 input_enable_update (in);
2226 update_input (in, 1);
2229 void
2230 input_set_point (WInput * in, int pos)
2232 int max_pos = str_length (in->buffer);
2234 if (pos > max_pos)
2235 pos = max_pos;
2236 if (pos != in->point)
2237 free_completions (in);
2238 in->point = pos;
2239 in->charpoint = 0;
2240 update_input (in, 1);
2243 cb_ret_t
2244 input_callback (Widget * w, widget_msg_t msg, int parm)
2246 WInput *in = (WInput *) w;
2247 cb_ret_t v;
2249 switch (msg)
2251 case WIDGET_KEY:
2252 if (parm == XCTRL ('q'))
2254 quote = 1;
2255 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2256 quote = 0;
2257 return v;
2260 /* Keys we want others to handle */
2261 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2262 || parm == KEY_F (10) || parm == '\n')
2263 return MSG_NOT_HANDLED;
2265 /* When pasting multiline text, insert literal Enter */
2266 if ((parm & ~KEY_M_MASK) == '\n')
2268 quote = 1;
2269 v = handle_char (in, '\n');
2270 quote = 0;
2271 return v;
2274 return handle_char (in, parm);
2276 case WIDGET_COMMAND:
2277 return input_execute_cmd (in, parm);
2279 case WIDGET_FOCUS:
2280 case WIDGET_UNFOCUS:
2281 case WIDGET_DRAW:
2282 update_input (in, 0);
2283 return MSG_HANDLED;
2285 case WIDGET_CURSOR:
2286 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2287 - in->term_first_shown);
2288 return MSG_HANDLED;
2290 case WIDGET_DESTROY:
2291 input_destroy (in);
2292 return MSG_HANDLED;
2294 default:
2295 return default_proc (msg, parm);
2299 static int
2300 input_event (Gpm_Event * event, void *data)
2302 WInput *in = data;
2304 if (event->type & GPM_DOWN)
2306 in->first = FALSE;
2307 input_mark_cmd (in, FALSE);
2309 if (event->type & (GPM_DOWN | GPM_DRAG))
2311 dlg_select_widget (in);
2313 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2314 && should_show_history_button (in))
2316 do_show_hist (in);
2318 else
2320 in->point = str_length (in->buffer);
2321 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2322 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2324 update_input (in, 1);
2326 /* A lone up mustn't do anything */
2327 if (in->highlight && event->type & (GPM_UP | GPM_DRAG))
2328 return MOU_NORMAL;
2330 if (!(event->type & GPM_DRAG))
2331 input_mark_cmd (in, TRUE);
2333 return MOU_NORMAL;
2336 WInput *
2337 input_new (int y, int x, int *input_colors, int width, const char *def_text,
2338 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2340 WInput *in = g_new (WInput, 1);
2341 size_t initial_buffer_len;
2343 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2345 /* history setup */
2346 in->history_name = NULL;
2347 in->history = NULL;
2348 if ((histname != NULL) && (*histname != '\0'))
2350 in->history_name = g_strdup (histname);
2351 in->history = history_get (histname);
2354 if (def_text == NULL)
2355 def_text = "";
2356 else if (def_text == INPUT_LAST_TEXT)
2358 if ((in->history != NULL) && (in->history->data != NULL))
2359 def_text = (char *) in->history->data;
2360 else
2361 def_text = "";
2364 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2365 in->widget.options |= W_IS_INPUT;
2366 in->completions = NULL;
2367 in->completion_flags = completion_flags;
2368 in->current_max_size = initial_buffer_len;
2369 in->buffer = g_new (char, initial_buffer_len);
2370 in->color = input_colors[0];
2371 in->unchanged_color = input_colors[1];
2372 in->mark_color = input_colors[2];
2373 in->field_width = width;
2374 in->first = TRUE;
2375 in->highlight = FALSE;
2376 in->term_first_shown = 0;
2377 in->disable_update = 0;
2378 in->mark = 0;
2379 in->need_push = 1;
2380 in->is_password = 0;
2382 strcpy (in->buffer, def_text);
2383 in->point = str_length (in->buffer);
2384 in->charpoint = 0;
2386 return in;
2390 /* Listbox widget */
2392 /* Should draw the scrollbar, but currently draws only
2393 * indications that there is more information
2396 static void
2397 listbox_entry_free (void *data)
2399 WLEntry *e = data;
2400 g_free (e->text);
2401 g_free (e);
2404 static void
2405 listbox_drawscroll (WListbox * l)
2407 const int max_line = l->widget.lines - 1;
2408 int line = 0;
2409 int i;
2411 /* Are we at the top? */
2412 widget_move (&l->widget, 0, l->widget.cols);
2413 if (l->top == 0)
2414 tty_print_one_vline (TRUE);
2415 else
2416 tty_print_char ('^');
2418 /* Are we at the bottom? */
2419 widget_move (&l->widget, max_line, l->widget.cols);
2420 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2421 tty_print_one_vline (TRUE);
2422 else
2423 tty_print_char ('v');
2425 /* Now draw the nice relative pointer */
2426 if (l->count != 0)
2427 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2429 for (i = 1; i < max_line; i++)
2431 widget_move (&l->widget, i, l->widget.cols);
2432 if (i != line)
2433 tty_print_one_vline (TRUE);
2434 else
2435 tty_print_char ('*');
2439 static void
2440 listbox_draw (WListbox * l, gboolean focused)
2442 const Dlg_head *h = l->widget.owner;
2443 const int normalc = DLG_NORMALC (h);
2444 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2446 GList *le;
2447 int pos;
2448 int i;
2449 int sel_line = -1;
2451 le = g_list_nth (l->list, l->top);
2452 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2453 pos = (le == NULL) ? 0 : l->top;
2455 for (i = 0; i < l->widget.lines; i++)
2457 const char *text;
2459 /* Display the entry */
2460 if (pos == l->pos && sel_line == -1)
2462 sel_line = i;
2463 tty_setcolor (selc);
2465 else
2466 tty_setcolor (normalc);
2468 widget_move (&l->widget, i, 1);
2470 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2471 text = "";
2472 else
2474 WLEntry *e = (WLEntry *) le->data;
2475 text = e->text;
2476 le = g_list_next (le);
2477 pos++;
2480 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2483 l->cursor_y = sel_line;
2485 if (l->scrollbar && (l->count > l->widget.lines))
2487 tty_setcolor (normalc);
2488 listbox_drawscroll (l);
2492 static int
2493 listbox_check_hotkey (WListbox * l, int key)
2495 int i;
2496 GList *le;
2498 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2500 WLEntry *e = (WLEntry *) le->data;
2502 if (e->hotkey == key)
2503 return i;
2506 return (-1);
2509 /* Selects the last entry and scrolls the list to the bottom */
2510 void
2511 listbox_select_last (WListbox * l)
2513 l->pos = l->count - 1;
2514 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2517 /* Selects the first entry and scrolls the list to the top */
2518 void
2519 listbox_select_first (WListbox * l)
2521 l->pos = l->top = 0;
2524 void
2525 listbox_set_list (WListbox * l, GList * list)
2527 listbox_remove_list (l);
2529 if (l != NULL)
2531 l->list = list;
2532 l->top = l->pos = 0;
2533 l->count = g_list_length (list);
2537 void
2538 listbox_remove_list (WListbox * l)
2540 if ((l != NULL) && (l->count != 0))
2542 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2543 g_list_free (l->list);
2544 l->list = NULL;
2545 l->count = l->pos = l->top = 0;
2549 void
2550 listbox_remove_current (WListbox * l)
2552 if ((l != NULL) && (l->count != 0))
2554 GList *current;
2556 current = g_list_nth (l->list, l->pos);
2557 l->list = g_list_remove_link (l->list, current);
2558 listbox_entry_free ((WLEntry *) current->data);
2559 g_list_free_1 (current);
2560 l->count--;
2562 if (l->count == 0)
2563 l->top = l->pos = 0;
2564 else if (l->pos >= l->count)
2565 l->pos = l->count - 1;
2569 void
2570 listbox_select_entry (WListbox * l, int dest)
2572 GList *le;
2573 int pos;
2574 gboolean top_seen = FALSE;
2576 if (dest < 0)
2577 return;
2579 /* Special case */
2580 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2582 if (pos == l->top)
2583 top_seen = TRUE;
2585 if (pos == dest)
2587 l->pos = dest;
2588 if (!top_seen)
2589 l->top = l->pos;
2590 else if (l->pos - l->top >= l->widget.lines)
2591 l->top = l->pos - l->widget.lines + 1;
2592 return;
2596 /* If we are unable to find it, set decent values */
2597 l->pos = l->top = 0;
2600 /* Selects from base the pos element */
2601 static int
2602 listbox_select_pos (WListbox * l, int base, int pos)
2604 int last = l->count - 1;
2606 base += pos;
2607 if (base >= last)
2608 base = last;
2610 return base;
2613 static void
2614 listbox_fwd (WListbox * l)
2616 if (l->pos + 1 >= l->count)
2617 listbox_select_first (l);
2618 else
2619 listbox_select_entry (l, l->pos + 1);
2622 static void
2623 listbox_back (WListbox * l)
2625 if (l->pos <= 0)
2626 listbox_select_last (l);
2627 else
2628 listbox_select_entry (l, l->pos - 1);
2631 /* Return MSG_HANDLED if we want a redraw */
2632 static cb_ret_t
2633 listbox_key (WListbox * l, int key)
2635 int i;
2637 cb_ret_t j = MSG_NOT_HANDLED;
2639 if (l->list == NULL)
2640 return MSG_NOT_HANDLED;
2642 /* focus on listbox item N by '0'..'9' keys */
2643 if (key >= '0' && key <= '9')
2645 int oldpos = l->pos;
2646 listbox_select_entry (l, key - '0');
2648 /* need scroll to item? */
2649 if (abs (oldpos - l->pos) > l->widget.lines)
2650 l->top = l->pos;
2652 return MSG_HANDLED;
2655 switch (key)
2657 case KEY_HOME:
2658 case KEY_A1:
2659 case ALT ('<'):
2660 listbox_select_first (l);
2661 return MSG_HANDLED;
2663 case KEY_END:
2664 case KEY_C1:
2665 case ALT ('>'):
2666 listbox_select_last (l);
2667 return MSG_HANDLED;
2669 case XCTRL ('p'):
2670 case KEY_UP:
2671 listbox_back (l);
2672 return MSG_HANDLED;
2674 case XCTRL ('n'):
2675 case KEY_DOWN:
2676 listbox_fwd (l);
2677 return MSG_HANDLED;
2679 case KEY_NPAGE:
2680 case XCTRL ('v'):
2681 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2683 listbox_fwd (l);
2684 j = MSG_HANDLED;
2686 break;
2688 case KEY_PPAGE:
2689 case ALT ('v'):
2690 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2692 listbox_back (l);
2693 j = MSG_HANDLED;
2695 break;
2697 case KEY_DC:
2698 case 'd':
2699 if (l->deletable)
2701 gboolean is_last = (l->pos + 1 >= l->count);
2702 gboolean is_more = (l->top + l->widget.lines >= l->count);
2704 listbox_remove_current (l);
2705 if ((l->top > 0) && (is_last || is_more))
2706 l->top--;
2708 return MSG_HANDLED;
2710 case (KEY_M_SHIFT | KEY_DC):
2711 case 'D':
2712 if (l->deletable && confirm_history_cleanup
2713 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2714 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2715 _("Do you want clean this history?"),
2716 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2718 listbox_remove_list (l);
2719 j = MSG_HANDLED;
2721 break;
2723 default:
2724 break;
2727 return j;
2730 static inline void
2731 listbox_destroy (WListbox * l)
2733 /* don't delete list in modifable listbox */
2734 if (!l->deletable)
2735 listbox_remove_list (l);
2738 static cb_ret_t
2739 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2741 WListbox *l = (WListbox *) w;
2742 Dlg_head *h = l->widget.owner;
2743 cb_ret_t ret_code;
2745 switch (msg)
2747 case WIDGET_INIT:
2748 return MSG_HANDLED;
2750 case WIDGET_HOTKEY:
2752 int pos, action;
2754 pos = listbox_check_hotkey (l, parm);
2755 if (pos < 0)
2756 return MSG_NOT_HANDLED;
2758 listbox_select_entry (l, pos);
2759 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2761 if (l->cback != NULL)
2762 action = l->cback (l);
2763 else
2764 action = LISTBOX_DONE;
2766 if (action == LISTBOX_DONE)
2768 h->ret_value = B_ENTER;
2769 dlg_stop (h);
2772 return MSG_HANDLED;
2775 case WIDGET_KEY:
2776 ret_code = listbox_key (l, parm);
2777 if (ret_code != MSG_NOT_HANDLED)
2779 listbox_draw (l, TRUE);
2780 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2782 return ret_code;
2784 case WIDGET_CURSOR:
2785 widget_move (&l->widget, l->cursor_y, 0);
2786 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2787 return MSG_HANDLED;
2789 case WIDGET_FOCUS:
2790 case WIDGET_UNFOCUS:
2791 case WIDGET_DRAW:
2792 listbox_draw (l, msg != WIDGET_UNFOCUS);
2793 return MSG_HANDLED;
2795 case WIDGET_DESTROY:
2796 listbox_destroy (l);
2797 return MSG_HANDLED;
2799 case WIDGET_RESIZED:
2800 return MSG_HANDLED;
2802 default:
2803 return default_proc (msg, parm);
2807 static int
2808 listbox_event (Gpm_Event * event, void *data)
2810 WListbox *l = data;
2811 int i;
2813 Dlg_head *h = l->widget.owner;
2815 /* Single click */
2816 if (event->type & GPM_DOWN)
2817 dlg_select_widget (l);
2819 if (l->list == NULL)
2820 return MOU_NORMAL;
2822 if (event->type & (GPM_DOWN | GPM_DRAG))
2824 int ret = MOU_REPEAT;
2826 if (event->x < 0 || event->x > l->widget.cols)
2827 return ret;
2829 if (event->y < 1)
2830 for (i = -event->y; i >= 0; i--)
2831 listbox_back (l);
2832 else if (event->y > l->widget.lines)
2833 for (i = event->y - l->widget.lines; i > 0; i--)
2834 listbox_fwd (l);
2835 else if (event->buttons & GPM_B_UP)
2837 listbox_back (l);
2838 ret = MOU_NORMAL;
2840 else if (event->buttons & GPM_B_DOWN)
2842 listbox_fwd (l);
2843 ret = MOU_NORMAL;
2845 else
2846 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2848 /* We need to refresh ourselves since the dialog manager doesn't */
2849 /* know about this event */
2850 listbox_draw (l, TRUE);
2851 return ret;
2854 /* Double click */
2855 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2857 int action;
2859 if (event->x < 0 || event->x >= l->widget.cols
2860 || event->y < 1 || event->y > l->widget.lines)
2861 return MOU_NORMAL;
2863 dlg_select_widget (l);
2864 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2866 if (l->cback != NULL)
2867 action = l->cback (l);
2868 else
2869 action = LISTBOX_DONE;
2871 if (action == LISTBOX_DONE)
2873 h->ret_value = B_ENTER;
2874 dlg_stop (h);
2875 return MOU_NORMAL;
2878 return MOU_NORMAL;
2881 WListbox *
2882 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2884 WListbox *l = g_new (WListbox, 1);
2886 if (height <= 0)
2887 height = 1;
2889 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2891 l->list = NULL;
2892 l->top = l->pos = 0;
2893 l->count = 0;
2894 l->deletable = deletable;
2895 l->cback = callback;
2896 l->allow_duplicates = TRUE;
2897 l->scrollbar = !tty_is_slow ();
2898 widget_want_hotkey (l->widget, 1);
2899 widget_want_cursor (l->widget, 0);
2901 return l;
2904 static int
2905 listbox_entry_cmp (const void *a, const void *b)
2907 const WLEntry *ea = (const WLEntry *) a;
2908 const WLEntry *eb = (const WLEntry *) b;
2910 return strcmp (ea->text, eb->text);
2913 /* Listbox item adding function */
2914 static inline void
2915 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2917 switch (pos)
2919 case LISTBOX_APPEND_AT_END:
2920 l->list = g_list_append (l->list, e);
2921 break;
2923 case LISTBOX_APPEND_BEFORE:
2924 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2925 if (l->pos > 0)
2926 l->pos--;
2927 break;
2929 case LISTBOX_APPEND_AFTER:
2930 l->list = g_list_insert (l->list, e, l->pos + 1);
2931 break;
2933 case LISTBOX_APPEND_SORTED:
2934 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2935 break;
2937 default:
2938 return;
2941 l->count++;
2944 char *
2945 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2947 WLEntry *entry;
2949 if (l == NULL)
2950 return NULL;
2952 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2953 return NULL;
2955 entry = g_new (WLEntry, 1);
2956 entry->text = g_strdup (text);
2957 entry->data = data;
2958 entry->hotkey = hotkey;
2960 listbox_append_item (l, entry, pos);
2962 return entry->text;
2966 listbox_search_text (WListbox * l, const char *text)
2968 if (l != NULL)
2970 int i;
2971 GList *le;
2973 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2975 WLEntry *e = (WLEntry *) le->data;
2977 if (strcmp (e->text, text) == 0)
2978 return i;
2982 return (-1);
2985 /* Returns the current string text as well as the associated extra data */
2986 void
2987 listbox_get_current (WListbox * l, char **string, void **extra)
2989 WLEntry *e = NULL;
2990 gboolean ok;
2992 if (l != NULL)
2993 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2995 ok = (e != NULL);
2997 if (string != NULL)
2998 *string = ok ? e->text : NULL;
3000 if (extra != NULL)
3001 *extra = ok ? e->data : NULL;
3005 /* ButtonBar widget */
3007 /* returns TRUE if a function has been called, FALSE otherwise. */
3008 static gboolean
3009 buttonbar_call (WButtonBar * bb, int i)
3011 cb_ret_t ret = MSG_NOT_HANDLED;
3013 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
3014 ret = bb->widget.owner->callback (bb->widget.owner,
3015 (Widget *) bb, DLG_ACTION,
3016 bb->labels[i].command, bb->labels[i].receiver);
3017 return ret;
3020 /* calculate positions of buttons; width is never less than 7 */
3021 static void
3022 buttonbar_init_button_positions (WButtonBar *bb)
3024 int i;
3025 int pos = 0;
3027 if (COLS < BUTTONBAR_LABELS_NUM * 7)
3029 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3031 if (pos + 7 <= COLS)
3032 pos += 7;
3034 bb->labels[i].end_coord = pos;
3037 else
3039 /* Distribute the extra width in a way that the middle vertical line
3040 (between F5 and F6) aligns with the two panels. The extra width
3041 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3042 int lc_div, mod;
3044 lc_div = COLS / BUTTONBAR_LABELS_NUM;
3045 mod = COLS % BUTTONBAR_LABELS_NUM;
3047 for (i = 0; i < BUTTONBAR_LABELS_NUM / 2; i++)
3049 pos += lc_div;
3050 if (BUTTONBAR_LABELS_NUM / 2 - 1 - i < mod / 2)
3051 pos++;
3053 bb->labels[i].end_coord = pos;
3056 for (; i < BUTTONBAR_LABELS_NUM; i++)
3058 pos += lc_div;
3059 if (BUTTONBAR_LABELS_NUM - 1 - i < (mod + 1) / 2)
3060 pos++;
3062 bb->labels[i].end_coord = pos;
3067 /* return width of one button */
3068 static int
3069 buttonbar_get_button_width (const WButtonBar *bb, int i)
3071 if (i == 0)
3072 return bb->labels[0].end_coord;
3073 return bb->labels[i].end_coord - bb->labels[i - 1].end_coord;
3076 static int
3077 buttonbar_get_button_by_x_coord (const WButtonBar *bb, int x)
3079 int i;
3081 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3082 if (bb->labels[i].end_coord > x)
3083 return i;
3085 return (-1);
3088 static cb_ret_t
3089 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
3091 WButtonBar *bb = (WButtonBar *) w;
3092 int i;
3093 const char *text;
3095 switch (msg)
3097 case WIDGET_FOCUS:
3098 return MSG_NOT_HANDLED;
3100 case WIDGET_HOTKEY:
3101 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3102 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
3103 return MSG_HANDLED;
3104 return MSG_NOT_HANDLED;
3106 case WIDGET_DRAW:
3107 if (bb->visible)
3109 buttonbar_init_button_positions (bb);
3110 widget_move (&bb->widget, 0, 0);
3111 tty_setcolor (DEFAULT_COLOR);
3112 tty_printf ("%-*s", bb->widget.cols, "");
3113 widget_move (&bb->widget, 0, 0);
3115 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3117 int width;
3119 width = buttonbar_get_button_width (bb, i);
3120 if (width <= 0)
3121 break;
3123 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
3124 tty_printf ("%2d", i + 1);
3125 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
3126 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
3127 tty_print_string (str_fit_to_term (text, width - 2, J_LEFT_FIT));
3130 return MSG_HANDLED;
3132 case WIDGET_DESTROY:
3133 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3134 g_free (bb->labels[i].text);
3135 return MSG_HANDLED;
3137 default:
3138 return default_proc (msg, parm);
3142 static int
3143 buttonbar_event (Gpm_Event * event, void *data)
3145 WButtonBar *bb = data;
3146 int button;
3148 if (!(event->type & GPM_UP))
3149 return MOU_NORMAL;
3150 if (event->y == 2)
3151 return MOU_NORMAL;
3152 button = buttonbar_get_button_by_x_coord (bb, event->x - 1);
3153 if (button >= 0)
3154 buttonbar_call (bb, button);
3155 return MOU_NORMAL;
3158 WButtonBar *
3159 buttonbar_new (gboolean visible)
3161 WButtonBar *bb;
3163 bb = g_new0 (WButtonBar, 1);
3165 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
3166 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
3167 bb->visible = visible;
3168 widget_want_hotkey (bb->widget, 1);
3169 widget_want_cursor (bb->widget, 0);
3171 return bb;
3174 static void
3175 set_label_text (WButtonBar * bb, int lc_index, const char *text)
3177 g_free (bb->labels[lc_index - 1].text);
3178 bb->labels[lc_index - 1].text = g_strdup (text);
3181 /* Find ButtonBar widget in the dialog */
3182 WButtonBar *
3183 find_buttonbar (const Dlg_head * h)
3185 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
3188 void
3189 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
3190 const struct global_keymap_t *keymap, const Widget * receiver)
3192 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
3194 unsigned long command = CK_Ignore_Key;
3196 if (keymap != NULL)
3197 command = lookup_keymap_command (keymap, KEY_F (idx));
3199 if ((text == NULL) || (text[0] == '\0'))
3200 set_label_text (bb, idx, "");
3201 else
3202 set_label_text (bb, idx, text);
3204 bb->labels[idx - 1].command = command;
3205 bb->labels[idx - 1].receiver = (Widget *) receiver;
3209 void
3210 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
3212 bb->visible = visible;
3215 void
3216 buttonbar_redraw (WButtonBar * bb)
3218 if (bb != NULL)
3219 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3222 static cb_ret_t
3223 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3225 WGroupbox *g = (WGroupbox *) w;
3227 switch (msg)
3229 case WIDGET_INIT:
3230 return MSG_HANDLED;
3232 case WIDGET_FOCUS:
3233 return MSG_NOT_HANDLED;
3235 case WIDGET_DRAW:
3236 tty_setcolor (COLOR_NORMAL);
3237 draw_box (g->widget.owner, g->widget.y - g->widget.owner->y,
3238 g->widget.x - g->widget.owner->x, g->widget.lines, g->widget.cols, TRUE);
3240 tty_setcolor (COLOR_HOT_NORMAL);
3241 dlg_move (g->widget.owner, g->widget.y - g->widget.owner->y,
3242 g->widget.x - g->widget.owner->x + 1);
3243 tty_print_string (g->title);
3244 return MSG_HANDLED;
3246 case WIDGET_DESTROY:
3247 g_free (g->title);
3248 return MSG_HANDLED;
3250 default:
3251 return default_proc (msg, parm);
3255 WGroupbox *
3256 groupbox_new (int y, int x, int height, int width, const char *title)
3258 WGroupbox *g = g_new (WGroupbox, 1);
3260 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3262 g->widget.options &= ~W_WANT_CURSOR;
3263 widget_want_hotkey (g->widget, 0);
3265 /* Strip existing spaces, add one space before and after the title */
3266 if (title)
3268 char *t;
3269 t = g_strstrip (g_strdup (title));
3270 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3271 g_free (t);
3274 return g;