Ticket #2170: Color collisions
[midnight-commander.git] / src / widget.c
blob7b4443349508b4101291c516d41fce034d81a93c
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 = h->color[DLG_COLOR_HOT_FOCUS];
78 else
79 color = h->color[DLG_COLOR_HOT_NORMAL];
81 else
83 if (focused)
84 color = h->color[DLG_COLOR_FOCUS];
85 else
86 color = h->color[DLG_COLOR_NORMAL];
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_draw_hline (r->widget.y + i, r->widget.x, ' ', r->widget.cols);
465 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
466 draw_hotkey (w, r->texts[i], focused);
468 return MSG_HANDLED;
470 case WIDGET_DESTROY:
471 for (i = 0; i < r->count; i++)
473 release_hotkey (r->texts[i]);
475 g_free (r->texts);
476 return MSG_HANDLED;
478 default:
479 return default_proc (msg, parm);
483 static int
484 radio_event (Gpm_Event * event, void *data)
486 WRadio *r = data;
487 Widget *w = data;
489 if (event->type & (GPM_DOWN | GPM_UP))
491 Dlg_head *h = r->widget.owner;
493 r->pos = event->y - 1;
494 dlg_select_widget (r);
495 if (event->type & GPM_UP)
497 radio_callback (w, WIDGET_KEY, ' ');
498 radio_callback (w, WIDGET_FOCUS, 0);
499 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
500 return MOU_NORMAL;
503 return MOU_NORMAL;
506 WRadio *
507 radio_new (int y, int x, int count, const char **texts)
509 WRadio *result = g_new (WRadio, 1);
510 int i, max, m;
512 /* Compute the longest string */
513 result->texts = g_new (struct hotkey_t, count);
515 max = 0;
516 for (i = 0; i < count; i++)
518 result->texts[i] = parse_hotkey (texts[i]);
519 m = hotkey_width (result->texts[i]);
520 if (m > max)
521 max = m;
524 init_widget (&result->widget, y, x, count, 4 + max, radio_callback, radio_event);
525 /* 4 is width of "(*) " */
526 result->state = 1;
527 result->pos = 0;
528 result->sel = 0;
529 result->count = count;
530 widget_want_hotkey (result->widget, 1);
532 return result;
536 /* Checkbutton widget */
538 static int check_event (Gpm_Event * event, void *);
540 static cb_ret_t
541 check_callback (Widget * w, widget_msg_t msg, int parm)
543 WCheck *c = (WCheck *) w;
544 Dlg_head *h = c->widget.owner;
546 switch (msg)
548 case WIDGET_HOTKEY:
549 if (c->text.hotkey != NULL)
551 if (g_ascii_tolower ((gchar) c->text.hotkey[0]) == g_ascii_tolower ((gchar) parm))
554 check_callback (w, WIDGET_KEY, ' '); /* make action */
555 return MSG_HANDLED;
558 return MSG_NOT_HANDLED;
560 case WIDGET_KEY:
561 if (parm != ' ')
562 return MSG_NOT_HANDLED;
563 c->state ^= C_BOOL;
564 c->state ^= C_CHANGE;
565 h->callback (h, w, DLG_ACTION, 0, NULL);
566 check_callback (w, WIDGET_FOCUS, ' ');
567 return MSG_HANDLED;
569 case WIDGET_CURSOR:
570 widget_move (&c->widget, 0, 1);
571 return MSG_HANDLED;
573 case WIDGET_FOCUS:
574 case WIDGET_UNFOCUS:
575 case WIDGET_DRAW:
576 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
577 widget_move (&c->widget, 0, 0);
578 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
579 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
580 return MSG_HANDLED;
582 case WIDGET_DESTROY:
583 release_hotkey (c->text);
584 return MSG_HANDLED;
586 default:
587 return default_proc (msg, parm);
591 static int
592 check_event (Gpm_Event * event, void *data)
594 WCheck *c = data;
595 Widget *w = data;
597 if (event->type & (GPM_DOWN | GPM_UP))
599 Dlg_head *h = c->widget.owner;
601 dlg_select_widget (c);
602 if (event->type & GPM_UP)
604 check_callback (w, WIDGET_KEY, ' ');
605 check_callback (w, WIDGET_FOCUS, 0);
606 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
607 return MOU_NORMAL;
610 return MOU_NORMAL;
613 WCheck *
614 check_new (int y, int x, int state, const char *text)
616 WCheck *c = g_new (WCheck, 1);
618 c->text = parse_hotkey (text);
620 init_widget (&c->widget, y, x, 1, 4 + hotkey_width (c->text), check_callback, check_event);
621 /* 4 is width of "[X] " */
622 c->state = state ? C_BOOL : 0;
623 widget_want_hotkey (c->widget, 1);
625 return c;
628 static gboolean
629 save_text_to_clip_file (const char *text)
631 int file;
632 char *fname = NULL;
633 ssize_t ret;
634 size_t str_len;
636 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
637 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
638 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
639 g_free (fname);
641 if (file == -1)
642 return FALSE;
644 str_len = strlen (text);
645 ret = mc_write (file, (char *) text, str_len);
646 mc_close (file);
647 return ret == (ssize_t) str_len;
650 static gboolean
651 load_text_from_clip_file (char **text)
653 char buf[BUF_LARGE];
654 FILE *f;
655 char *fname = NULL;
656 gboolean first = TRUE;
658 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
659 f = fopen (fname, "r");
660 g_free (fname);
662 if (f == NULL)
663 return FALSE;
665 *text = NULL;
667 while (fgets (buf, sizeof (buf), f))
669 size_t len;
671 len = strlen (buf);
672 if (len > 0)
674 if (buf[len - 1] == '\n')
675 buf[len - 1] = '\0';
677 if (first)
679 first = FALSE;
680 *text = g_strdup (buf);
682 else
684 /* remove \n on EOL */
685 char *tmp;
687 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
688 g_free (*text);
689 *text = tmp;
694 fclose (f);
696 return (*text != NULL);
699 static gboolean
700 panel_save_curent_file_to_clip_file (void)
702 gboolean res;
704 if (current_panel->marked == 0)
705 res = save_text_to_clip_file (selection (current_panel)->fname);
706 else
708 int i;
709 gboolean first = TRUE;
710 char *flist = NULL;
712 for (i = 0; i < current_panel->count; i++)
713 if (current_panel->dir.list[i].f.marked != 0)
714 { /* Skip the unmarked ones */
715 if (first)
717 flist = g_strdup (current_panel->dir.list[i].fname);
718 first = FALSE;
720 else
722 /* Add empty lines after the file */
723 char *tmp;
725 tmp =
726 g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
727 g_free (flist);
728 flist = tmp;
732 if (flist != NULL)
734 res = save_text_to_clip_file (flist);
735 g_free (flist);
738 return res;
741 /* Label widget */
743 static cb_ret_t
744 label_callback (Widget * w, widget_msg_t msg, int parm)
746 WLabel *l = (WLabel *) w;
747 Dlg_head *h = l->widget.owner;
749 switch (msg)
751 case WIDGET_INIT:
752 return MSG_HANDLED;
754 /* We don't want to get the focus */
755 case WIDGET_FOCUS:
756 return MSG_NOT_HANDLED;
758 case WIDGET_DRAW:
760 char *p = l->text, *q, c = 0;
761 int y = 0;
762 gboolean disabled = (w->options & W_DISABLED) != 0;
764 if (!l->text)
765 return MSG_HANDLED;
767 if (l->transparent)
768 tty_setcolor (disabled ? DISABLED_COLOR : DEFAULT_COLOR);
769 else
770 tty_setcolor (disabled ? DISABLED_COLOR : h->color[DLG_COLOR_NORMAL]);
772 for (;;)
774 q = strchr (p, '\n');
775 if (q != NULL)
777 c = q[0];
778 q[0] = '\0';
781 widget_move (&l->widget, y, 0);
782 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
784 if (q == NULL)
785 break;
786 q[0] = c;
787 p = q + 1;
788 y++;
790 return MSG_HANDLED;
793 case WIDGET_DESTROY:
794 g_free (l->text);
795 return MSG_HANDLED;
797 default:
798 return default_proc (msg, parm);
802 void
803 label_set_text (WLabel * label, const char *text)
805 int newcols = label->widget.cols;
806 int newlines;
808 if (label->text && text && !strcmp (label->text, text))
809 return; /* Flickering is not nice */
811 g_free (label->text);
813 if (text != NULL)
815 label->text = g_strdup (text);
816 if (label->auto_adjust_cols)
818 str_msg_term_size (text, &newlines, &newcols);
819 if (newcols > label->widget.cols)
820 label->widget.cols = newcols;
821 if (newlines > label->widget.lines)
822 label->widget.lines = newlines;
825 else
826 label->text = NULL;
828 if (label->widget.owner)
829 label_callback ((Widget *) label, WIDGET_DRAW, 0);
831 if (newcols < label->widget.cols)
832 label->widget.cols = newcols;
835 WLabel *
836 label_new (int y, int x, const char *text)
838 WLabel *l;
839 int cols = 1;
840 int lines = 1;
842 if (text != NULL)
843 str_msg_term_size (text, &lines, &cols);
845 l = g_new (WLabel, 1);
846 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
847 l->text = (text != NULL) ? g_strdup (text) : NULL;
848 l->auto_adjust_cols = 1;
849 l->transparent = 0;
850 widget_want_cursor (l->widget, 0);
851 return l;
854 static cb_ret_t
855 hline_callback (Widget * w, widget_msg_t msg, int parm)
857 WHLine *l = (WHLine *) w;
858 Dlg_head *h = l->widget.owner;
860 switch (msg)
862 case WIDGET_INIT:
863 case WIDGET_RESIZED:
864 if (l->auto_adjust_cols)
866 if (((w->owner->flags & DLG_COMPACT) != 0))
868 w->x = w->owner->x;
869 w->cols = w->owner->cols;
871 else
873 w->x = w->owner->x + 1;
874 w->cols = w->owner->cols - 2;
878 case WIDGET_FOCUS:
879 /* We don't want to get the focus */
880 return MSG_NOT_HANDLED;
882 case WIDGET_DRAW:
883 if (l->transparent)
884 tty_setcolor (DEFAULT_COLOR);
885 else
886 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
888 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
890 if (l->auto_adjust_cols)
892 widget_move (w, 0, 0);
893 tty_print_alt_char (ACS_LTEE, FALSE);
894 widget_move (w, 0, w->cols - 1);
895 tty_print_alt_char (ACS_RTEE, FALSE);
897 return MSG_HANDLED;
899 default:
900 return default_proc (msg, parm);
905 WHLine *
906 hline_new (int y, int x, int width)
908 WHLine *l;
909 int cols = width;
910 int lines = 1;
912 l = g_new (WHLine, 1);
913 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
914 l->auto_adjust_cols = (width < 0);
915 l->transparent = FALSE;
916 widget_want_cursor (l->widget, 0);
917 return l;
920 /* Gauge widget (progress indicator) */
921 /* Currently width is hardcoded here for text mode */
922 #define gauge_len 47
924 static cb_ret_t
925 gauge_callback (Widget * w, widget_msg_t msg, int parm)
927 WGauge *g = (WGauge *) w;
928 Dlg_head *h = g->widget.owner;
930 if (msg == WIDGET_INIT)
931 return MSG_HANDLED;
933 /* We don't want to get the focus */
934 if (msg == WIDGET_FOCUS)
935 return MSG_NOT_HANDLED;
937 if (msg == WIDGET_DRAW)
939 widget_move (&g->widget, 0, 0);
940 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
941 if (!g->shown)
942 tty_printf ("%*s", gauge_len, "");
943 else
945 int percentage, columns;
946 long total = g->max, done = g->current;
948 if (total <= 0 || done < 0)
950 done = 0;
951 total = 100;
953 if (done > total)
954 done = total;
955 while (total > 65535)
957 total /= 256;
958 done /= 256;
960 percentage = (200 * done / total + 1) / 2;
961 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
962 tty_print_char ('[');
963 if (g->from_left_to_right)
965 tty_setcolor (GAUGE_COLOR);
966 tty_printf ("%*s", (int) columns, "");
967 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
968 tty_printf ("%*s] %3d%%", (int) (gauge_len - 7 - columns), "", (int) percentage);
970 else
972 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
973 tty_printf ("%*s", gauge_len - columns - 7, "");
974 tty_setcolor (GAUGE_COLOR);
975 tty_printf ("%*s", columns, "");
976 tty_setcolor (h->color[DLG_COLOR_NORMAL]);
977 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
980 return MSG_HANDLED;
983 return default_proc (msg, parm);
986 void
987 gauge_set_value (WGauge * g, int max, int current)
989 if (g->current == current && g->max == max)
990 return; /* Do not flicker */
991 if (max == 0)
992 max = 1; /* I do not like division by zero :) */
994 g->current = current;
995 g->max = max;
996 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
999 void
1000 gauge_show (WGauge * g, int shown)
1002 if (g->shown == shown)
1003 return;
1004 g->shown = shown;
1005 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
1008 WGauge *
1009 gauge_new (int y, int x, int shown, int max, int current)
1011 WGauge *g = g_new (WGauge, 1);
1013 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
1014 g->shown = shown;
1015 if (max == 0)
1016 max = 1; /* I do not like division by zero :) */
1017 g->max = max;
1018 g->current = current;
1019 g->from_left_to_right = TRUE;
1020 widget_want_cursor (g->widget, 0);
1021 return g;
1025 /* Input widget */
1027 /* {{{ history button */
1029 #define LARGE_HISTORY_BUTTON 1
1031 #ifdef LARGE_HISTORY_BUTTON
1032 #define HISTORY_BUTTON_WIDTH 3
1033 #else
1034 #define HISTORY_BUTTON_WIDTH 1
1035 #endif
1037 #define should_show_history_button(in) \
1038 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.owner)
1040 static void
1041 draw_history_button (WInput * in)
1043 char c;
1044 gboolean disabled = (((Widget *) in)->options & W_DISABLED) != 0;
1046 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
1047 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
1048 tty_setcolor (disabled ? DISABLED_COLOR : in->color[WINPUTC_HISTORY]);
1049 #ifdef LARGE_HISTORY_BUTTON
1051 Dlg_head *h;
1052 h = in->widget.owner;
1053 tty_print_string ("[ ]");
1054 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
1056 #endif
1057 tty_print_char (c);
1060 /* }}} history button */
1063 /* Input widgets now have a global kill ring */
1064 /* Pointer to killed data */
1065 static char *kill_buffer = NULL;
1067 static void
1068 input_set_markers (WInput * in, long m1)
1070 in->mark = m1;
1073 static void
1074 input_mark_cmd (WInput * in, gboolean mark)
1076 if (!mark)
1078 in->highlight = FALSE;
1079 input_set_markers (in, 0);
1081 else
1083 in->highlight = TRUE;
1084 input_set_markers (in, in->point);
1088 static gboolean
1089 input_eval_marks (WInput * in, long *start_mark, long *end_mark)
1091 if (in->highlight)
1093 *start_mark = min (in->mark, in->point);
1094 *end_mark = max (in->mark, in->point);
1095 return TRUE;
1097 else
1099 *start_mark = *end_mark = 0;
1100 return FALSE;
1104 static void
1105 delete_region (WInput * in, int x_first, int x_last)
1107 int first = min (x_first, x_last);
1108 int last = max (x_first, x_last);
1109 size_t len;
1111 input_mark_cmd (in, FALSE);
1112 in->point = first;
1113 last = str_offset_to_pos (in->buffer, last);
1114 first = str_offset_to_pos (in->buffer, first);
1115 len = strlen (&in->buffer[last]) + 1;
1116 memmove (&in->buffer[first], &in->buffer[last], len);
1117 in->charpoint = 0;
1118 in->need_push = 1;
1121 void
1122 update_input (WInput * in, int clear_first)
1124 int has_history = 0;
1125 int i;
1126 int buf_len = str_length (in->buffer);
1127 const char *cp;
1128 int pw;
1130 if (should_show_history_button (in))
1131 has_history = HISTORY_BUTTON_WIDTH;
1133 if (in->disable_update)
1134 return;
1136 pw = str_term_width2 (in->buffer, in->point);
1138 /* Make the point visible */
1139 if ((pw < in->term_first_shown) || (pw >= in->term_first_shown + in->field_width - has_history))
1142 in->term_first_shown = pw - (in->field_width / 3);
1143 if (in->term_first_shown < 0)
1144 in->term_first_shown = 0;
1147 /* Adjust the mark */
1148 if (in->mark > buf_len)
1149 in->mark = buf_len;
1151 if (has_history)
1152 draw_history_button (in);
1154 if ((((Widget *) in)->options & W_DISABLED) != 0)
1155 tty_setcolor (DISABLED_COLOR);
1156 else if (in->first)
1157 tty_setcolor (in->color[WINPUTC_UNCHANGED]);
1158 else
1159 tty_setcolor (in->color[WINPUTC_MAIN]);
1161 widget_move (&in->widget, 0, 0);
1163 if (!in->is_password)
1165 if (!in->highlight)
1167 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1168 in->field_width - has_history));
1170 else
1172 long m1, m2;
1173 if (input_eval_marks (in, &m1, &m2))
1175 tty_setcolor (in->color[WINPUTC_MAIN]);
1176 cp = str_term_substring (in->buffer, in->term_first_shown,
1177 in->field_width - has_history);
1178 tty_print_string (cp);
1179 tty_setcolor (in->color[WINPUTC_MARK]);
1180 if (m1 < in->term_first_shown)
1182 widget_move (&in->widget, 0, 0);
1183 tty_print_string (str_term_substring
1184 (in->buffer, in->term_first_shown,
1185 m2 - in->term_first_shown));
1187 else
1189 int sel_width;
1190 widget_move (&in->widget, 0, m1 - in->term_first_shown);
1191 sel_width =
1192 min (m2 - m1,
1193 (in->field_width - has_history) - (str_term_width2 (in->buffer, m1) -
1194 in->term_first_shown));
1195 tty_print_string (str_term_substring (in->buffer, m1, sel_width));
1200 else
1202 cp = str_term_substring (in->buffer, in->term_first_shown, in->field_width - has_history);
1203 for (i = 0; i < in->field_width - has_history; i++)
1205 if (i >= 0)
1207 tty_setcolor (in->color[WINPUTC_MAIN]);
1208 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1210 if (cp[0] != '\0')
1211 str_cnext_char (&cp);
1215 if (clear_first)
1216 in->first = FALSE;
1219 void
1220 winput_set_origin (WInput * in, int x, int field_width)
1222 in->widget.x = x;
1223 in->field_width = in->widget.cols = field_width;
1224 update_input (in, 0);
1227 /* {{{ history saving and loading */
1229 int num_history_items_recorded = 60;
1232 This loads and saves the history of an input line to and from the
1233 widget. It is called with the widgets history name on creation of the
1234 widget, and returns the GList list. It stores histories in the file
1235 ~/.mc/history in using the profile code.
1237 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1238 function) then input_new assigns the default text to be the last text
1239 entered, or "" if not found.
1242 GList *
1243 history_get (const char *input_name)
1245 size_t i;
1246 GList *hist = NULL;
1247 char *profile;
1248 mc_config_t *cfg;
1249 char **keys;
1250 size_t keys_num = 0;
1251 char *this_entry;
1253 if (num_history_items_recorded == 0) /* this is how to disable */
1254 return NULL;
1255 if ((input_name == NULL) || (*input_name == '\0'))
1256 return NULL;
1258 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1259 cfg = mc_config_init (profile);
1261 /* get number of keys */
1262 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1263 g_strfreev (keys);
1265 for (i = 0; i < keys_num; i++)
1267 char key[BUF_TINY];
1268 g_snprintf (key, sizeof (key), "%lu", (unsigned long) i);
1269 this_entry = mc_config_get_string (cfg, input_name, key, "");
1271 if (this_entry != NULL)
1272 hist = list_append_unique (hist, this_entry);
1275 mc_config_deinit (cfg);
1276 g_free (profile);
1278 /* return pointer to the last entry in the list */
1279 return g_list_last (hist);
1282 void
1283 history_put (const char *input_name, GList * h)
1285 int i;
1286 char *profile;
1287 mc_config_t *cfg;
1289 if (num_history_items_recorded == 0) /* this is how to disable */
1290 return;
1291 if ((input_name == NULL) || (*input_name == '\0'))
1292 return;
1293 if (h == NULL)
1294 return;
1296 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1298 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1299 if (i != -1)
1300 close (i);
1302 /* Make sure the history is only readable by the user */
1303 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
1305 g_free (profile);
1306 return;
1309 /* go to end of list */
1310 h = g_list_last (h);
1312 /* go back 60 places */
1313 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1314 h = g_list_previous (h);
1316 cfg = mc_config_init (profile);
1318 if (input_name != NULL)
1319 mc_config_del_group (cfg, input_name);
1321 /* dump history into profile */
1322 for (i = 0; h != NULL; h = g_list_next (h))
1324 char *text = (char *) h->data;
1326 /* We shouldn't have null entries, but let's be sure */
1327 if (text != NULL)
1329 char key[BUF_TINY];
1330 g_snprintf (key, sizeof (key), "%d", i++);
1331 mc_config_set_string (cfg, input_name, key, text);
1335 mc_config_save_file (cfg, NULL);
1336 mc_config_deinit (cfg);
1337 g_free (profile);
1340 /* }}} history saving and loading */
1343 /* {{{ history display */
1345 static const char *
1346 i18n_htitle (void)
1348 return _("History");
1351 typedef struct
1353 Widget *widget;
1354 size_t count;
1355 size_t maxlen;
1356 } dlg_hist_data;
1358 static cb_ret_t
1359 dlg_hist_reposition (Dlg_head * dlg_head)
1361 dlg_hist_data *data;
1362 int x = 0, y, he, wi;
1364 /* guard checks */
1365 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1366 return MSG_NOT_HANDLED;
1368 data = (dlg_hist_data *) dlg_head->data;
1370 y = data->widget->y;
1371 he = data->count + 2;
1373 if (he <= y || y > (LINES - 6))
1375 he = min (he, y - 1);
1376 y -= he;
1378 else
1380 y++;
1381 he = min (he, LINES - y);
1384 if (data->widget->x > 2)
1385 x = data->widget->x - 2;
1387 wi = data->maxlen + 4;
1389 if ((wi + x) > COLS)
1391 wi = min (wi, COLS);
1392 x = COLS - wi;
1395 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1397 return MSG_HANDLED;
1400 static cb_ret_t
1401 dlg_hist_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
1403 switch (msg)
1405 case DLG_RESIZE:
1406 return dlg_hist_reposition (h);
1408 default:
1409 return default_dlg_callback (h, sender, msg, parm, data);
1413 char *
1414 show_hist (GList ** history, Widget * widget)
1416 GList *z, *hlist = NULL, *hi;
1417 size_t maxlen, i, count = 0;
1418 char *r = NULL;
1419 Dlg_head *query_dlg;
1420 WListbox *query_list;
1421 dlg_hist_data hist_data;
1423 if (*history == NULL)
1424 return NULL;
1426 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1428 for (z = *history; z != NULL; z = g_list_previous (z))
1430 WLEntry *entry;
1432 i = str_term_width1 ((char *) z->data);
1433 maxlen = max (maxlen, i);
1434 count++;
1436 entry = g_new0 (WLEntry, 1);
1437 /* history is being reverted here */
1438 entry->text = g_strdup ((char *) z->data);
1439 hlist = g_list_prepend (hlist, entry);
1442 hist_data.widget = widget;
1443 hist_data.count = count;
1444 hist_data.maxlen = maxlen;
1446 query_dlg =
1447 create_dlg (TRUE, 0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1448 "[History-query]", i18n_htitle (), DLG_COMPACT);
1449 query_dlg->data = &hist_data;
1451 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1453 /* this call makes list stick to all sides of dialog, effectively make
1454 it be resized with dialog */
1455 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1457 /* to avoid diplicating of (calculating sizes in two places)
1458 code, call dlg_hist_callback function here, to set dialog and
1459 controls positions.
1460 The main idea - create 4x4 dialog and add 2x2 list in
1461 center of it, and let dialog function resize it to needed
1462 size. */
1463 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1465 if (query_dlg->y < widget->y)
1467 /* draw list entries from bottom upto top */
1468 listbox_set_list (query_list, hlist);
1469 listbox_select_last (query_list);
1471 else
1473 /* draw list entries from top downto bottom */
1474 /* revert history direction */
1475 hlist = g_list_reverse (hlist);
1476 listbox_set_list (query_list, hlist);
1479 if (run_dlg (query_dlg) != B_CANCEL)
1481 char *q;
1483 listbox_get_current (query_list, &q, NULL);
1484 if (q != NULL)
1485 r = g_strdup (q);
1488 /* get modified history from dialog */
1489 z = NULL;
1490 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi))
1492 WLEntry *entry;
1494 entry = (WLEntry *) hi->data;
1495 /* history is being reverted here again */
1496 z = g_list_prepend (z, entry->text);
1497 entry->text = NULL;
1500 /* restore history direction */
1501 if (query_dlg->y < widget->y)
1502 z = g_list_reverse (z);
1504 destroy_dlg (query_dlg);
1506 g_list_foreach (*history, (GFunc) g_free, NULL);
1507 g_list_free (*history);
1508 *history = g_list_last (z);
1510 return r;
1513 static void
1514 do_show_hist (WInput * in)
1516 char *r;
1518 r = show_hist (&in->history, &in->widget);
1519 if (r != NULL)
1521 assign_text (in, r);
1522 g_free (r);
1526 /* }}} history display */
1528 static void
1529 input_destroy (WInput * in)
1531 if (in == NULL)
1533 fprintf (stderr, "Internal error: null Input *\n");
1534 exit (EXIT_FAILURE);
1537 new_input (in);
1539 if (in->history != NULL)
1541 if (!in->is_password && (((Widget *) in)->owner->ret_value != B_CANCEL))
1542 history_put (in->history_name, in->history);
1544 in->history = g_list_first (in->history);
1545 g_list_foreach (in->history, (GFunc) g_free, NULL);
1546 g_list_free (in->history);
1549 g_free (in->buffer);
1550 free_completions (in);
1551 g_free (in->history_name);
1553 g_free (kill_buffer);
1554 kill_buffer = NULL;
1557 void
1558 input_disable_update (WInput * in)
1560 in->disable_update++;
1563 void
1564 input_enable_update (WInput * in)
1566 in->disable_update--;
1567 update_input (in, 0);
1571 static void
1572 push_history (WInput * in, const char *text)
1574 /* input widget where urls with passwords are entered without any
1575 vfs prefix */
1576 const char *password_input_fields[] = {
1577 " Link to a remote machine ",
1578 " FTP to machine ",
1579 " SMB link to machine "
1581 const size_t ELEMENTS = (sizeof (password_input_fields) / sizeof (password_input_fields[0]));
1583 char *t;
1584 size_t i;
1585 gboolean empty;
1587 if (text == NULL)
1588 return;
1590 #ifdef ENABLE_NLS
1591 for (i = 0; i < ELEMENTS; i++)
1592 password_input_fields[i] = _(password_input_fields[i]);
1593 #endif
1595 t = g_strstrip (g_strdup (text));
1596 empty = *t == '\0';
1597 g_free (t);
1598 t = g_strdup (empty ? "" : text);
1600 if (in->history_name != NULL)
1602 /* FIXME: It is the strange code. Rewrite is needed. */
1604 const char *p = in->history_name + 3;
1606 for (i = 0; i < ELEMENTS; i++)
1607 if (strcmp (p, password_input_fields[i]) == 0)
1608 break;
1610 strip_password (t, i >= ELEMENTS);
1613 in->history = list_append_unique (in->history, t);
1614 in->need_push = 0;
1617 /* Cleans the input line and adds the current text to the history */
1618 void
1619 new_input (WInput * in)
1621 push_history (in, in->buffer);
1622 in->need_push = 1;
1623 in->buffer[0] = '\0';
1624 in->point = 0;
1625 in->charpoint = 0;
1626 in->mark = 0;
1627 in->highlight = FALSE;
1628 free_completions (in);
1629 update_input (in, 0);
1632 static void
1633 move_buffer_backward (WInput * in, int start, int end)
1635 int i, pos, len;
1636 int str_len = str_length (in->buffer);
1637 if (start >= str_len || end > str_len + 1)
1638 return;
1640 pos = str_offset_to_pos (in->buffer, start);
1641 len = str_offset_to_pos (in->buffer, end) - pos;
1643 for (i = pos; in->buffer[i + len - 1]; i++)
1644 in->buffer[i] = in->buffer[i + len];
1647 static cb_ret_t
1648 insert_char (WInput * in, int c_code)
1650 size_t i;
1651 int res;
1653 if (in->highlight)
1655 long m1, m2;
1656 if (input_eval_marks (in, &m1, &m2))
1657 delete_region (in, m1, m2);
1659 if (c_code == -1)
1660 return MSG_NOT_HANDLED;
1662 if (in->charpoint >= MB_LEN_MAX)
1663 return MSG_HANDLED;
1665 in->charbuf[in->charpoint] = c_code;
1666 in->charpoint++;
1668 res = str_is_valid_char (in->charbuf, in->charpoint);
1669 if (res < 0)
1671 if (res != -2)
1672 in->charpoint = 0; /* broken multibyte char, skip */
1673 return MSG_HANDLED;
1676 in->need_push = 1;
1677 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size)
1679 /* Expand the buffer */
1680 size_t new_length = in->current_max_size + in->field_width + in->charpoint;
1681 char *narea = g_try_renew (char, in->buffer, new_length);
1682 if (narea)
1684 in->buffer = narea;
1685 in->current_max_size = new_length;
1689 if (strlen (in->buffer) + in->charpoint < in->current_max_size)
1691 /* bytes from begin */
1692 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1693 /* move chars */
1694 size_t rest_bytes = strlen (in->buffer + ins_point);
1696 for (i = rest_bytes + 1; i > 0; i--)
1697 in->buffer[ins_point + i + in->charpoint - 1] = in->buffer[ins_point + i - 1];
1699 memcpy (in->buffer + ins_point, in->charbuf, in->charpoint);
1700 in->point++;
1703 in->charpoint = 0;
1704 return MSG_HANDLED;
1707 static void
1708 beginning_of_line (WInput * in)
1710 in->point = 0;
1711 in->charpoint = 0;
1714 static void
1715 end_of_line (WInput * in)
1717 in->point = str_length (in->buffer);
1718 in->charpoint = 0;
1721 static void
1722 backward_char (WInput * in)
1724 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1726 if (in->point > 0)
1728 in->point -= str_cprev_noncomb_char (&act, in->buffer);
1730 in->charpoint = 0;
1733 static void
1734 forward_char (WInput * in)
1736 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1737 if (act[0] != '\0')
1739 in->point += str_cnext_noncomb_char (&act);
1741 in->charpoint = 0;
1744 static void
1745 forward_word (WInput * in)
1747 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1749 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p)))
1751 str_cnext_char (&p);
1752 in->point++;
1754 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p))
1756 str_cnext_char (&p);
1757 in->point++;
1761 static void
1762 backward_word (WInput * in)
1764 const char *p;
1765 const char *p_tmp;
1767 for (p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1768 (p != in->buffer) && (p[0] == '\0'); str_cprev_char (&p), in->point--);
1770 while (p != in->buffer)
1772 p_tmp = p;
1773 str_cprev_char (&p);
1774 if (!str_isspace (p) && !str_ispunct (p))
1776 p = p_tmp;
1777 break;
1779 in->point--;
1781 while (p != in->buffer)
1783 str_cprev_char (&p);
1784 if (str_isspace (p) || str_ispunct (p))
1785 break;
1787 in->point--;
1791 static void
1792 key_left (WInput * in)
1794 backward_char (in);
1797 static void
1798 key_ctrl_left (WInput * in)
1800 backward_word (in);
1803 static void
1804 key_right (WInput * in)
1806 forward_char (in);
1809 static void
1810 key_ctrl_right (WInput * in)
1812 forward_word (in);
1815 static void
1816 backward_delete (WInput * in)
1818 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1819 int start;
1821 if (in->point == 0)
1822 return;
1824 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1825 move_buffer_backward (in, start, in->point);
1826 in->charpoint = 0;
1827 in->need_push = 1;
1828 in->point = start;
1831 static void
1832 delete_char (WInput * in)
1834 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1835 int end = in->point;
1837 end += str_cnext_noncomb_char (&act);
1839 move_buffer_backward (in, in->point, end);
1840 in->charpoint = 0;
1841 in->need_push = 1;
1844 static void
1845 copy_region (WInput * in, int x_first, int x_last)
1847 int first = min (x_first, x_last);
1848 int last = max (x_first, x_last);
1850 if (last == first)
1852 /* Copy selected files to clipboard */
1853 panel_save_curent_file_to_clip_file ();
1854 /* try use external clipboard utility */
1855 copy_file_to_ext_clip ();
1856 return;
1859 g_free (kill_buffer);
1861 first = str_offset_to_pos (in->buffer, first);
1862 last = str_offset_to_pos (in->buffer, last);
1864 kill_buffer = g_strndup (in->buffer + first, last - first);
1866 save_text_to_clip_file (kill_buffer);
1867 /* try use external clipboard utility */
1868 copy_file_to_ext_clip ();
1871 static void
1872 kill_word (WInput * in)
1874 int old_point = in->point;
1875 int new_point;
1877 forward_word (in);
1878 new_point = in->point;
1879 in->point = old_point;
1881 copy_region (in, old_point, new_point);
1882 delete_region (in, old_point, new_point);
1883 in->need_push = 1;
1884 in->charpoint = 0;
1885 in->charpoint = 0;
1888 static void
1889 back_kill_word (WInput * in)
1891 int old_point = in->point;
1892 int new_point;
1894 backward_word (in);
1895 new_point = in->point;
1896 in->point = old_point;
1898 copy_region (in, old_point, new_point);
1899 delete_region (in, old_point, new_point);
1900 in->need_push = 1;
1903 static void
1904 set_mark (WInput * in)
1906 input_mark_cmd (in, TRUE);
1909 static void
1910 kill_save (WInput * in)
1912 copy_region (in, in->mark, in->point);
1915 static void
1916 kill_region (WInput * in)
1918 kill_save (in);
1919 delete_region (in, in->point, in->mark);
1922 static void
1923 clear_region (WInput * in)
1925 delete_region (in, in->point, in->mark);
1928 static void
1929 yank (WInput * in)
1931 if (kill_buffer != NULL)
1933 char *p;
1935 in->charpoint = 0;
1936 for (p = kill_buffer; *p != '\0'; p++)
1937 insert_char (in, *p);
1938 in->charpoint = 0;
1942 static void
1943 kill_line (WInput * in)
1945 int chp = str_offset_to_pos (in->buffer, in->point);
1946 g_free (kill_buffer);
1947 kill_buffer = g_strdup (&in->buffer[chp]);
1948 in->buffer[chp] = '\0';
1949 in->charpoint = 0;
1952 static void
1953 ins_from_clip (WInput * in)
1955 char *p = NULL;
1957 /* try use external clipboard utility */
1958 paste_to_file_from_ext_clip ();
1960 if (load_text_from_clip_file (&p))
1962 char *pp;
1964 for (pp = p; *pp != '\0'; pp++)
1965 insert_char (in, *pp);
1967 g_free (p);
1971 void
1972 assign_text (WInput * in, const char *text)
1974 free_completions (in);
1975 g_free (in->buffer);
1976 in->buffer = g_strdup (text); /* was in->buffer->text */
1977 in->current_max_size = strlen (in->buffer) + 1;
1978 in->point = str_length (in->buffer);
1979 in->mark = 0;
1980 in->need_push = 1;
1981 in->charpoint = 0;
1984 static void
1985 hist_prev (WInput * in)
1987 GList *prev;
1989 if (!in->history)
1990 return;
1992 if (in->need_push)
1993 push_history (in, in->buffer);
1995 prev = g_list_previous (in->history);
1996 if (prev != NULL)
1998 in->history = prev;
1999 assign_text (in, (char *) prev->data);
2000 in->need_push = 0;
2004 static void
2005 hist_next (WInput * in)
2007 if (in->need_push)
2009 push_history (in, in->buffer);
2010 assign_text (in, "");
2011 return;
2014 if (!in->history)
2015 return;
2017 if (!in->history->next)
2019 assign_text (in, "");
2020 return;
2023 in->history = g_list_next (in->history);
2024 assign_text (in, (char *) in->history->data);
2025 in->need_push = 0;
2028 static void
2029 port_region_marked_for_delete (WInput * in)
2031 in->buffer[0] = '\0';
2032 in->point = 0;
2033 in->first = FALSE;
2034 in->charpoint = 0;
2037 static cb_ret_t
2038 input_execute_cmd (WInput * in, unsigned long command)
2040 cb_ret_t res = MSG_HANDLED;
2042 /* a highlight command like shift-arrow */
2043 if (command == CK_InputLeftHighlight ||
2044 command == CK_InputRightHighlight ||
2045 command == CK_InputWordLeftHighlight ||
2046 command == CK_InputWordRightHighlight ||
2047 command == CK_InputBolHighlight || command == CK_InputEolHighlight)
2049 if (!in->highlight)
2051 input_mark_cmd (in, FALSE); /* clear */
2052 input_mark_cmd (in, TRUE); /* marking on */
2056 switch (command)
2058 case CK_InputForwardWord:
2059 case CK_InputBackwardWord:
2060 case CK_InputForwardChar:
2061 case CK_InputBackwardChar:
2062 if (in->highlight)
2063 input_mark_cmd (in, FALSE);
2066 switch (command)
2068 case CK_InputBol:
2069 case CK_InputBolHighlight:
2070 beginning_of_line (in);
2071 break;
2072 case CK_InputEol:
2073 case CK_InputEolHighlight:
2074 end_of_line (in);
2075 break;
2076 case CK_InputMoveLeft:
2077 case CK_InputLeftHighlight:
2078 key_left (in);
2079 break;
2080 case CK_InputWordLeft:
2081 case CK_InputWordLeftHighlight:
2082 key_ctrl_left (in);
2083 break;
2084 case CK_InputMoveRight:
2085 case CK_InputRightHighlight:
2086 key_right (in);
2087 break;
2088 case CK_InputWordRight:
2089 case CK_InputWordRightHighlight:
2090 key_ctrl_right (in);
2091 break;
2092 case CK_InputBackwardChar:
2093 backward_char (in);
2094 break;
2095 case CK_InputBackwardWord:
2096 backward_word (in);
2097 break;
2098 case CK_InputForwardChar:
2099 forward_char (in);
2100 break;
2101 case CK_InputForwardWord:
2102 forward_word (in);
2103 break;
2104 case CK_InputBackwardDelete:
2105 if (in->highlight)
2107 long m1, m2;
2108 if (input_eval_marks (in, &m1, &m2))
2109 delete_region (in, m1, m2);
2111 else
2113 backward_delete (in);
2115 break;
2116 case CK_InputDeleteChar:
2117 if (in->first)
2118 port_region_marked_for_delete (in);
2119 else if (in->highlight)
2121 long m1, m2;
2122 if (input_eval_marks (in, &m1, &m2))
2123 delete_region (in, m1, m2);
2125 else
2126 delete_char (in);
2127 break;
2128 case CK_InputKillWord:
2129 kill_word (in);
2130 break;
2131 case CK_InputBackwardKillWord:
2132 back_kill_word (in);
2133 break;
2134 case CK_InputSetMark:
2135 set_mark (in);
2136 break;
2137 case CK_InputKillRegion:
2138 kill_region (in);
2139 break;
2140 case CK_InputClearLine:
2141 clear_region (in);
2142 break;
2143 case CK_InputKillSave:
2144 kill_save (in);
2145 break;
2146 case CK_InputYank:
2147 yank (in);
2148 break;
2149 case CK_InputPaste:
2150 ins_from_clip (in);
2151 break;
2152 case CK_InputKillLine:
2153 kill_line (in);
2154 break;
2155 case CK_InputHistoryPrev:
2156 hist_prev (in);
2157 break;
2158 case CK_InputHistoryNext:
2159 hist_next (in);
2160 break;
2161 case CK_InputHistoryShow:
2162 do_show_hist (in);
2163 break;
2164 case CK_InputComplete:
2165 complete (in);
2166 break;
2167 default:
2168 res = MSG_NOT_HANDLED;
2171 if (command != CK_InputLeftHighlight &&
2172 command != CK_InputRightHighlight &&
2173 command != CK_InputWordLeftHighlight &&
2174 command != CK_InputWordRightHighlight &&
2175 command != CK_InputBolHighlight && command != CK_InputEolHighlight)
2177 in->highlight = FALSE;
2180 return res;
2183 /* This function is a test for a special input key used in complete.c */
2184 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
2185 and 2 if it is a complete key */
2187 is_in_input_map (WInput * in, int key)
2189 size_t i;
2190 for (i = 0; input_map[i].key != 0; i++)
2191 if (key == input_map[i].key)
2193 input_execute_cmd (in, input_map[i].command);
2194 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
2196 return 0;
2199 cb_ret_t
2200 handle_char (WInput * in, int key)
2202 cb_ret_t v;
2203 int i;
2205 v = MSG_NOT_HANDLED;
2207 if (quote)
2209 free_completions (in);
2210 v = insert_char (in, key);
2211 update_input (in, 1);
2212 quote = 0;
2213 return v;
2215 for (i = 0; input_map[i].key; i++)
2217 if (key == input_map[i].key)
2219 if (input_map[i].command != CK_InputComplete)
2220 free_completions (in);
2221 input_execute_cmd (in, input_map[i].command);
2222 update_input (in, 1);
2223 v = MSG_HANDLED;
2224 break;
2227 if (input_map[i].command == CK_Ignore_Key)
2229 if (key > 255)
2230 return MSG_NOT_HANDLED;
2231 if (in->first)
2232 port_region_marked_for_delete (in);
2233 free_completions (in);
2234 v = insert_char (in, key);
2236 update_input (in, 1);
2237 return v;
2240 /* Inserts text in input line */
2241 void
2242 stuff (WInput * in, const char *text, int insert_extra_space)
2244 input_disable_update (in);
2245 while (*text != '\0')
2246 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
2247 if (insert_extra_space)
2248 handle_char (in, ' ');
2249 input_enable_update (in);
2250 update_input (in, 1);
2253 void
2254 input_set_point (WInput * in, int pos)
2256 int max_pos = str_length (in->buffer);
2258 if (pos > max_pos)
2259 pos = max_pos;
2260 if (pos != in->point)
2261 free_completions (in);
2262 in->point = pos;
2263 in->charpoint = 0;
2264 update_input (in, 1);
2267 cb_ret_t
2268 input_callback (Widget * w, widget_msg_t msg, int parm)
2270 WInput *in = (WInput *) w;
2271 cb_ret_t v;
2273 switch (msg)
2275 case WIDGET_KEY:
2276 if (parm == XCTRL ('q'))
2278 quote = 1;
2279 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2280 quote = 0;
2281 return v;
2284 /* Keys we want others to handle */
2285 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2286 || parm == KEY_F (10) || parm == '\n')
2287 return MSG_NOT_HANDLED;
2289 /* When pasting multiline text, insert literal Enter */
2290 if ((parm & ~KEY_M_MASK) == '\n')
2292 quote = 1;
2293 v = handle_char (in, '\n');
2294 quote = 0;
2295 return v;
2298 return handle_char (in, parm);
2300 case WIDGET_COMMAND:
2301 return input_execute_cmd (in, parm);
2303 case WIDGET_FOCUS:
2304 case WIDGET_UNFOCUS:
2305 case WIDGET_DRAW:
2306 update_input (in, 0);
2307 return MSG_HANDLED;
2309 case WIDGET_CURSOR:
2310 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2311 - in->term_first_shown);
2312 return MSG_HANDLED;
2314 case WIDGET_DESTROY:
2315 input_destroy (in);
2316 return MSG_HANDLED;
2318 default:
2319 return default_proc (msg, parm);
2323 static int
2324 input_event (Gpm_Event * event, void *data)
2326 WInput *in = data;
2328 if (event->type & GPM_DOWN)
2330 in->first = FALSE;
2331 input_mark_cmd (in, FALSE);
2333 if (event->type & (GPM_DOWN | GPM_DRAG))
2335 dlg_select_widget (in);
2337 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2338 && should_show_history_button (in))
2340 do_show_hist (in);
2342 else
2344 in->point = str_length (in->buffer);
2345 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2346 in->point = str_column_to_pos (in->buffer, event->x + in->term_first_shown - 1);
2348 update_input (in, 1);
2350 /* A lone up mustn't do anything */
2351 if (in->highlight && event->type & (GPM_UP | GPM_DRAG))
2352 return MOU_NORMAL;
2354 if (!(event->type & GPM_DRAG))
2355 input_mark_cmd (in, TRUE);
2357 return MOU_NORMAL;
2360 /** Get default colors for WInput widget.
2361 * @returns default colors
2363 int *
2364 input_get_default_colors (void)
2366 static input_colors_t standart_colors;
2368 standart_colors[WINPUTC_MAIN] = INPUT_COLOR;
2369 standart_colors[WINPUTC_MARK] = INPUT_MARK_COLOR;
2370 standart_colors[WINPUTC_UNCHANGED] = INPUT_UNCHANGED_COLOR;
2371 standart_colors[WINPUTC_HISTORY] = INPUT_HISTORY_COLOR;
2373 return standart_colors;
2376 /** Create new instance of WInput object.
2377 * @param y Y coordinate
2378 * @param x X coordinate
2379 * @param input_colors Array of used colors
2380 * @param width Widget width
2381 * @param def_text Default text filled in widget
2382 * @param histname Name of history
2383 * @param completion_flags Flags for specify type of completions
2384 * @returns WInput object
2386 WInput *
2387 input_new (int y, int x, int *input_colors, int width, const char *def_text,
2388 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2390 WInput *in = g_new (WInput, 1);
2391 size_t initial_buffer_len;
2393 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2395 /* history setup */
2396 in->history_name = NULL;
2397 in->history = NULL;
2398 if ((histname != NULL) && (*histname != '\0'))
2400 in->history_name = g_strdup (histname);
2401 in->history = history_get (histname);
2404 if (def_text == NULL)
2405 def_text = "";
2406 else if (def_text == INPUT_LAST_TEXT)
2408 if ((in->history != NULL) && (in->history->data != NULL))
2409 def_text = (char *) in->history->data;
2410 else
2411 def_text = "";
2414 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2415 in->widget.options |= W_IS_INPUT;
2416 in->completions = NULL;
2417 in->completion_flags = completion_flags;
2418 in->current_max_size = initial_buffer_len;
2419 in->buffer = g_new (char, initial_buffer_len);
2421 memmove (in->color, input_colors, sizeof (input_colors_t));
2423 in->field_width = width;
2424 in->first = TRUE;
2425 in->highlight = FALSE;
2426 in->term_first_shown = 0;
2427 in->disable_update = 0;
2428 in->mark = 0;
2429 in->need_push = 1;
2430 in->is_password = 0;
2432 strcpy (in->buffer, def_text);
2433 in->point = str_length (in->buffer);
2434 in->charpoint = 0;
2436 return in;
2440 /* Listbox widget */
2442 /* Should draw the scrollbar, but currently draws only
2443 * indications that there is more information
2446 static void
2447 listbox_entry_free (void *data)
2449 WLEntry *e = data;
2450 g_free (e->text);
2451 g_free (e);
2454 static void
2455 listbox_drawscroll (WListbox * l)
2457 const int max_line = l->widget.lines - 1;
2458 int line = 0;
2459 int i;
2461 /* Are we at the top? */
2462 widget_move (&l->widget, 0, l->widget.cols);
2463 if (l->top == 0)
2464 tty_print_one_vline (TRUE);
2465 else
2466 tty_print_char ('^');
2468 /* Are we at the bottom? */
2469 widget_move (&l->widget, max_line, l->widget.cols);
2470 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2471 tty_print_one_vline (TRUE);
2472 else
2473 tty_print_char ('v');
2475 /* Now draw the nice relative pointer */
2476 if (l->count != 0)
2477 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2479 for (i = 1; i < max_line; i++)
2481 widget_move (&l->widget, i, l->widget.cols);
2482 if (i != line)
2483 tty_print_one_vline (TRUE);
2484 else
2485 tty_print_char ('*');
2489 static void
2490 listbox_draw (WListbox * l, gboolean focused)
2492 const Dlg_head *h = l->widget.owner;
2493 const gboolean disabled = (((Widget *) l)->options & W_DISABLED) != 0;
2494 const int normalc = disabled ? DISABLED_COLOR : h->color[DLG_COLOR_NORMAL];
2495 int selc = disabled ? DISABLED_COLOR : focused ? h->color[DLG_COLOR_HOT_FOCUS] : h->color[DLG_COLOR_FOCUS];
2497 GList *le;
2498 int pos;
2499 int i;
2500 int sel_line = -1;
2502 le = g_list_nth (l->list, l->top);
2503 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2504 pos = (le == NULL) ? 0 : l->top;
2506 for (i = 0; i < l->widget.lines; i++)
2508 const char *text;
2510 /* Display the entry */
2511 if (pos == l->pos && sel_line == -1)
2513 sel_line = i;
2514 tty_setcolor (selc);
2516 else
2517 tty_setcolor (normalc);
2519 widget_move (&l->widget, i, 1);
2521 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2522 text = "";
2523 else
2525 WLEntry *e = (WLEntry *) le->data;
2526 text = e->text;
2527 le = g_list_next (le);
2528 pos++;
2531 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2534 l->cursor_y = sel_line;
2536 if (l->scrollbar && (l->count > l->widget.lines))
2538 tty_setcolor (normalc);
2539 listbox_drawscroll (l);
2543 static int
2544 listbox_check_hotkey (WListbox * l, int key)
2546 int i;
2547 GList *le;
2549 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
2551 WLEntry *e = (WLEntry *) le->data;
2553 if (e->hotkey == key)
2554 return i;
2557 return (-1);
2560 /* Selects the last entry and scrolls the list to the bottom */
2561 void
2562 listbox_select_last (WListbox * l)
2564 l->pos = l->count - 1;
2565 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2568 /* Selects the first entry and scrolls the list to the top */
2569 void
2570 listbox_select_first (WListbox * l)
2572 l->pos = l->top = 0;
2575 void
2576 listbox_set_list (WListbox * l, GList * list)
2578 listbox_remove_list (l);
2580 if (l != NULL)
2582 l->list = list;
2583 l->top = l->pos = 0;
2584 l->count = g_list_length (list);
2588 void
2589 listbox_remove_list (WListbox * l)
2591 if ((l != NULL) && (l->count != 0))
2593 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2594 g_list_free (l->list);
2595 l->list = NULL;
2596 l->count = l->pos = l->top = 0;
2600 void
2601 listbox_remove_current (WListbox * l)
2603 if ((l != NULL) && (l->count != 0))
2605 GList *current;
2607 current = g_list_nth (l->list, l->pos);
2608 l->list = g_list_remove_link (l->list, current);
2609 listbox_entry_free ((WLEntry *) current->data);
2610 g_list_free_1 (current);
2611 l->count--;
2613 if (l->count == 0)
2614 l->top = l->pos = 0;
2615 else if (l->pos >= l->count)
2616 l->pos = l->count - 1;
2620 void
2621 listbox_select_entry (WListbox * l, int dest)
2623 GList *le;
2624 int pos;
2625 gboolean top_seen = FALSE;
2627 if (dest < 0)
2628 return;
2630 /* Special case */
2631 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le))
2633 if (pos == l->top)
2634 top_seen = TRUE;
2636 if (pos == dest)
2638 l->pos = dest;
2639 if (!top_seen)
2640 l->top = l->pos;
2641 else if (l->pos - l->top >= l->widget.lines)
2642 l->top = l->pos - l->widget.lines + 1;
2643 return;
2647 /* If we are unable to find it, set decent values */
2648 l->pos = l->top = 0;
2651 /* Selects from base the pos element */
2652 static int
2653 listbox_select_pos (WListbox * l, int base, int pos)
2655 int last = l->count - 1;
2657 base += pos;
2658 if (base >= last)
2659 base = last;
2661 return base;
2664 static void
2665 listbox_fwd (WListbox * l)
2667 if (l->pos + 1 >= l->count)
2668 listbox_select_first (l);
2669 else
2670 listbox_select_entry (l, l->pos + 1);
2673 static void
2674 listbox_back (WListbox * l)
2676 if (l->pos <= 0)
2677 listbox_select_last (l);
2678 else
2679 listbox_select_entry (l, l->pos - 1);
2682 static cb_ret_t
2683 listbox_execute_cmd (WListbox * l, unsigned long command)
2685 cb_ret_t ret = MSG_HANDLED;
2686 int i;
2688 switch (command)
2690 case CK_ListboxMoveUp:
2691 listbox_back (l);
2692 break;
2693 case CK_ListboxMoveDown:
2694 listbox_fwd (l);
2695 break;
2696 case CK_ListboxMoveHome:
2697 listbox_select_first (l);
2698 break;
2699 case CK_ListboxMoveEnd:
2700 listbox_select_last (l);
2701 break;
2702 case CK_ListboxMovePgUp:
2703 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++)
2704 listbox_back (l);
2705 break;
2706 case CK_ListboxMovePgDn:
2707 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++)
2708 listbox_fwd (l);
2709 break;
2710 case CK_ListboxDeleteItem:
2711 if (l->deletable)
2713 gboolean is_last = (l->pos + 1 >= l->count);
2714 gboolean is_more = (l->top + l->widget.lines >= l->count);
2716 listbox_remove_current (l);
2717 if ((l->top > 0) && (is_last || is_more))
2718 l->top--;
2720 break;
2721 case CK_ListboxDeleteAll:
2722 if (l->deletable && confirm_history_cleanup
2723 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2724 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
2725 _("Do you want clean this history?"),
2726 D_ERROR, 2, _("&Yes"), _("&No")) == 0))
2727 listbox_remove_list (l);
2728 break;
2729 default:
2730 ret = MSG_NOT_HANDLED;
2733 return ret;
2736 /* Return MSG_HANDLED if we want a redraw */
2737 static cb_ret_t
2738 listbox_key (WListbox * l, int key)
2740 unsigned long command;
2742 if (l->list == NULL)
2743 return MSG_NOT_HANDLED;
2745 /* focus on listbox item N by '0'..'9' keys */
2746 if (key >= '0' && key <= '9')
2748 int oldpos = l->pos;
2749 listbox_select_entry (l, key - '0');
2751 /* need scroll to item? */
2752 if (abs (oldpos - l->pos) > l->widget.lines)
2753 l->top = l->pos;
2755 return MSG_HANDLED;
2758 command = lookup_keymap_command (listbox_map, key);
2759 if (command == CK_Ignore_Key)
2760 return MSG_NOT_HANDLED;
2761 return listbox_execute_cmd (l, command);
2764 static inline void
2765 listbox_destroy (WListbox * l)
2767 listbox_remove_list (l);
2770 static cb_ret_t
2771 listbox_callback (Widget * w, widget_msg_t msg, int parm)
2773 WListbox *l = (WListbox *) w;
2774 Dlg_head *h = l->widget.owner;
2775 cb_ret_t ret_code;
2777 switch (msg)
2779 case WIDGET_INIT:
2780 return MSG_HANDLED;
2782 case WIDGET_HOTKEY:
2784 int pos, action;
2786 pos = listbox_check_hotkey (l, parm);
2787 if (pos < 0)
2788 return MSG_NOT_HANDLED;
2790 listbox_select_entry (l, pos);
2791 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2793 if (l->cback != NULL)
2794 action = l->cback (l);
2795 else
2796 action = LISTBOX_DONE;
2798 if (action == LISTBOX_DONE)
2800 h->ret_value = B_ENTER;
2801 dlg_stop (h);
2804 return MSG_HANDLED;
2807 case WIDGET_KEY:
2808 ret_code = listbox_key (l, parm);
2809 if (ret_code != MSG_NOT_HANDLED)
2811 listbox_draw (l, TRUE);
2812 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2814 return ret_code;
2816 case WIDGET_COMMAND:
2817 return listbox_execute_cmd (l, parm);
2819 case WIDGET_CURSOR:
2820 widget_move (&l->widget, l->cursor_y, 0);
2821 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2822 return MSG_HANDLED;
2824 case WIDGET_FOCUS:
2825 case WIDGET_UNFOCUS:
2826 case WIDGET_DRAW:
2827 listbox_draw (l, msg != WIDGET_UNFOCUS);
2828 return MSG_HANDLED;
2830 case WIDGET_DESTROY:
2831 listbox_destroy (l);
2832 return MSG_HANDLED;
2834 case WIDGET_RESIZED:
2835 return MSG_HANDLED;
2837 default:
2838 return default_proc (msg, parm);
2842 static int
2843 listbox_event (Gpm_Event * event, void *data)
2845 WListbox *l = data;
2846 int i;
2848 Dlg_head *h = l->widget.owner;
2850 /* Single click */
2851 if (event->type & GPM_DOWN)
2852 dlg_select_widget (l);
2854 if (l->list == NULL)
2855 return MOU_NORMAL;
2857 if (event->type & (GPM_DOWN | GPM_DRAG))
2859 int ret = MOU_REPEAT;
2861 if (event->x < 0 || event->x > l->widget.cols)
2862 return ret;
2864 if (event->y < 1)
2865 for (i = -event->y; i >= 0; i--)
2866 listbox_back (l);
2867 else if (event->y > l->widget.lines)
2868 for (i = event->y - l->widget.lines; i > 0; i--)
2869 listbox_fwd (l);
2870 else if (event->buttons & GPM_B_UP)
2872 listbox_back (l);
2873 ret = MOU_NORMAL;
2875 else if (event->buttons & GPM_B_DOWN)
2877 listbox_fwd (l);
2878 ret = MOU_NORMAL;
2880 else
2881 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2883 /* We need to refresh ourselves since the dialog manager doesn't */
2884 /* know about this event */
2885 listbox_draw (l, TRUE);
2886 return ret;
2889 /* Double click */
2890 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE))
2892 int action;
2894 if (event->x < 0 || event->x >= l->widget.cols
2895 || event->y < 1 || event->y > l->widget.lines)
2896 return MOU_NORMAL;
2898 dlg_select_widget (l);
2899 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2901 if (l->cback != NULL)
2902 action = l->cback (l);
2903 else
2904 action = LISTBOX_DONE;
2906 if (action == LISTBOX_DONE)
2908 h->ret_value = B_ENTER;
2909 dlg_stop (h);
2910 return MOU_NORMAL;
2913 return MOU_NORMAL;
2916 WListbox *
2917 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2919 WListbox *l = g_new (WListbox, 1);
2921 if (height <= 0)
2922 height = 1;
2924 init_widget (&l->widget, y, x, height, width, listbox_callback, listbox_event);
2926 l->list = NULL;
2927 l->top = l->pos = 0;
2928 l->count = 0;
2929 l->deletable = deletable;
2930 l->cback = callback;
2931 l->allow_duplicates = TRUE;
2932 l->scrollbar = !tty_is_slow ();
2933 widget_want_hotkey (l->widget, 1);
2934 widget_want_cursor (l->widget, 0);
2936 return l;
2939 static int
2940 listbox_entry_cmp (const void *a, const void *b)
2942 const WLEntry *ea = (const WLEntry *) a;
2943 const WLEntry *eb = (const WLEntry *) b;
2945 return strcmp (ea->text, eb->text);
2948 /* Listbox item adding function */
2949 static inline void
2950 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
2952 switch (pos)
2954 case LISTBOX_APPEND_AT_END:
2955 l->list = g_list_append (l->list, e);
2956 break;
2958 case LISTBOX_APPEND_BEFORE:
2959 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2960 if (l->pos > 0)
2961 l->pos--;
2962 break;
2964 case LISTBOX_APPEND_AFTER:
2965 l->list = g_list_insert (l->list, e, l->pos + 1);
2966 break;
2968 case LISTBOX_APPEND_SORTED:
2969 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2970 break;
2972 default:
2973 return;
2976 l->count++;
2979 char *
2980 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data)
2982 WLEntry *entry;
2984 if (l == NULL)
2985 return NULL;
2987 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2988 return NULL;
2990 entry = g_new (WLEntry, 1);
2991 entry->text = g_strdup (text);
2992 entry->data = data;
2993 entry->hotkey = hotkey;
2995 listbox_append_item (l, entry, pos);
2997 return entry->text;
3001 listbox_search_text (WListbox * l, const char *text)
3003 if (l != NULL)
3005 int i;
3006 GList *le;
3008 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le))
3010 WLEntry *e = (WLEntry *) le->data;
3012 if (strcmp (e->text, text) == 0)
3013 return i;
3017 return (-1);
3020 /* Returns the current string text as well as the associated extra data */
3021 void
3022 listbox_get_current (WListbox * l, char **string, void **extra)
3024 WLEntry *e = NULL;
3025 gboolean ok;
3027 if (l != NULL)
3028 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
3030 ok = (e != NULL);
3032 if (string != NULL)
3033 *string = ok ? e->text : NULL;
3035 if (extra != NULL)
3036 *extra = ok ? e->data : NULL;
3040 /* ButtonBar widget */
3042 /* returns TRUE if a function has been called, FALSE otherwise. */
3043 static gboolean
3044 buttonbar_call (WButtonBar * bb, int i)
3046 cb_ret_t ret = MSG_NOT_HANDLED;
3048 if ((bb != NULL) && (bb->labels[i].command != CK_Ignore_Key))
3049 ret = bb->widget.owner->callback (bb->widget.owner,
3050 (Widget *) bb, DLG_ACTION,
3051 bb->labels[i].command, bb->labels[i].receiver);
3052 return ret;
3055 /* calculate positions of buttons; width is never less than 7 */
3056 static void
3057 buttonbar_init_button_positions (WButtonBar * bb)
3059 int i;
3060 int pos = 0;
3062 if (COLS < BUTTONBAR_LABELS_NUM * 7)
3064 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3066 if (pos + 7 <= COLS)
3067 pos += 7;
3069 bb->labels[i].end_coord = pos;
3072 else
3074 /* Distribute the extra width in a way that the middle vertical line
3075 (between F5 and F6) aligns with the two panels. The extra width
3076 is distributed in this order: F10, F5, F9, F4, ..., F6, F1. */
3077 int lc_div, mod;
3079 lc_div = COLS / BUTTONBAR_LABELS_NUM;
3080 mod = COLS % BUTTONBAR_LABELS_NUM;
3082 for (i = 0; i < BUTTONBAR_LABELS_NUM / 2; i++)
3084 pos += lc_div;
3085 if (BUTTONBAR_LABELS_NUM / 2 - 1 - i < mod / 2)
3086 pos++;
3088 bb->labels[i].end_coord = pos;
3091 for (; i < BUTTONBAR_LABELS_NUM; i++)
3093 pos += lc_div;
3094 if (BUTTONBAR_LABELS_NUM - 1 - i < (mod + 1) / 2)
3095 pos++;
3097 bb->labels[i].end_coord = pos;
3102 /* return width of one button */
3103 static int
3104 buttonbar_get_button_width (const WButtonBar * bb, int i)
3106 if (i == 0)
3107 return bb->labels[0].end_coord;
3108 return bb->labels[i].end_coord - bb->labels[i - 1].end_coord;
3111 static int
3112 buttonbar_get_button_by_x_coord (const WButtonBar * bb, int x)
3114 int i;
3116 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3117 if (bb->labels[i].end_coord > x)
3118 return i;
3120 return (-1);
3123 static cb_ret_t
3124 buttonbar_callback (Widget * w, widget_msg_t msg, int parm)
3126 WButtonBar *bb = (WButtonBar *) w;
3127 int i;
3128 const char *text;
3130 switch (msg)
3132 case WIDGET_FOCUS:
3133 return MSG_NOT_HANDLED;
3135 case WIDGET_HOTKEY:
3136 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3137 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
3138 return MSG_HANDLED;
3139 return MSG_NOT_HANDLED;
3141 case WIDGET_DRAW:
3142 if (bb->visible)
3144 buttonbar_init_button_positions (bb);
3145 widget_move (&bb->widget, 0, 0);
3146 tty_setcolor (DEFAULT_COLOR);
3147 tty_printf ("%-*s", bb->widget.cols, "");
3148 widget_move (&bb->widget, 0, 0);
3150 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3152 int width;
3154 width = buttonbar_get_button_width (bb, i);
3155 if (width <= 0)
3156 break;
3158 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
3159 tty_printf ("%2d", i + 1);
3161 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
3162 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
3163 tty_print_string (str_fit_to_term (text, width - 2, J_LEFT_FIT));
3166 return MSG_HANDLED;
3168 case WIDGET_DESTROY:
3169 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
3170 g_free (bb->labels[i].text);
3171 return MSG_HANDLED;
3173 default:
3174 return default_proc (msg, parm);
3178 static int
3179 buttonbar_event (Gpm_Event * event, void *data)
3181 WButtonBar *bb = data;
3182 int button;
3184 if (!(event->type & GPM_UP))
3185 return MOU_NORMAL;
3186 if (event->y == 2)
3187 return MOU_NORMAL;
3188 button = buttonbar_get_button_by_x_coord (bb, event->x - 1);
3189 if (button >= 0)
3190 buttonbar_call (bb, button);
3191 return MOU_NORMAL;
3194 WButtonBar *
3195 buttonbar_new (gboolean visible)
3197 WButtonBar *bb;
3199 bb = g_new0 (WButtonBar, 1);
3201 init_widget (&bb->widget, LINES - 1, 0, 1, COLS, buttonbar_callback, buttonbar_event);
3202 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
3203 bb->visible = visible;
3204 widget_want_hotkey (bb->widget, 1);
3205 widget_want_cursor (bb->widget, 0);
3207 return bb;
3210 static void
3211 set_label_text (WButtonBar * bb, int lc_index, const char *text)
3213 g_free (bb->labels[lc_index - 1].text);
3214 bb->labels[lc_index - 1].text = g_strdup (text);
3217 /* Find ButtonBar widget in the dialog */
3218 WButtonBar *
3219 find_buttonbar (const Dlg_head * h)
3221 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
3224 void
3225 buttonbar_set_label (WButtonBar * bb, int idx, const char *text,
3226 const struct global_keymap_t *keymap, const Widget * receiver)
3228 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM))
3230 unsigned long command = CK_Ignore_Key;
3232 if (keymap != NULL)
3233 command = lookup_keymap_command (keymap, KEY_F (idx));
3235 if ((text == NULL) || (text[0] == '\0'))
3236 set_label_text (bb, idx, "");
3237 else
3238 set_label_text (bb, idx, text);
3240 bb->labels[idx - 1].command = command;
3241 bb->labels[idx - 1].receiver = (Widget *) receiver;
3245 void
3246 buttonbar_set_visible (WButtonBar * bb, gboolean visible)
3248 bb->visible = visible;
3251 void
3252 buttonbar_redraw (WButtonBar * bb)
3254 if (bb != NULL)
3255 send_message ((Widget *) bb, WIDGET_DRAW, 0);
3258 static cb_ret_t
3259 groupbox_callback (Widget * w, widget_msg_t msg, int parm)
3261 WGroupbox *g = (WGroupbox *) w;
3263 switch (msg)
3265 case WIDGET_INIT:
3266 return MSG_HANDLED;
3268 case WIDGET_FOCUS:
3269 return MSG_NOT_HANDLED;
3271 case WIDGET_DRAW:
3273 gboolean disabled = (w->options & W_DISABLED) != 0;
3274 tty_setcolor (disabled ? DISABLED_COLOR : COLOR_NORMAL);
3275 draw_box (g->widget.owner, g->widget.y - g->widget.owner->y,
3276 g->widget.x - g->widget.owner->x, g->widget.lines, g->widget.cols, TRUE);
3278 if (g->title != NULL)
3280 tty_setcolor (disabled ? DISABLED_COLOR : COLOR_TITLE);
3281 dlg_move (g->widget.owner, g->widget.y - g->widget.owner->y,
3282 g->widget.x - g->widget.owner->x + 1);
3283 tty_print_string (g->title);
3285 return MSG_HANDLED;
3288 case WIDGET_DESTROY:
3289 g_free (g->title);
3290 return MSG_HANDLED;
3292 default:
3293 return default_proc (msg, parm);
3297 WGroupbox *
3298 groupbox_new (int y, int x, int height, int width, const char *title)
3300 WGroupbox *g = g_new (WGroupbox, 1);
3302 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
3304 g->widget.options &= ~W_WANT_CURSOR;
3305 widget_want_hotkey (g->widget, 0);
3307 /* Strip existing spaces, add one space before and after the title */
3308 if (title)
3310 char *t;
3311 t = g_strstrip (g_strdup (title));
3312 g->title = g_strconcat (" ", t, " ", (char *) NULL);
3313 g_free (t);
3316 return g;