WInput: type accuracy.
[midnight-commander.git] / src / widget.c
blobb8415b08e459b3245812289ef943540435618013
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
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 /** \file widget.c
29 * \brief Source: widgets
32 #include <config.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/mouse.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
48 #include "lib/skin.h"
49 #include "lib/mcconfig.h" /* for history loading and saving */
50 #include "lib/vfs/mc-vfs/vfs.h"
51 #include "lib/fileloc.h"
52 #include "lib/strutil.h"
54 #include "dialog.h"
55 #include "widget.h"
56 #include "wtools.h"
58 #include "cmddef.h" /* CK_ cmd name const */
59 #include "keybind.h" /* global_keymap_t */
60 #include "panel.h" /* current_panel */
61 #include "main.h" /* confirm_history_cleanup */
63 const global_keymap_t *input_map;
65 static void
66 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
68 Dlg_head *h = w->parent;
70 tty_setcolor (hotkey
71 ? (focused
72 ? DLG_HOT_FOCUSC (h)
73 : DLG_HOT_NORMALC (h))
74 : (focused
75 ? DLG_FOCUSC (h)
76 : DLG_NORMALC (h)));
79 struct hotkey_t
80 parse_hotkey (const char *text)
82 struct hotkey_t result;
83 const char *cp, *p;
85 /* search for '&', that is not on the of text */
86 cp = strchr (text, '&');
87 if (cp != NULL && cp[1] != '\0') {
88 result.start = g_strndup (text, cp - text);
90 /* skip '&' */
91 cp++;
92 p = str_cget_next_char (cp);
93 result.hotkey = g_strndup (cp, p - cp);
95 cp = p;
96 result.end = g_strdup (cp);
97 } else {
98 result.start = g_strdup (text);
99 result.hotkey = NULL;
100 result.end = NULL;
103 return result;
105 void
106 release_hotkey (const struct hotkey_t hotkey)
108 g_free (hotkey.start);
109 g_free (hotkey.hotkey);
110 g_free (hotkey.end);
114 hotkey_width (const struct hotkey_t hotkey)
116 int result;
118 result = str_term_width1 (hotkey.start);
119 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
120 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
121 return result;
124 static void
125 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
127 widget_selectcolor (w, focused, FALSE);
128 tty_print_string (hotkey.start);
130 if (hotkey.hotkey != NULL) {
131 widget_selectcolor (w, focused, TRUE);
132 tty_print_string (hotkey.hotkey);
133 widget_selectcolor (w, focused, FALSE);
136 if (hotkey.end != NULL)
137 tty_print_string (hotkey.end);
141 /* Default callback for widgets */
142 cb_ret_t
143 default_proc (widget_msg_t msg, int parm)
145 (void) parm;
147 switch (msg) {
148 case WIDGET_INIT:
149 case WIDGET_FOCUS:
150 case WIDGET_UNFOCUS:
151 case WIDGET_DRAW:
152 case WIDGET_DESTROY:
153 case WIDGET_CURSOR:
154 case WIDGET_IDLE:
155 return MSG_HANDLED;
157 default:
158 return MSG_NOT_HANDLED;
162 static int button_event (Gpm_Event *event, void *);
164 int quote = 0;
166 static cb_ret_t
167 button_callback (Widget *w, widget_msg_t msg, int parm)
169 WButton *b = (WButton *) w;
170 int stop = 0;
171 int off = 0;
172 Dlg_head *h = b->widget.parent;
174 switch (msg) {
175 case WIDGET_HOTKEY:
177 * Don't let the default button steal Enter from the current
178 * button. This is a workaround for the flawed event model
179 * when hotkeys are sent to all widgets before the key is
180 * handled by the current widget.
182 if (parm == '\n' && h->current == &b->widget) {
183 button_callback (w, WIDGET_KEY, ' ');
184 return MSG_HANDLED;
187 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
188 button_callback (w, WIDGET_KEY, ' ');
189 return MSG_HANDLED;
192 if (b->text.hotkey != NULL) {
193 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
194 g_ascii_tolower ((gchar)parm)) {
195 button_callback (w, WIDGET_KEY, ' ');
196 return MSG_HANDLED;
199 return MSG_NOT_HANDLED;
201 case WIDGET_KEY:
202 if (parm != ' ' && parm != '\n')
203 return MSG_NOT_HANDLED;
205 if (b->callback)
206 stop = (*b->callback) (b->action);
207 if (!b->callback || stop) {
208 h->ret_value = b->action;
209 dlg_stop (h);
211 return MSG_HANDLED;
213 case WIDGET_CURSOR:
214 switch (b->flags) {
215 case DEFPUSH_BUTTON:
216 off = 3;
217 break;
218 case NORMAL_BUTTON:
219 off = 2;
220 break;
221 case NARROW_BUTTON:
222 off = 1;
223 break;
224 case HIDDEN_BUTTON:
225 default:
226 off = 0;
227 break;
229 widget_move (&b->widget, 0, b->hotpos + off);
230 return MSG_HANDLED;
232 case WIDGET_UNFOCUS:
233 case WIDGET_FOCUS:
234 case WIDGET_DRAW:
235 if (msg == WIDGET_UNFOCUS)
236 b->selected = 0;
237 else if (msg == WIDGET_FOCUS)
238 b->selected = 1;
240 widget_selectcolor (w, b->selected, FALSE);
241 widget_move (w, 0, 0);
243 switch (b->flags) {
244 case DEFPUSH_BUTTON:
245 tty_print_string ("[< ");
246 break;
247 case NORMAL_BUTTON:
248 tty_print_string ("[ ");
249 break;
250 case NARROW_BUTTON:
251 tty_print_string ("[");
252 break;
253 case HIDDEN_BUTTON:
254 default:
255 return MSG_HANDLED;
258 draw_hotkey (w, b->text, b->selected);
260 switch (b->flags) {
261 case DEFPUSH_BUTTON:
262 tty_print_string (" >]");
263 break;
264 case NORMAL_BUTTON:
265 tty_print_string (" ]");
266 break;
267 case NARROW_BUTTON:
268 tty_print_string ("]");
269 break;
271 return MSG_HANDLED;
273 case WIDGET_DESTROY:
274 release_hotkey (b->text);
275 return MSG_HANDLED;
277 default:
278 return default_proc (msg, parm);
282 static int
283 button_event (Gpm_Event *event, void *data)
285 WButton *b = data;
287 if (event->type & (GPM_DOWN|GPM_UP)){
288 Dlg_head *h = b->widget.parent;
289 dlg_select_widget (b);
290 if (event->type & GPM_UP){
291 button_callback ((Widget *) data, WIDGET_KEY, ' ');
292 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
293 return MOU_NORMAL;
296 return MOU_NORMAL;
300 button_get_len (const WButton *b)
302 int ret = hotkey_width (b->text);
303 switch (b->flags) {
304 case DEFPUSH_BUTTON:
305 ret += 6;
306 break;
307 case NORMAL_BUTTON:
308 ret += 4;
309 break;
310 case NARROW_BUTTON:
311 ret += 2;
312 break;
313 case HIDDEN_BUTTON:
314 default:
315 return 0;
317 return ret;
320 WButton *
321 button_new (int y, int x, int action, int flags, const char *text,
322 bcback callback)
324 WButton *b = g_new (WButton, 1);
326 b->action = action;
327 b->flags = flags;
328 b->text = parse_hotkey (text);
330 init_widget (&b->widget, y, x, 1, button_get_len (b),
331 button_callback, button_event);
333 b->selected = 0;
334 b->callback = callback;
335 widget_want_hotkey (b->widget, 1);
336 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
338 return b;
341 const char *
342 button_get_text (const WButton *b)
344 if (b->text.hotkey != NULL)
345 return g_strconcat (b->text.start, "&", b->text.hotkey,
346 b->text.end, (char *) NULL);
347 else
348 return g_strdup (b->text.start);
351 void
352 button_set_text (WButton *b, const char *text)
354 release_hotkey (b->text);
355 b->text = parse_hotkey (text);
356 b->widget.cols = button_get_len (b);
357 dlg_redraw (b->widget.parent);
361 /* Radio button widget */
362 static int radio_event (Gpm_Event *event, void *);
364 static cb_ret_t
365 radio_callback (Widget *w, widget_msg_t msg, int parm)
367 WRadio *r = (WRadio *) w;
368 int i;
369 Dlg_head *h = r->widget.parent;
371 switch (msg) {
372 case WIDGET_HOTKEY:
374 int lp = g_ascii_tolower ((gchar)parm);
376 for (i = 0; i < r->count; i++) {
377 if (r->texts[i].hotkey != NULL) {
378 int c = g_ascii_tolower ((gchar)r->texts[i].hotkey[0]);
380 if (c != lp)
381 continue;
382 r->pos = i;
384 /* Take action */
385 radio_callback (w, WIDGET_KEY, ' ');
386 return MSG_HANDLED;
390 return MSG_NOT_HANDLED;
392 case WIDGET_KEY:
393 switch (parm) {
394 case ' ':
395 r->sel = r->pos;
396 h->callback (h, w, DLG_ACTION, 0, NULL);
397 radio_callback (w, WIDGET_FOCUS, ' ');
398 return MSG_HANDLED;
400 case KEY_UP:
401 case KEY_LEFT:
402 if (r->pos > 0) {
403 r->pos--;
404 return MSG_HANDLED;
406 return MSG_NOT_HANDLED;
408 case KEY_DOWN:
409 case KEY_RIGHT:
410 if (r->count - 1 > r->pos) {
411 r->pos++;
412 return MSG_HANDLED;
415 return MSG_NOT_HANDLED;
417 case WIDGET_CURSOR:
418 h->callback (h, w, DLG_ACTION, 0, NULL);
419 radio_callback (w, WIDGET_FOCUS, ' ');
420 widget_move (&r->widget, r->pos, 1);
421 return MSG_HANDLED;
423 case WIDGET_UNFOCUS:
424 case WIDGET_FOCUS:
425 case WIDGET_DRAW:
426 for (i = 0; i < r->count; i++) {
427 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
428 widget_selectcolor (w, focused, FALSE);
429 widget_move (&r->widget, i, 0);
430 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
431 draw_hotkey (w, r->texts[i], focused);
433 return MSG_HANDLED;
435 case WIDGET_DESTROY:
436 for (i = 0; i < r->count; i++) {
437 release_hotkey (r->texts[i]);
439 g_free (r->texts);
440 return MSG_HANDLED;
442 default:
443 return default_proc (msg, parm);
447 static int
448 radio_event (Gpm_Event *event, void *data)
450 WRadio *r = data;
451 Widget *w = data;
453 if (event->type & (GPM_DOWN|GPM_UP)){
454 Dlg_head *h = r->widget.parent;
456 r->pos = event->y - 1;
457 dlg_select_widget (r);
458 if (event->type & GPM_UP){
459 radio_callback (w, WIDGET_KEY, ' ');
460 radio_callback (w, WIDGET_FOCUS, 0);
461 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
462 return MOU_NORMAL;
465 return MOU_NORMAL;
468 WRadio *
469 radio_new (int y, int x, int count, const char **texts)
471 WRadio *result = g_new (WRadio, 1);
472 int i, max, m;
474 /* Compute the longest string */
475 result->texts = g_new (struct hotkey_t, count);
477 max = 0;
478 for (i = 0; i < count; i++){
479 result->texts[i] = parse_hotkey (texts[i]);
480 m = hotkey_width (result->texts[i]);
481 if (m > max)
482 max = m;
485 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
486 result->state = 1;
487 result->pos = 0;
488 result->sel = 0;
489 result->count = count;
490 widget_want_hotkey (result->widget, 1);
492 return result;
496 /* Checkbutton widget */
498 static int check_event (Gpm_Event *event, void *);
500 static cb_ret_t
501 check_callback (Widget *w, widget_msg_t msg, int parm)
503 WCheck *c = (WCheck *) w;
504 Dlg_head *h = c->widget.parent;
506 switch (msg) {
507 case WIDGET_HOTKEY:
508 if (c->text.hotkey != NULL) {
509 if (g_ascii_tolower ((gchar)c->text.hotkey[0]) ==
510 g_ascii_tolower ((gchar)parm)) {
512 check_callback (w, WIDGET_KEY, ' '); /* make action */
513 return MSG_HANDLED;
516 return MSG_NOT_HANDLED;
518 case WIDGET_KEY:
519 if (parm != ' ')
520 return MSG_NOT_HANDLED;
521 c->state ^= C_BOOL;
522 c->state ^= C_CHANGE;
523 h->callback (h, w, DLG_ACTION, 0, NULL);
524 check_callback (w, WIDGET_FOCUS, ' ');
525 return MSG_HANDLED;
527 case WIDGET_CURSOR:
528 widget_move (&c->widget, 0, 1);
529 return MSG_HANDLED;
531 case WIDGET_FOCUS:
532 case WIDGET_UNFOCUS:
533 case WIDGET_DRAW:
534 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
535 widget_move (&c->widget, 0, 0);
536 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
537 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
538 return MSG_HANDLED;
540 case WIDGET_DESTROY:
541 release_hotkey (c->text);
542 return MSG_HANDLED;
544 default:
545 return default_proc (msg, parm);
549 static int
550 check_event (Gpm_Event *event, void *data)
552 WCheck *c = data;
553 Widget *w = data;
555 if (event->type & (GPM_DOWN|GPM_UP)){
556 Dlg_head *h = c->widget.parent;
558 dlg_select_widget (c);
559 if (event->type & GPM_UP){
560 check_callback (w, WIDGET_KEY, ' ');
561 check_callback (w, WIDGET_FOCUS, 0);
562 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
563 return MOU_NORMAL;
566 return MOU_NORMAL;
569 WCheck *
570 check_new (int y, int x, int state, const char *text)
572 WCheck *c = g_new (WCheck, 1);
574 c->text = parse_hotkey (text);
576 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
577 check_callback, check_event);
578 c->state = state ? C_BOOL : 0;
579 widget_want_hotkey (c->widget, 1);
581 return c;
584 static gboolean
585 save_text_to_clip_file (const char *text)
587 int file;
588 char *fname = NULL;
590 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
591 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
592 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
593 g_free (fname);
595 if (file == -1)
596 return FALSE;
598 mc_write (file, (char *) text, strlen (text));
599 mc_close (file);
600 return TRUE;
603 static gboolean
604 load_text_from_clip_file (char **text)
606 char buf[BUF_LARGE];
607 FILE *f;
608 char *fname = NULL;
609 gboolean first = TRUE;
611 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
612 f = fopen (fname, "r");
613 g_free (fname);
615 if (f == NULL)
616 return FALSE;
618 *text = NULL;
620 while (fgets (buf, sizeof (buf), f)) {
621 size_t len;
623 len = strlen (buf);
624 if ( len > 0 ) {
625 if (buf[len - 1] == '\n')
626 buf[len - 1] = '\0';
628 if (first) {
629 first = FALSE;
630 *text = g_strdup (buf);
631 } else {
632 /* remove \n on EOL */
633 char *tmp;
635 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
636 g_free (*text);
637 *text = tmp;
642 fclose (f);
644 return (*text != NULL);
647 static gboolean
648 panel_save_curent_file_to_clip_file (void)
650 gboolean res;
652 if (current_panel->marked == 0)
653 res = save_text_to_clip_file (selection (current_panel)->fname);
654 else {
655 int i;
656 gboolean first = TRUE;
657 char *flist = NULL;
659 for (i = 0; i < current_panel->count; i++)
660 if (current_panel->dir.list[i].f.marked != 0) { /* Skip the unmarked ones */
661 if (first) {
662 flist = g_strdup (current_panel->dir.list[i].fname);
663 first = FALSE;
664 } else {
665 /* Add empty lines after the file */
666 char *tmp;
668 tmp = g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
669 g_free (flist);
670 flist = tmp;
674 if (flist != NULL) {
675 res = save_text_to_clip_file (flist);
676 g_free (flist);
679 return res;
683 /* Label widget */
685 static cb_ret_t
686 label_callback (Widget *w, widget_msg_t msg, int parm)
688 WLabel *l = (WLabel *) w;
689 Dlg_head *h = l->widget.parent;
691 switch (msg) {
692 case WIDGET_INIT:
693 return MSG_HANDLED;
695 /* We don't want to get the focus */
696 case WIDGET_FOCUS:
697 return MSG_NOT_HANDLED;
699 case WIDGET_DRAW:
701 char *p = l->text, *q, c = 0;
702 int y = 0;
704 if (!l->text)
705 return MSG_HANDLED;
707 if (l->transparent)
708 tty_setcolor (DEFAULT_COLOR);
709 else
710 tty_setcolor (DLG_NORMALC (h));
712 for (;;) {
713 q = strchr (p, '\n');
714 if (q != NULL) {
715 c = q[0];
716 q[0] = '\0';
719 widget_move (&l->widget, y, 0);
720 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
722 if (q == NULL)
723 break;
724 q[0] = c;
725 p = q + 1;
726 y++;
728 return MSG_HANDLED;
731 case WIDGET_DESTROY:
732 g_free (l->text);
733 return MSG_HANDLED;
735 default:
736 return default_proc (msg, parm);
740 void
741 label_set_text (WLabel *label, const char *text)
743 int newcols = label->widget.cols;
744 int newlines;
746 if (label->text && text && !strcmp (label->text, text))
747 return; /* Flickering is not nice */
749 g_free (label->text);
751 if (text != NULL) {
752 label->text = g_strdup (text);
753 if (label->auto_adjust_cols) {
754 str_msg_term_size (text, &newlines, &newcols);
755 if (newcols > label->widget.cols)
756 label->widget.cols = newcols;
757 if (newlines > label->widget.lines)
758 label->widget.lines = newlines;
760 } else label->text = NULL;
762 if (label->widget.parent)
763 label_callback ((Widget *) label, WIDGET_DRAW, 0);
765 if (newcols < label->widget.cols)
766 label->widget.cols = newcols;
769 WLabel *
770 label_new (int y, int x, const char *text)
772 WLabel *l;
773 int cols = 1;
774 int lines = 1;
776 if (text != NULL)
777 str_msg_term_size (text, &lines, &cols);
779 l = g_new (WLabel, 1);
780 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
781 l->text = (text != NULL) ? g_strdup (text) : NULL;
782 l->auto_adjust_cols = 1;
783 l->transparent = 0;
784 widget_want_cursor (l->widget, 0);
785 return l;
788 static cb_ret_t
789 hline_callback (Widget *w, widget_msg_t msg, int parm)
791 WHLine *l = (WHLine *) w;
792 Dlg_head *h = l->widget.parent;
794 switch (msg) {
795 case WIDGET_INIT:
796 case WIDGET_RESIZED:
797 if (l->auto_adjust_cols) {
798 if (((w->parent->flags & DLG_COMPACT) != 0)) {
799 w->x = w->parent->x;
800 w->cols = w->parent->cols;
801 } else {
802 w->x = w->parent->x + 1;
803 w->cols = w->parent->cols - 2;
807 case WIDGET_FOCUS:
808 /* We don't want to get the focus */
809 return MSG_NOT_HANDLED;
811 case WIDGET_DRAW:
812 if (l->transparent)
813 tty_setcolor (DEFAULT_COLOR);
814 else
815 tty_setcolor (DLG_NORMALC (h));
817 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
819 if (l->auto_adjust_cols) {
820 widget_move (w, 0, 0);
821 tty_print_alt_char (ACS_LTEE);
822 widget_move (w, 0, w->cols - 1);
823 tty_print_alt_char (ACS_RTEE);
825 return MSG_HANDLED;
827 default:
828 return default_proc (msg, parm);
833 WHLine *
834 hline_new (int y, int x, int width)
836 WHLine *l;
837 int cols = width;
838 int lines = 1;
840 l = g_new (WHLine, 1);
841 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
842 l->auto_adjust_cols = (width < 0);
843 l->transparent = FALSE;
844 widget_want_cursor (l->widget, 0);
845 return l;
849 /* Gauge widget (progress indicator) */
850 /* Currently width is hardcoded here for text mode */
851 #define gauge_len 47
853 static cb_ret_t
854 gauge_callback (Widget *w, widget_msg_t msg, int parm)
856 WGauge *g = (WGauge *) w;
857 Dlg_head *h = g->widget.parent;
859 if (msg == WIDGET_INIT)
860 return MSG_HANDLED;
862 /* We don't want to get the focus */
863 if (msg == WIDGET_FOCUS)
864 return MSG_NOT_HANDLED;
866 if (msg == WIDGET_DRAW){
867 widget_move (&g->widget, 0, 0);
868 tty_setcolor (DLG_NORMALC (h));
869 if (!g->shown)
870 tty_printf ("%*s", gauge_len, "");
871 else {
872 int percentage, columns;
873 long total = g->max, done = g->current;
875 if (total <= 0 || done < 0) {
876 done = 0;
877 total = 100;
879 if (done > total)
880 done = total;
881 while (total > 65535) {
882 total /= 256;
883 done /= 256;
885 percentage = (200 * done / total + 1) / 2;
886 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
887 tty_print_char ('[');
888 if (g->from_left_to_right) {
889 tty_setcolor (GAUGE_COLOR);
890 tty_printf ("%*s", (int) columns, "");
891 tty_setcolor (DLG_NORMALC (h));
892 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
893 } else {
894 tty_setcolor (DLG_NORMALC (h));
895 tty_printf ("%*s", gauge_len - columns - 7, "");
896 tty_setcolor (GAUGE_COLOR);
897 tty_printf ("%*s", columns, "");
898 tty_setcolor (DLG_NORMALC (h));
899 tty_printf ("] %3d%%", 100 * columns / (gauge_len - 7), percentage);
902 return MSG_HANDLED;
905 return default_proc (msg, parm);
908 void
909 gauge_set_value (WGauge *g, int max, int current)
911 if (g->current == current && g->max == max)
912 return; /* Do not flicker */
913 if (max == 0)
914 max = 1; /* I do not like division by zero :) */
916 g->current = current;
917 g->max = max;
918 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
921 void
922 gauge_show (WGauge *g, int shown)
924 if (g->shown == shown)
925 return;
926 g->shown = shown;
927 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
930 WGauge *
931 gauge_new (int y, int x, int shown, int max, int current)
933 WGauge *g = g_new (WGauge, 1);
935 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
936 g->shown = shown;
937 if (max == 0)
938 max = 1; /* I do not like division by zero :) */
939 g->max = max;
940 g->current = current;
941 g->from_left_to_right = TRUE;
942 widget_want_cursor (g->widget, 0);
943 return g;
947 /* Input widget */
949 /* {{{ history button */
951 #define LARGE_HISTORY_BUTTON 1
953 #ifdef LARGE_HISTORY_BUTTON
954 # define HISTORY_BUTTON_WIDTH 3
955 #else
956 # define HISTORY_BUTTON_WIDTH 1
957 #endif
959 #define should_show_history_button(in) \
960 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
962 static void draw_history_button (WInput * in)
964 char c;
965 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
966 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
967 #ifdef LARGE_HISTORY_BUTTON
969 Dlg_head *h;
970 h = in->widget.parent;
971 tty_setcolor (NORMAL_COLOR);
972 tty_print_string ("[ ]");
973 /* Too distracting: tty_setcolor (MARKED_COLOR); */
974 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
975 tty_print_char (c);
977 #else
978 tty_setcolor (MARKED_COLOR);
979 tty_print_char (c);
980 #endif
983 /* }}} history button */
986 /* Input widgets now have a global kill ring */
987 /* Pointer to killed data */
988 static char *kill_buffer = NULL;
990 void
991 update_input (WInput *in, int clear_first)
993 int has_history = 0;
994 int i;
995 int buf_len = str_length (in->buffer);
996 const char *cp;
997 int pw;
999 if (should_show_history_button (in))
1000 has_history = HISTORY_BUTTON_WIDTH;
1002 if (in->disable_update)
1003 return;
1005 pw = str_term_width2 (in->buffer, in->point);
1007 /* Make the point visible */
1008 if ((pw < in->term_first_shown) ||
1009 (pw >= in->term_first_shown + in->field_width - has_history)) {
1011 in->term_first_shown = pw - (in->field_width / 3);
1012 if (in->term_first_shown < 0)
1013 in->term_first_shown = 0;
1016 /* Adjust the mark */
1017 if (in->mark > buf_len)
1018 in->mark = buf_len;
1020 if (has_history)
1021 draw_history_button (in);
1023 tty_setcolor (in->color);
1025 widget_move (&in->widget, 0, 0);
1027 if (!in->is_password) {
1028 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1029 in->field_width - has_history));
1030 } else {
1031 cp = in->buffer;
1032 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
1033 if (i >= 0) {
1034 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1036 if (cp[0] != '\0') str_cnext_char (&cp);
1040 if (clear_first)
1041 in->first = 0;
1044 void
1045 winput_set_origin (WInput *in, int x, int field_width)
1047 in->widget.x = x;
1048 in->field_width = in->widget.cols = field_width;
1049 update_input (in, 0);
1052 /* {{{ history saving and loading */
1054 int num_history_items_recorded = 60;
1057 This loads and saves the history of an input line to and from the
1058 widget. It is called with the widgets history name on creation of the
1059 widget, and returns the GList list. It stores histories in the file
1060 ~/.mc/history in using the profile code.
1062 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1063 function) then input_new assigns the default text to be the last text
1064 entered, or "" if not found.
1067 GList *
1068 history_get (const char *input_name)
1070 size_t i;
1071 GList *hist = NULL;
1072 char *profile;
1073 mc_config_t *cfg;
1074 char **keys;
1075 size_t keys_num = 0;
1076 char *this_entry;
1078 if (num_history_items_recorded == 0) /* this is how to disable */
1079 return NULL;
1080 if ((input_name == NULL) || (*input_name == '\0'))
1081 return NULL;
1083 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1084 cfg = mc_config_init (profile);
1086 /* get number of keys */
1087 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1088 g_strfreev (keys);
1090 for (i = 0; i < keys_num; i++) {
1091 char key[BUF_TINY];
1092 g_snprintf (key, sizeof (key), "%lu", (unsigned long)i);
1093 this_entry = mc_config_get_string (cfg, input_name, key, "");
1095 if (this_entry != NULL)
1096 hist = list_append_unique (hist, this_entry);
1099 mc_config_deinit (cfg);
1100 g_free (profile);
1102 /* return pointer to the last entry in the list */
1103 return g_list_last (hist);
1106 void
1107 history_put (const char *input_name, GList *h)
1109 int i;
1110 char *profile;
1111 mc_config_t *cfg;
1113 if (num_history_items_recorded == 0) /* this is how to disable */
1114 return;
1115 if ((input_name == NULL) || (*input_name == '\0'))
1116 return;
1117 if (h == NULL)
1118 return;
1120 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1122 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1123 if (i != -1)
1124 close (i);
1126 /* Make sure the history is only readable by the user */
1127 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1128 g_free (profile);
1129 return;
1132 /* go to end of list */
1133 h = g_list_last (h);
1135 /* go back 60 places */
1136 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1137 h = g_list_previous (h);
1139 cfg = mc_config_init (profile);
1141 if (input_name != NULL)
1142 mc_config_del_group (cfg, input_name);
1144 /* dump history into profile */
1145 for (i = 0; h != NULL; h = g_list_next (h)) {
1146 char *text = (char *) h->data;
1148 /* We shouldn't have null entries, but let's be sure */
1149 if (text != NULL) {
1150 char key[BUF_TINY];
1151 g_snprintf (key, sizeof (key), "%d", i++);
1152 mc_config_set_string (cfg, input_name, key, text);
1156 mc_config_save_file (cfg, NULL);
1157 mc_config_deinit(cfg);
1158 g_free (profile);
1161 /* }}} history saving and loading */
1164 /* {{{ history display */
1166 static const char *
1167 i18n_htitle (void)
1169 return _(" History ");
1172 typedef struct {
1173 Widget *widget;
1174 size_t count;
1175 size_t maxlen;
1176 } dlg_hist_data;
1178 static cb_ret_t
1179 dlg_hist_reposition (Dlg_head *dlg_head)
1181 dlg_hist_data *data;
1182 int x = 0, y, he, wi;
1184 /* guard checks */
1185 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1186 return MSG_NOT_HANDLED;
1188 data = (dlg_hist_data *) dlg_head->data;
1190 y = data->widget->y;
1191 he = data->count + 2;
1193 if (he <= y || y > (LINES - 6)) {
1194 he = min (he, y - 1);
1195 y -= he;
1196 } else {
1197 y++;
1198 he = min (he, LINES - y);
1201 if (data->widget->x > 2)
1202 x = data->widget->x - 2;
1204 wi = data->maxlen + 4;
1206 if ((wi + x) > COLS) {
1207 wi = min (wi, COLS);
1208 x = COLS - wi;
1211 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1213 return MSG_HANDLED;
1216 static cb_ret_t
1217 dlg_hist_callback (Dlg_head *h, Widget *sender,
1218 dlg_msg_t msg, int parm, void *data)
1220 switch (msg) {
1221 case DLG_RESIZE:
1222 return dlg_hist_reposition (h);
1224 default:
1225 return default_dlg_callback (h, sender, msg, parm, data);
1229 char *
1230 show_hist (GList **history, Widget *widget)
1232 GList *z, *hlist = NULL, *hi;
1233 size_t maxlen, i, count = 0;
1234 char *r = NULL;
1235 Dlg_head *query_dlg;
1236 WListbox *query_list;
1237 dlg_hist_data hist_data;
1239 if (*history == NULL)
1240 return NULL;
1242 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1244 for (z = *history; z != NULL; z = g_list_previous (z)) {
1245 WLEntry *entry;
1247 i = str_term_width1 ((char *) z->data);
1248 maxlen = max (maxlen, i);
1249 count++;
1251 entry = g_new0 (WLEntry, 1);
1252 /* history is being reverted here */
1253 entry->text = g_strdup ((char *) z->data);
1254 hlist = g_list_prepend (hlist, entry);
1257 hist_data.widget = widget;
1258 hist_data.count = count;
1259 hist_data.maxlen = maxlen;
1261 query_dlg =
1262 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1263 "[History-query]", i18n_htitle (), DLG_COMPACT);
1264 query_dlg->data = &hist_data;
1266 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1268 /* this call makes list stick to all sides of dialog, effectively make
1269 it be resized with dialog */
1270 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1272 /* to avoid diplicating of (calculating sizes in two places)
1273 code, call dlg_hist_callback function here, to set dialog and
1274 controls positions.
1275 The main idea - create 4x4 dialog and add 2x2 list in
1276 center of it, and let dialog function resize it to needed
1277 size. */
1278 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1280 if (query_dlg->y < widget->y) {
1281 /* draw list entries from bottom upto top */
1282 listbox_set_list (query_list, hlist);
1283 listbox_select_last (query_list);
1284 } else {
1285 /* draw list entries from top downto bottom */
1286 /* revert history direction */
1287 hlist = g_list_reverse (hlist);
1288 listbox_set_list (query_list, hlist);
1291 if (run_dlg (query_dlg) != B_CANCEL) {
1292 char *q;
1294 listbox_get_current (query_list, &q, NULL);
1295 if (q != NULL)
1296 r = g_strdup (q);
1299 /* get modified history from dialog */
1300 z = NULL;
1301 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi)) {
1302 WLEntry *entry;
1304 entry = (WLEntry *) hi->data;
1305 /* history is being reverted here again */
1306 z = g_list_prepend (z, entry->text);
1307 entry->text = NULL;
1310 destroy_dlg (query_dlg);
1312 /* restore history direction */
1313 if (query_dlg->y < widget->y)
1314 z = g_list_reverse (z);
1316 g_list_foreach (*history, (GFunc) g_free, NULL);
1317 g_list_free (*history);
1318 *history = g_list_last (z);
1320 return r;
1323 static void
1324 do_show_hist (WInput *in)
1326 char *r;
1328 r = show_hist (&in->history, &in->widget);
1329 if (r != NULL) {
1330 assign_text (in, r);
1331 g_free (r);
1335 /* }}} history display */
1337 static void
1338 input_destroy (WInput *in)
1340 if (in == NULL){
1341 fprintf (stderr, "Internal error: null Input *\n");
1342 exit (1);
1345 new_input (in);
1347 if (in->history != NULL){
1348 if (!in->is_password && (((Widget *) in)->parent->ret_value != B_CANCEL))
1349 history_put (in->history_name, in->history);
1351 in->history = g_list_first (in->history);
1352 g_list_foreach (in->history, (GFunc) g_free, NULL);
1353 g_list_free (in->history);
1356 g_free (in->buffer);
1357 free_completions (in);
1358 g_free (in->history_name);
1361 void
1362 input_disable_update (WInput *in)
1364 in->disable_update++;
1367 void
1368 input_enable_update (WInput *in)
1370 in->disable_update--;
1371 update_input (in, 0);
1375 static void
1376 push_history (WInput *in, const char *text)
1378 /* input widget where urls with passwords are entered without any
1379 vfs prefix */
1380 const char *password_input_fields[] = {
1381 N_(" Link to a remote machine "),
1382 N_(" FTP to machine "),
1383 N_(" SMB link to machine ")
1385 const size_t ELEMENTS = (sizeof (password_input_fields) /
1386 sizeof (password_input_fields[0]));
1388 char *t;
1389 size_t i;
1390 gboolean empty;
1392 if (text == NULL)
1393 return;
1395 #ifdef ENABLE_NLS
1396 for (i = 0; i < ELEMENTS; i++)
1397 password_input_fields[i] = _(password_input_fields[i]);
1398 #endif
1400 t = g_strstrip (g_strdup (text));
1401 empty = *t == '\0';
1402 g_free (t);
1403 t = g_strdup (empty ? "" : text);
1405 if (in->history_name != NULL) {
1406 const char *p = in->history_name + 3;
1408 for (i = 0; i < ELEMENTS; i++)
1409 if (strcmp (p, password_input_fields[i]) == 0)
1410 break;
1412 strip_password (t, i >= ELEMENTS);
1415 in->history = list_append_unique (in->history, t);
1416 in->need_push = 0;
1419 /* Cleans the input line and adds the current text to the history */
1420 void
1421 new_input (WInput *in)
1423 push_history (in, in->buffer);
1424 in->need_push = 1;
1425 in->buffer[0] = '\0';
1426 in->point = 0;
1427 in->charpoint = 0;
1428 in->mark = 0;
1429 free_completions (in);
1430 update_input (in, 0);
1433 static void
1434 move_buffer_backward (WInput *in, int start, int end)
1436 int i, pos, len;
1437 int str_len = str_length (in->buffer);
1438 if (start >= str_len || end > str_len + 1) return;
1440 pos = str_offset_to_pos (in->buffer, start);
1441 len = str_offset_to_pos (in->buffer, end) - pos;
1443 for (i = pos; in->buffer[i + len - 1]; i++)
1444 in->buffer[i] = in->buffer[i + len];
1447 static cb_ret_t
1448 insert_char (WInput *in, int c_code)
1450 size_t i;
1451 int res;
1453 if (c_code == -1)
1454 return MSG_NOT_HANDLED;
1456 if (in->charpoint >= MB_LEN_MAX)
1457 return MSG_HANDLED;
1459 in->charbuf[in->charpoint] = c_code;
1460 in->charpoint++;
1462 res = str_is_valid_char (in->charbuf, in->charpoint);
1463 if (res < 0) {
1464 if (res != -2)
1465 in->charpoint = 0; /* broken multibyte char, skip */
1466 return MSG_HANDLED;
1469 in->need_push = 1;
1470 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1471 /* Expand the buffer */
1472 size_t new_length = in->current_max_size +
1473 in->field_width + in->charpoint;
1474 char *narea = g_try_renew (char, in->buffer, new_length);
1475 if (narea){
1476 in->buffer = narea;
1477 in->current_max_size = new_length;
1481 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1482 /* bytes from begin */
1483 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1484 /* move chars */
1485 size_t rest_bytes = strlen (in->buffer + ins_point);
1487 for (i = rest_bytes + 1; i > 0; i--)
1488 in->buffer[ins_point + i + in->charpoint - 1] =
1489 in->buffer[ins_point + i - 1];
1491 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1492 in->point++;
1495 in->charpoint = 0;
1496 return MSG_HANDLED;
1499 static void
1500 beginning_of_line (WInput *in)
1502 in->point = 0;
1503 in->charpoint = 0;
1506 static void
1507 end_of_line (WInput *in)
1509 in->point = str_length (in->buffer);
1510 in->charpoint = 0;
1513 static void
1514 backward_char (WInput *in)
1516 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1518 if (in->point > 0) {
1519 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1521 in->charpoint = 0;
1524 static void
1525 forward_char (WInput *in)
1527 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1528 if (act[0] != '\0') {
1529 in->point+= str_cnext_noncomb_char (&act);
1531 in->charpoint = 0;
1534 static void
1535 forward_word (WInput * in)
1537 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1539 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1540 str_cnext_char (&p);
1541 in->point++;
1543 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1544 str_cnext_char (&p);
1545 in->point++;
1549 static void
1550 backward_word (WInput *in)
1552 const char *p;
1553 const char *p_tmp;
1555 for (
1556 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1557 (p != in->buffer) && (p[0] == '\0');
1558 str_cprev_char (&p), in->point--
1561 while (p != in->buffer) {
1562 p_tmp = p;
1563 str_cprev_char (&p);
1564 if (!str_isspace (p) && !str_ispunct (p)) {
1565 p = p_tmp;
1566 break;
1568 in->point--;
1570 while (p != in->buffer) {
1571 str_cprev_char (&p);
1572 if (str_isspace (p) || str_ispunct (p))
1573 break;
1575 in->point--;
1579 static void
1580 key_left (WInput *in)
1582 backward_char (in);
1585 static void
1586 key_ctrl_left (WInput *in)
1588 backward_word (in);
1591 static void
1592 key_right (WInput *in)
1594 forward_char (in);
1597 static void
1598 key_ctrl_right (WInput *in)
1600 forward_word (in);
1602 static void
1603 backward_delete (WInput *in)
1605 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1606 int start;
1608 if (in->point == 0)
1609 return;
1611 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1612 move_buffer_backward(in, start, in->point);
1613 in->charpoint = 0;
1614 in->need_push = 1;
1615 in->point = start;
1618 static void
1619 delete_char (WInput *in)
1621 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1622 int end = in->point;
1624 end+= str_cnext_noncomb_char (&act);
1626 move_buffer_backward(in, in->point, end);
1627 in->charpoint = 0;
1628 in->need_push = 1;
1631 static void
1632 copy_region (WInput *in, int x_first, int x_last)
1634 int first = min (x_first, x_last);
1635 int last = max (x_first, x_last);
1637 if (last == first) {
1638 /* Copy selected files to clipboard */
1639 panel_save_curent_file_to_clip_file ();
1640 return;
1643 g_free (kill_buffer);
1645 first = str_offset_to_pos (in->buffer, first);
1646 last = str_offset_to_pos (in->buffer, last);
1648 kill_buffer = g_strndup(in->buffer + first, last - first);
1649 save_text_to_clip_file (kill_buffer);
1652 static void
1653 delete_region (WInput *in, int x_first, int x_last)
1655 int first = min (x_first, x_last);
1656 int last = max (x_first, x_last);
1657 size_t len;
1659 in->point = first;
1660 if (in->mark > first)
1661 in->mark = first;
1662 last = str_offset_to_pos (in->buffer, last);
1663 first = str_offset_to_pos (in->buffer, first);
1664 len = strlen (&in->buffer[last]) + 1;
1665 memmove (&in->buffer[first], &in->buffer[last], len);
1666 in->charpoint = 0;
1667 in->need_push = 1;
1670 static void
1671 kill_word (WInput *in)
1673 int old_point = in->point;
1674 int new_point;
1676 forward_word (in);
1677 new_point = in->point;
1678 in->point = old_point;
1680 copy_region (in, old_point, new_point);
1681 delete_region (in, old_point, new_point);
1682 in->need_push = 1;
1683 in->charpoint = 0;
1684 in->charpoint = 0;
1687 static void
1688 back_kill_word (WInput *in)
1690 int old_point = in->point;
1691 int new_point;
1693 backward_word (in);
1694 new_point = in->point;
1695 in->point = old_point;
1697 copy_region (in, old_point, new_point);
1698 delete_region (in, old_point, new_point);
1699 in->need_push = 1;
1702 static void
1703 set_mark (WInput *in)
1705 in->mark = in->point;
1708 static void
1709 kill_save (WInput *in)
1711 copy_region (in, in->mark, in->point);
1714 static void
1715 kill_region (WInput *in)
1717 kill_save (in);
1718 delete_region (in, in->point, in->mark);
1721 static void
1722 clear_region (WInput *in)
1724 delete_region (in, in->point, in->mark);
1727 static void
1728 yank (WInput *in)
1730 if (kill_buffer != NULL) {
1731 char *p;
1733 in->charpoint = 0;
1734 for (p = kill_buffer; *p != '\0'; p++)
1735 insert_char (in, *p);
1736 in->charpoint = 0;
1740 static void
1741 kill_line (WInput *in)
1743 int chp = str_offset_to_pos (in->buffer, in->point);
1744 g_free (kill_buffer);
1745 kill_buffer = g_strdup (&in->buffer[chp]);
1746 in->buffer[chp] = '\0';
1747 in->charpoint = 0;
1750 static void
1751 ins_from_clip (WInput *in)
1753 char *p = NULL;
1755 if (load_text_from_clip_file (&p)) {
1756 char *pp;
1758 for (pp = p; *pp != '\0'; pp++)
1759 insert_char (in, *pp);
1761 g_free (p);
1765 void
1766 assign_text (WInput *in, const char *text)
1768 free_completions (in);
1769 g_free (in->buffer);
1770 in->buffer = g_strdup (text); /* was in->buffer->text */
1771 in->current_max_size = strlen (in->buffer) + 1;
1772 in->point = str_length (in->buffer);
1773 in->mark = 0;
1774 in->need_push = 1;
1775 in->charpoint = 0;
1778 static void
1779 hist_prev (WInput *in)
1781 GList *prev;
1783 if (!in->history)
1784 return;
1786 if (in->need_push)
1787 push_history (in, in->buffer);
1789 prev = g_list_previous (in->history);
1790 if (prev != NULL) {
1791 in->history = prev;
1792 assign_text (in, (char *) prev->data);
1793 in->need_push = 0;
1797 static void
1798 hist_next (WInput *in)
1800 if (in->need_push) {
1801 push_history (in, in->buffer);
1802 assign_text (in, "");
1803 return;
1806 if (!in->history)
1807 return;
1809 if (!in->history->next) {
1810 assign_text (in, "");
1811 return;
1814 in->history = g_list_next (in->history);
1815 assign_text (in, (char *) in->history->data);
1816 in->need_push = 0;
1819 static void
1820 port_region_marked_for_delete (WInput *in)
1822 in->buffer[0] = '\0';
1823 in->point = 0;
1824 in->first = 0;
1825 in->charpoint = 0;
1828 static cb_ret_t
1829 input_execute_cmd (WInput *in, unsigned long command)
1831 cb_ret_t res = MSG_HANDLED;
1833 switch (command) {
1834 case CK_InputBol:
1835 beginning_of_line (in);
1836 break;
1837 case CK_InputEol:
1838 end_of_line (in);
1839 break;
1840 case CK_InputMoveLeft:
1841 key_left (in);
1842 break;
1843 case CK_InputWordLeft:
1844 key_ctrl_left (in);
1845 break;
1846 case CK_InputMoveRight:
1847 key_right (in);
1848 break;
1849 case CK_InputWordRight:
1850 key_ctrl_right (in);
1851 break;
1852 case CK_InputBackwardChar:
1853 backward_char (in);
1854 break;
1855 case CK_InputBackwardWord:
1856 backward_word (in);
1857 break;
1858 case CK_InputForwardChar:
1859 forward_char (in);
1860 break;
1861 case CK_InputForwardWord:
1862 forward_word (in);
1863 break;
1864 case CK_InputBackwardDelete:
1865 backward_delete (in);
1866 break;
1867 case CK_InputDeleteChar:
1868 delete_char (in);
1869 break;
1870 case CK_InputKillWord:
1871 kill_word (in);
1872 break;
1873 case CK_InputBackwardKillWord:
1874 back_kill_word (in);
1875 break;
1876 case CK_InputSetMark:
1877 set_mark (in);
1878 break;
1879 case CK_InputKillRegion:
1880 kill_region (in);
1881 break;
1882 case CK_InputClearLine:
1883 clear_region (in);
1884 break;
1885 case CK_InputKillSave:
1886 kill_save (in);
1887 break;
1888 case CK_InputYank:
1889 yank (in);
1890 break;
1891 case CK_InputPaste:
1892 ins_from_clip (in);
1893 break;
1894 case CK_InputKillLine:
1895 kill_line (in);
1896 break;
1897 case CK_InputHistoryPrev:
1898 hist_prev (in);
1899 break;
1900 case CK_InputHistoryNext:
1901 hist_next (in);
1902 break;
1903 case CK_InputHistoryShow:
1904 do_show_hist (in);
1905 break;
1906 case CK_InputComplete:
1907 complete (in);
1908 break;
1909 default:
1910 res = MSG_NOT_HANDLED;
1913 return res;
1916 /* This function is a test for a special input key used in complete.c */
1917 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1918 and 2 if it is a complete key */
1920 is_in_input_map (WInput *in, int key)
1922 size_t i;
1923 for (i = 0; input_map[i].key != 0; i++)
1924 if (key == input_map[i].key) {
1925 input_execute_cmd (in, input_map[i].command);
1926 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1928 return 0;
1931 cb_ret_t
1932 handle_char (WInput *in, int key)
1934 cb_ret_t v;
1935 int i;
1937 v = MSG_NOT_HANDLED;
1939 if (quote) {
1940 free_completions (in);
1941 v = insert_char (in, key);
1942 update_input (in, 1);
1943 quote = 0;
1944 return v;
1946 for (i = 0; input_map[i].key; i++) {
1947 if (key == input_map[i].key) {
1948 if (input_map[i].command != CK_InputComplete)
1949 free_completions (in);
1950 input_execute_cmd (in, input_map[i].command);
1951 update_input (in, 1);
1952 v = MSG_HANDLED;
1953 break;
1956 if (input_map[i].command == 0) {
1957 if (key > 255)
1958 return MSG_NOT_HANDLED;
1959 if (in->first)
1960 port_region_marked_for_delete (in);
1961 free_completions (in);
1962 v = insert_char (in, key);
1964 update_input (in, 1);
1965 return v;
1968 /* Inserts text in input line */
1969 void
1970 stuff (WInput *in, const char *text, int insert_extra_space)
1972 input_disable_update (in);
1973 while (*text != '\0')
1974 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1975 if (insert_extra_space)
1976 handle_char (in, ' ');
1977 input_enable_update (in);
1978 update_input (in, 1);
1981 void
1982 input_set_point (WInput *in, int pos)
1984 int max_pos = str_length (in->buffer);
1986 if (pos > max_pos)
1987 pos = max_pos;
1988 if (pos != in->point)
1989 free_completions (in);
1990 in->point = pos;
1991 in->charpoint = 0;
1992 update_input (in, 1);
1995 cb_ret_t
1996 input_callback (Widget *w, widget_msg_t msg, int parm)
1998 WInput *in = (WInput *) w;
1999 cb_ret_t v;
2001 switch (msg) {
2002 case WIDGET_KEY:
2003 if (parm == XCTRL ('q')) {
2004 quote = 1;
2005 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
2006 quote = 0;
2007 return v;
2010 /* Keys we want others to handle */
2011 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2012 || parm == KEY_F (10) || parm == '\n')
2013 return MSG_NOT_HANDLED;
2015 /* When pasting multiline text, insert literal Enter */
2016 if ((parm & ~KEY_M_MASK) == '\n') {
2017 quote = 1;
2018 v = handle_char (in, '\n');
2019 quote = 0;
2020 return v;
2023 return handle_char (in, parm);
2025 case WIDGET_COMMAND:
2026 return input_execute_cmd (in, parm);
2028 case WIDGET_FOCUS:
2029 case WIDGET_UNFOCUS:
2030 case WIDGET_DRAW:
2031 update_input (in, 0);
2032 return MSG_HANDLED;
2034 case WIDGET_CURSOR:
2035 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2036 - in->term_first_shown);
2037 return MSG_HANDLED;
2039 case WIDGET_DESTROY:
2040 input_destroy (in);
2041 return MSG_HANDLED;
2043 default:
2044 return default_proc (msg, parm);
2048 static int
2049 input_event (Gpm_Event * event, void *data)
2051 WInput *in = data;
2053 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2054 dlg_select_widget (in);
2056 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2057 && should_show_history_button (in)) {
2058 do_show_hist (in);
2059 } else {
2060 in->point = str_length (in->buffer);
2061 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2062 in->point = str_column_to_pos (in->buffer, event->x
2063 + in->term_first_shown - 1);
2065 update_input (in, 1);
2067 return MOU_NORMAL;
2070 WInput *
2071 input_new (int y, int x, int color, int width, const char *def_text,
2072 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2074 WInput *in = g_new (WInput, 1);
2075 size_t initial_buffer_len;
2077 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2079 /* history setup */
2080 in->history_name = NULL;
2081 in->history = NULL;
2082 if ((histname != NULL) && (*histname != '\0')) {
2083 in->history_name = g_strdup (histname);
2084 in->history = history_get (histname);
2087 if (def_text == NULL)
2088 def_text = "";
2089 else if (def_text == INPUT_LAST_TEXT) {
2090 if ((in->history != NULL) && (in->history->data != NULL))
2091 def_text = (char *) in->history->data;
2092 else
2093 def_text = "";
2096 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2097 in->widget.options |= W_IS_INPUT;
2098 in->completions = NULL;
2099 in->completion_flags = completion_flags;
2100 in->current_max_size = initial_buffer_len;
2101 in->buffer = g_new (char, initial_buffer_len);
2102 in->color = color;
2103 in->field_width = width;
2104 in->first = 1;
2105 in->term_first_shown = 0;
2106 in->disable_update = 0;
2107 in->mark = 0;
2108 in->need_push = 1;
2109 in->is_password = 0;
2111 strcpy (in->buffer, def_text);
2112 in->point = str_length (in->buffer);
2113 in->charpoint = 0;
2115 return in;
2119 /* Listbox widget */
2121 /* Should draw the scrollbar, but currently draws only
2122 * indications that there is more information
2125 static void
2126 listbox_entry_free (void *data)
2128 WLEntry *e = data;
2129 g_free (e->text);
2130 g_free (e);
2133 static void
2134 listbox_drawscroll (WListbox *l)
2136 const int max_line = l->widget.lines - 1;
2137 int line = 0;
2138 int i;
2140 /* Are we at the top? */
2141 widget_move (&l->widget, 0, l->widget.cols);
2142 if (l->top == 0)
2143 tty_print_one_vline ();
2144 else
2145 tty_print_char ('^');
2147 /* Are we at the bottom? */
2148 widget_move (&l->widget, max_line, l->widget.cols);
2149 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2150 tty_print_one_vline ();
2151 else
2152 tty_print_char ('v');
2154 /* Now draw the nice relative pointer */
2155 if (l->count != 0)
2156 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2158 for (i = 1; i < max_line; i++) {
2159 widget_move (&l->widget, i, l->widget.cols);
2160 if (i != line)
2161 tty_print_one_vline ();
2162 else
2163 tty_print_char ('*');
2167 static void
2168 listbox_draw (WListbox *l, gboolean focused)
2170 const Dlg_head *h = l->widget.parent;
2171 const int normalc = DLG_NORMALC (h);
2172 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2174 GList *le;
2175 int pos;
2176 int i;
2177 int sel_line = -1;
2179 le = g_list_nth (l->list, l->top);
2180 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2181 pos = (le == NULL) ? 0 : l->top;
2183 for (i = 0; i < l->widget.lines; i++) {
2184 const char *text;
2186 /* Display the entry */
2187 if (pos == l->pos && sel_line == -1) {
2188 sel_line = i;
2189 tty_setcolor (selc);
2190 } else
2191 tty_setcolor (normalc);
2193 widget_move (&l->widget, i, 1);
2195 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2196 text = "";
2197 else {
2198 WLEntry *e = (WLEntry *) le->data;
2199 text = e->text;
2200 le = g_list_next (le);
2201 pos++;
2204 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2207 l->cursor_y = sel_line;
2209 if (l->scrollbar && (l->count > l->widget.lines)) {
2210 tty_setcolor (normalc);
2211 listbox_drawscroll (l);
2215 static int
2216 listbox_check_hotkey (WListbox *l, int key)
2218 int i;
2219 GList *le;
2221 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2222 WLEntry *e = (WLEntry *) le->data;
2224 if (e->hotkey == key)
2225 return i;
2228 return (-1);
2231 /* Selects the last entry and scrolls the list to the bottom */
2232 void
2233 listbox_select_last (WListbox *l)
2235 l->pos = l->count - 1;
2236 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2239 /* Selects the first entry and scrolls the list to the top */
2240 void
2241 listbox_select_first (WListbox *l)
2243 l->pos = l->top = 0;
2246 void
2247 listbox_set_list (WListbox *l, GList *list)
2249 listbox_remove_list (l);
2251 if (l != NULL) {
2252 l->list = list;
2253 l->top = l->pos = 0;
2254 l->count = g_list_length (list);
2258 void
2259 listbox_remove_list (WListbox *l)
2261 if ((l != NULL) && (l->count != 0)) {
2262 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2263 g_list_free (l->list);
2264 l->list = NULL;
2265 l->count = l->pos = l->top = 0;
2269 void
2270 listbox_remove_current (WListbox *l)
2272 if ((l != NULL) && (l->count != 0)) {
2273 GList *current;
2275 current = g_list_nth (l->list, l->pos);
2276 l->list = g_list_remove_link (l->list, current);
2277 listbox_entry_free ((WLEntry *) current->data);
2278 g_list_free_1 (current);
2279 l->count--;
2281 if (l->count == 0)
2282 l->top = l->pos = 0;
2283 else if (l->pos >= l->count)
2284 l->pos = l->count - 1;
2288 void
2289 listbox_select_entry (WListbox *l, int dest)
2291 GList *le;
2292 int pos;
2293 gboolean top_seen = FALSE;
2295 if (dest < 0)
2296 return;
2298 /* Special case */
2299 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le)) {
2300 if (pos == l->top)
2301 top_seen = TRUE;
2303 if (pos == dest) {
2304 l->pos = dest;
2305 if (!top_seen)
2306 l->top = l->pos;
2307 else
2308 if (l->pos - l->top >= l->widget.lines)
2309 l->top = l->pos - l->widget.lines + 1;
2310 return;
2314 /* If we are unable to find it, set decent values */
2315 l->pos = l->top = 0;
2318 /* Selects from base the pos element */
2319 static int
2320 listbox_select_pos (WListbox *l, int base, int pos)
2322 int last = l->count - 1;
2324 base += pos;
2325 if (base >= last)
2326 base = last;
2328 return base;
2331 static void
2332 listbox_fwd (WListbox *l)
2334 if (l->pos + 1 >= l->count)
2335 listbox_select_first (l);
2336 else
2337 listbox_select_entry (l, l->pos + 1);
2340 static void
2341 listbox_back (WListbox *l)
2343 if (l->pos <= 0)
2344 listbox_select_last (l);
2345 else
2346 listbox_select_entry (l, l->pos - 1);
2349 /* Return MSG_HANDLED if we want a redraw */
2350 static cb_ret_t
2351 listbox_key (WListbox *l, int key)
2353 int i;
2355 cb_ret_t j = MSG_NOT_HANDLED;
2357 if (l->list == NULL)
2358 return MSG_NOT_HANDLED;
2360 /* focus on listbox item N by '0'..'9' keys */
2361 if (key >= '0' && key <= '9') {
2362 int oldpos = l->pos;
2363 listbox_select_entry (l, key - '0');
2365 /* need scroll to item? */
2366 if (abs (oldpos - l->pos) > l->widget.lines)
2367 l->top = l->pos;
2369 return MSG_HANDLED;
2372 switch (key){
2373 case KEY_HOME:
2374 case KEY_A1:
2375 case ALT ('<'):
2376 listbox_select_first (l);
2377 return MSG_HANDLED;
2379 case KEY_END:
2380 case KEY_C1:
2381 case ALT ('>'):
2382 listbox_select_last (l);
2383 return MSG_HANDLED;
2385 case XCTRL('p'):
2386 case KEY_UP:
2387 listbox_back (l);
2388 return MSG_HANDLED;
2390 case XCTRL('n'):
2391 case KEY_DOWN:
2392 listbox_fwd (l);
2393 return MSG_HANDLED;
2395 case KEY_NPAGE:
2396 case XCTRL('v'):
2397 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++) {
2398 listbox_fwd (l);
2399 j = MSG_HANDLED;
2401 break;
2403 case KEY_PPAGE:
2404 case ALT('v'):
2405 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++) {
2406 listbox_back (l);
2407 j = MSG_HANDLED;
2409 break;
2411 case KEY_DC:
2412 case 'd':
2413 if (l->deletable) {
2414 gboolean is_last = (l->pos + 1 >= l->count);
2415 gboolean is_more = (l->top + l->widget.lines >= l->count);
2417 listbox_remove_current (l);
2418 if ((l->top > 0) && (is_last || is_more))
2419 l->top--;
2421 return MSG_HANDLED;
2423 case (KEY_M_SHIFT | KEY_DC):
2424 case 'D':
2425 if (l->deletable && confirm_history_cleanup
2426 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2427 && (query_dialog (Q_("DialogTitle|History cleanup"),
2428 _("Do you want clean this history?"),
2429 D_ERROR, 2, _("&Yes"), _("&No")) == 0)) {
2430 listbox_remove_list (l);
2431 j = MSG_HANDLED;
2433 break;
2435 default:
2436 break;
2439 return j;
2442 static inline void
2443 listbox_destroy (WListbox *l)
2445 /* don't delete list in modifable listbox */
2446 if (!l->deletable)
2447 listbox_remove_list (l);
2450 static cb_ret_t
2451 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2453 WListbox *l = (WListbox *) w;
2454 Dlg_head *h = l->widget.parent;
2455 cb_ret_t ret_code;
2457 switch (msg) {
2458 case WIDGET_INIT:
2459 return MSG_HANDLED;
2461 case WIDGET_HOTKEY:
2463 int pos, action;
2465 pos = listbox_check_hotkey (l, parm);
2466 if (pos < 0)
2467 return MSG_NOT_HANDLED;
2469 listbox_select_entry (l, pos);
2470 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2472 if (l->cback != NULL)
2473 action = l->cback (l);
2474 else
2475 action = LISTBOX_DONE;
2477 if (action == LISTBOX_DONE) {
2478 h->ret_value = B_ENTER;
2479 dlg_stop (h);
2482 return MSG_HANDLED;
2485 case WIDGET_KEY:
2486 ret_code = listbox_key (l, parm);
2487 if (ret_code != MSG_NOT_HANDLED) {
2488 listbox_draw (l, TRUE);
2489 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2491 return ret_code;
2493 case WIDGET_CURSOR:
2494 widget_move (&l->widget, l->cursor_y, 0);
2495 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2496 return MSG_HANDLED;
2498 case WIDGET_FOCUS:
2499 case WIDGET_UNFOCUS:
2500 case WIDGET_DRAW:
2501 listbox_draw (l, msg != WIDGET_UNFOCUS);
2502 return MSG_HANDLED;
2504 case WIDGET_DESTROY:
2505 listbox_destroy (l);
2506 return MSG_HANDLED;
2508 case WIDGET_RESIZED:
2509 return MSG_HANDLED;
2511 default:
2512 return default_proc (msg, parm);
2516 static int
2517 listbox_event (Gpm_Event *event, void *data)
2519 WListbox *l = data;
2520 int i;
2522 Dlg_head *h = l->widget.parent;
2524 /* Single click */
2525 if (event->type & GPM_DOWN)
2526 dlg_select_widget (l);
2528 if (l->list == NULL)
2529 return MOU_NORMAL;
2531 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2532 int ret = MOU_REPEAT;
2534 if (event->x < 0 || event->x > l->widget.cols)
2535 return ret;
2537 if (event->y < 1)
2538 for (i = -event->y; i >= 0; i--)
2539 listbox_back (l);
2540 else if (event->y > l->widget.lines)
2541 for (i = event->y - l->widget.lines; i > 0; i--)
2542 listbox_fwd (l);
2543 else if (event->buttons & GPM_B_UP) {
2544 listbox_back (l);
2545 ret = MOU_NORMAL;
2546 } else if (event->buttons & GPM_B_DOWN) {
2547 listbox_fwd (l);
2548 ret = MOU_NORMAL;
2549 } else
2550 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2552 /* We need to refresh ourselves since the dialog manager doesn't */
2553 /* know about this event */
2554 listbox_draw (l, TRUE);
2555 return ret;
2558 /* Double click */
2559 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2560 int action;
2562 if (event->x < 0 || event->x >= l->widget.cols
2563 || event->y < 1 || event->y > l->widget.lines)
2564 return MOU_NORMAL;
2566 dlg_select_widget (l);
2567 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2569 if (l->cback != NULL)
2570 action = l->cback (l);
2571 else
2572 action = LISTBOX_DONE;
2574 if (action == LISTBOX_DONE) {
2575 h->ret_value = B_ENTER;
2576 dlg_stop (h);
2577 return MOU_NORMAL;
2580 return MOU_NORMAL;
2583 WListbox *
2584 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2586 WListbox *l = g_new (WListbox, 1);
2588 if (height <= 0)
2589 height = 1;
2591 init_widget (&l->widget, y, x, height, width,
2592 listbox_callback, listbox_event);
2594 l->list = NULL;
2595 l->top = l->pos = 0;
2596 l->count = 0;
2597 l->deletable = deletable;
2598 l->cback = callback;
2599 l->allow_duplicates = TRUE;
2600 l->scrollbar = !tty_is_slow ();
2601 widget_want_hotkey (l->widget, 1);
2603 return l;
2606 static int
2607 listbox_entry_cmp (const void *a, const void *b)
2609 const WLEntry *ea = (const WLEntry *) a;
2610 const WLEntry *eb = (const WLEntry *) b;
2612 return strcmp (ea->text, eb->text);
2615 /* Listbox item adding function */
2616 static inline void
2617 listbox_append_item (WListbox *l, WLEntry *e, listbox_append_t pos)
2619 switch (pos) {
2620 case LISTBOX_APPEND_AT_END:
2621 l->list = g_list_append (l->list, e);
2622 break;
2624 case LISTBOX_APPEND_BEFORE:
2625 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2626 if (l->pos > 0)
2627 l->pos--;
2628 break;
2630 case LISTBOX_APPEND_AFTER:
2631 l->list = g_list_insert (l->list, e, l->pos + 1);
2632 break;
2634 case LISTBOX_APPEND_SORTED:
2635 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2636 break;
2638 default:
2639 return;
2642 l->count++;
2645 char *
2646 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey,
2647 const char *text, void *data)
2649 WLEntry *entry;
2651 if (l == NULL)
2652 return NULL;
2654 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2655 return NULL;
2657 entry = g_new (WLEntry, 1);
2658 entry->text = g_strdup (text);
2659 entry->data = data;
2660 entry->hotkey = hotkey;
2662 listbox_append_item (l, entry, pos);
2664 return entry->text;
2668 listbox_search_text (WListbox *l, const char *text)
2670 if (l != NULL) {
2671 int i;
2672 GList *le;
2674 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2675 WLEntry *e = (WLEntry *) le->data;
2677 if (strcmp (e->text, text) == 0)
2678 return i;
2682 return (-1);
2685 /* Returns the current string text as well as the associated extra data */
2686 void
2687 listbox_get_current (WListbox *l, char **string, void **extra)
2689 WLEntry *e = NULL;
2690 gboolean ok;
2692 if (l != NULL)
2693 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2695 ok = (e != NULL);
2697 if (string != NULL)
2698 *string = ok ? e->text : NULL;
2700 if (extra != NULL)
2701 *extra = ok ? e->data : NULL;
2705 /* ButtonBar widget */
2707 /* returns TRUE if a function has been called, FALSE otherwise. */
2708 static gboolean
2709 buttonbar_call (WButtonBar *bb, int i)
2711 cb_ret_t ret = MSG_NOT_HANDLED;
2713 if (bb != NULL)
2714 ret = bb->widget.parent->callback (bb->widget.parent,
2715 (Widget *) bb, DLG_ACTION,
2716 bb->labels[i].command,
2717 bb->labels[i].receiver);
2718 return ret;
2721 /* calculate width of one button, width is never lesser than 7 */
2722 static int
2723 buttonbat_get_button_width (void)
2725 int result = COLS / BUTTONBAR_LABELS_NUM;
2726 return (result >= 7) ? result : 7;
2729 static cb_ret_t
2730 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2732 WButtonBar *bb = (WButtonBar *) w;
2733 int i;
2734 const char *text;
2736 switch (msg) {
2737 case WIDGET_FOCUS:
2738 return MSG_NOT_HANDLED;
2740 case WIDGET_HOTKEY:
2741 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2742 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2743 return MSG_HANDLED;
2744 return MSG_NOT_HANDLED;
2746 case WIDGET_DRAW:
2747 if (bb->visible) {
2748 int offset = 0;
2749 int count_free_positions;
2751 widget_move (&bb->widget, 0, 0);
2752 tty_setcolor (DEFAULT_COLOR);
2753 bb->btn_width = buttonbat_get_button_width ();
2754 tty_printf ("%-*s", bb->widget.cols, "");
2755 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2757 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2758 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2759 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2760 tty_printf ("%2d", i + 1);
2761 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2762 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2763 tty_print_string (str_fit_to_term (
2764 text,
2765 bb->btn_width - 2 + (int)(offset < count_free_positions),
2766 J_LEFT_FIT));
2768 if (count_free_positions != 0 && offset < count_free_positions)
2769 offset++;
2772 return MSG_HANDLED;
2774 case WIDGET_DESTROY:
2775 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2776 g_free (bb->labels[i].text);
2777 return MSG_HANDLED;
2779 default:
2780 return default_proc (msg, parm);
2784 static int
2785 buttonbar_event (Gpm_Event *event, void *data)
2787 WButtonBar *bb = data;
2788 int button;
2790 if (!(event->type & GPM_UP))
2791 return MOU_NORMAL;
2792 if (event->y == 2)
2793 return MOU_NORMAL;
2794 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2795 if (button < BUTTONBAR_LABELS_NUM)
2796 buttonbar_call (bb, button);
2797 return MOU_NORMAL;
2800 WButtonBar *
2801 buttonbar_new (gboolean visible)
2803 WButtonBar *bb;
2805 bb = g_new0 (WButtonBar, 1);
2807 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2808 buttonbar_callback, buttonbar_event);
2809 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2810 bb->visible = visible;
2811 widget_want_hotkey (bb->widget, 1);
2812 widget_want_cursor (bb->widget, 0);
2813 bb->btn_width = buttonbat_get_button_width ();
2815 return bb;
2818 static void
2819 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2821 g_free (bb->labels[lc_index - 1].text);
2822 bb->labels[lc_index - 1].text = g_strdup (text);
2825 /* Find ButtonBar widget in the dialog */
2826 WButtonBar *
2827 find_buttonbar (const Dlg_head *h)
2829 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2832 void
2833 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2834 const struct global_keymap_t *keymap, const Widget *receiver)
2836 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2837 unsigned long command = CK_Ignore_Key;
2839 if (keymap != NULL)
2840 command = lookup_keymap_command (keymap, KEY_F (idx));
2842 if ((text == NULL) || (text[0] == '\0'))
2843 set_label_text (bb, idx, "");
2844 else
2845 set_label_text (bb, idx, text);
2847 bb->labels[idx - 1].command = command;
2848 bb->labels[idx - 1].receiver = (Widget *) receiver;
2852 void
2853 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2855 bb->visible = visible;
2858 void
2859 buttonbar_redraw (WButtonBar *bb)
2861 if (bb != NULL)
2862 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2865 static cb_ret_t
2866 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2868 WGroupbox *g = (WGroupbox *) w;
2870 switch (msg) {
2871 case WIDGET_INIT:
2872 return MSG_HANDLED;
2874 case WIDGET_FOCUS:
2875 return MSG_NOT_HANDLED;
2877 case WIDGET_DRAW:
2878 tty_setcolor (COLOR_NORMAL);
2879 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2880 g->widget.x - g->widget.parent->x, g->widget.lines,
2881 g->widget.cols);
2883 tty_setcolor (COLOR_HOT_NORMAL);
2884 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2885 g->widget.x - g->widget.parent->x + 1);
2886 tty_print_string (g->title);
2887 return MSG_HANDLED;
2889 case WIDGET_DESTROY:
2890 g_free (g->title);
2891 return MSG_HANDLED;
2893 default:
2894 return default_proc (msg, parm);
2898 WGroupbox *
2899 groupbox_new (int y, int x, int height, int width, const char *title)
2901 WGroupbox *g = g_new (WGroupbox, 1);
2903 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2905 g->widget.options &= ~W_WANT_CURSOR;
2906 widget_want_hotkey (g->widget, 0);
2908 /* Strip existing spaces, add one space before and after the title */
2909 if (title) {
2910 char *t;
2911 t = g_strstrip (g_strdup (title));
2912 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2913 g_free (t);
2916 return g;