Updated italian translation.
[midnight-commander.git] / src / widget.c
blobcfa485f6d70e809a74c4e8c930a5cfe3b08140b8
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;
789 /* Gauge widget (progress indicator) */
790 /* Currently width is hardcoded here for text mode */
791 #define gauge_len 47
793 static cb_ret_t
794 gauge_callback (Widget *w, widget_msg_t msg, int parm)
796 WGauge *g = (WGauge *) w;
797 Dlg_head *h = g->widget.parent;
799 if (msg == WIDGET_INIT)
800 return MSG_HANDLED;
802 /* We don't want to get the focus */
803 if (msg == WIDGET_FOCUS)
804 return MSG_NOT_HANDLED;
806 if (msg == WIDGET_DRAW){
807 widget_move (&g->widget, 0, 0);
808 tty_setcolor (DLG_NORMALC (h));
809 if (!g->shown)
810 tty_printf ("%*s", gauge_len, "");
811 else {
812 int percentage, columns;
813 long total = g->max, done = g->current;
815 if (total <= 0 || done < 0) {
816 done = 0;
817 total = 100;
819 if (done > total)
820 done = total;
821 while (total > 65535) {
822 total /= 256;
823 done /= 256;
825 percentage = (200 * done / total + 1) / 2;
826 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
827 tty_print_char ('[');
828 tty_setcolor (GAUGE_COLOR);
829 tty_printf ("%*s", (int) columns, "");
830 tty_setcolor (DLG_NORMALC (h));
831 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
833 return MSG_HANDLED;
836 return default_proc (msg, parm);
839 void
840 gauge_set_value (WGauge *g, int max, int current)
842 if (g->current == current && g->max == max)
843 return; /* Do not flicker */
844 if (max == 0)
845 max = 1; /* I do not like division by zero :) */
847 g->current = current;
848 g->max = max;
849 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
852 void
853 gauge_show (WGauge *g, int shown)
855 if (g->shown == shown)
856 return;
857 g->shown = shown;
858 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
861 WGauge *
862 gauge_new (int y, int x, int shown, int max, int current)
864 WGauge *g = g_new (WGauge, 1);
866 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
867 g->shown = shown;
868 if (max == 0)
869 max = 1; /* I do not like division by zero :) */
870 g->max = max;
871 g->current = current;
872 widget_want_cursor (g->widget, 0);
873 return g;
877 /* Input widget */
879 /* {{{ history button */
881 #define LARGE_HISTORY_BUTTON 1
883 #ifdef LARGE_HISTORY_BUTTON
884 # define HISTORY_BUTTON_WIDTH 3
885 #else
886 # define HISTORY_BUTTON_WIDTH 1
887 #endif
889 #define should_show_history_button(in) \
890 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
892 static void draw_history_button (WInput * in)
894 char c;
895 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
896 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
897 #ifdef LARGE_HISTORY_BUTTON
899 Dlg_head *h;
900 h = in->widget.parent;
901 tty_setcolor (NORMAL_COLOR);
902 tty_print_string ("[ ]");
903 /* Too distracting: tty_setcolor (MARKED_COLOR); */
904 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
905 tty_print_char (c);
907 #else
908 tty_setcolor (MARKED_COLOR);
909 tty_print_char (c);
910 #endif
913 /* }}} history button */
916 /* Input widgets now have a global kill ring */
917 /* Pointer to killed data */
918 static char *kill_buffer = 0;
920 void
921 update_input (WInput *in, int clear_first)
923 int has_history = 0;
924 int i;
925 int buf_len = str_length (in->buffer);
926 const char *cp;
927 int pw;
929 if (should_show_history_button (in))
930 has_history = HISTORY_BUTTON_WIDTH;
932 if (in->disable_update)
933 return;
935 pw = str_term_width2 (in->buffer, in->point);
937 /* Make the point visible */
938 if ((pw < in->term_first_shown) ||
939 (pw >= in->term_first_shown + in->field_width - has_history)) {
941 in->term_first_shown = pw - (in->field_width / 3);
942 if (in->term_first_shown < 0)
943 in->term_first_shown = 0;
946 /* Adjust the mark */
947 if (in->mark > buf_len)
948 in->mark = buf_len;
950 if (has_history)
951 draw_history_button (in);
953 tty_setcolor (in->color);
955 widget_move (&in->widget, 0, 0);
957 if (!in->is_password) {
958 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
959 in->field_width - has_history));
960 } else {
961 cp = in->buffer;
962 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
963 if (i >= 0) {
964 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
966 if (cp[0] != '\0') str_cnext_char (&cp);
970 if (clear_first)
971 in->first = 0;
974 void
975 winput_set_origin (WInput *in, int x, int field_width)
977 in->widget.x = x;
978 in->field_width = in->widget.cols = field_width;
979 update_input (in, 0);
982 /* {{{ history saving and loading */
984 int num_history_items_recorded = 60;
987 This loads and saves the history of an input line to and from the
988 widget. It is called with the widgets history name on creation of the
989 widget, and returns the GList list. It stores histories in the file
990 ~/.mc/history in using the profile code.
992 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
993 function) then input_new assigns the default text to be the last text
994 entered, or "" if not found.
997 GList *
998 history_get (const char *input_name)
1000 size_t i;
1001 GList *hist = NULL;
1002 char *profile;
1003 mc_config_t *cfg;
1004 char **keys;
1005 size_t keys_num = 0;
1006 char *this_entry;
1008 if (num_history_items_recorded == 0) /* this is how to disable */
1009 return NULL;
1010 if ((input_name == NULL) || (*input_name == '\0'))
1011 return NULL;
1013 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1014 cfg = mc_config_init (profile);
1016 /* get number of keys */
1017 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1018 g_strfreev (keys);
1020 for (i = 0; i < keys_num; i++) {
1021 char key[BUF_TINY];
1022 g_snprintf (key, sizeof (key), "%lu", (unsigned long)i);
1023 this_entry = mc_config_get_string (cfg, input_name, key, "");
1025 if (this_entry != NULL)
1026 hist = list_append_unique (hist, this_entry);
1029 mc_config_deinit (cfg);
1030 g_free (profile);
1032 /* return pointer to the last entry in the list */
1033 return g_list_last (hist);
1036 void
1037 history_put (const char *input_name, GList *h)
1039 int i;
1040 char *profile;
1041 mc_config_t *cfg;
1043 if (num_history_items_recorded == 0) /* this is how to disable */
1044 return;
1045 if ((input_name == NULL) || (*input_name == '\0'))
1046 return;
1047 if (h == NULL)
1048 return;
1050 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1052 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1053 if (i != -1)
1054 close (i);
1056 /* Make sure the history is only readable by the user */
1057 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1058 g_free (profile);
1059 return;
1062 /* go to end of list */
1063 h = g_list_last (h);
1065 /* go back 60 places */
1066 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1067 h = g_list_previous (h);
1069 cfg = mc_config_init (profile);
1071 if (input_name != NULL)
1072 mc_config_del_group (cfg, input_name);
1074 /* dump history into profile */
1075 for (i = 0; h != NULL; h = g_list_next (h)) {
1076 char *text = (char *) h->data;
1078 /* We shouldn't have null entries, but let's be sure */
1079 if (text != NULL) {
1080 char key[BUF_TINY];
1081 g_snprintf (key, sizeof (key), "%d", i++);
1082 mc_config_set_string (cfg, input_name, key, text);
1086 mc_config_save_file (cfg, NULL);
1087 mc_config_deinit(cfg);
1088 g_free (profile);
1091 /* }}} history saving and loading */
1094 /* {{{ history display */
1096 static const char *
1097 i18n_htitle (void)
1099 return _(" History ");
1102 typedef struct {
1103 Widget *widget;
1104 size_t count;
1105 size_t maxlen;
1106 } dlg_hist_data;
1108 static cb_ret_t
1109 dlg_hist_reposition (Dlg_head *dlg_head)
1111 dlg_hist_data *data;
1112 int x = 0, y, he, wi;
1114 /* guard checks */
1115 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1116 return MSG_NOT_HANDLED;
1118 data = (dlg_hist_data *) dlg_head->data;
1120 y = data->widget->y;
1121 he = data->count + 2;
1123 if (he <= y || y > (LINES - 6)) {
1124 he = min (he, y - 1);
1125 y -= he;
1126 } else {
1127 y++;
1128 he = min (he, LINES - y);
1131 if (data->widget->x > 2)
1132 x = data->widget->x - 2;
1134 wi = data->maxlen + 4;
1136 if ((wi + x) > COLS) {
1137 wi = min (wi, COLS);
1138 x = COLS - wi;
1141 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1143 return MSG_HANDLED;
1146 static cb_ret_t
1147 dlg_hist_callback (Dlg_head *h, Widget *sender,
1148 dlg_msg_t msg, int parm, void *data)
1150 switch (msg) {
1151 case DLG_RESIZE:
1152 return dlg_hist_reposition (h);
1154 default:
1155 return default_dlg_callback (h, sender, msg, parm, data);
1159 char *
1160 show_hist (GList **history, Widget *widget)
1162 GList *z, *hlist = NULL, *hi;
1163 size_t maxlen, i, count = 0;
1164 char *r = NULL;
1165 Dlg_head *query_dlg;
1166 WListbox *query_list;
1167 dlg_hist_data hist_data;
1169 if (*history == NULL)
1170 return NULL;
1172 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1174 for (z = *history; z != NULL; z = g_list_previous (z)) {
1175 WLEntry *entry;
1177 i = str_term_width1 ((char *) z->data);
1178 maxlen = max (maxlen, i);
1179 count++;
1181 entry = g_new0 (WLEntry, 1);
1182 /* history is being reverted here */
1183 entry->text = g_strdup ((char *) z->data);
1184 hlist = g_list_prepend (hlist, entry);
1187 hist_data.widget = widget;
1188 hist_data.count = count;
1189 hist_data.maxlen = maxlen;
1191 query_dlg =
1192 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1193 "[History-query]", i18n_htitle (), DLG_COMPACT);
1194 query_dlg->data = &hist_data;
1196 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1198 /* this call makes list stick to all sides of dialog, effectively make
1199 it be resized with dialog */
1200 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1202 /* to avoid diplicating of (calculating sizes in two places)
1203 code, call dlg_hist_callback function here, to set dialog and
1204 controls positions.
1205 The main idea - create 4x4 dialog and add 2x2 list in
1206 center of it, and let dialog function resize it to needed
1207 size. */
1208 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1210 if (query_dlg->y < widget->y) {
1211 /* draw list entries from bottom upto top */
1212 listbox_set_list (query_list, hlist);
1213 listbox_select_last (query_list);
1214 } else {
1215 /* draw list entries from top downto bottom */
1216 /* revert history direction */
1217 hlist = g_list_reverse (hlist);
1218 listbox_set_list (query_list, hlist);
1221 if (run_dlg (query_dlg) != B_CANCEL) {
1222 char *q;
1224 listbox_get_current (query_list, &q, NULL);
1225 if (q != NULL)
1226 r = g_strdup (q);
1229 /* get modified history from dialog */
1230 z = NULL;
1231 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi)) {
1232 WLEntry *entry;
1234 entry = (WLEntry *) hi->data;
1235 /* history is being reverted here again */
1236 z = g_list_prepend (z, entry->text);
1237 entry->text = NULL;
1240 destroy_dlg (query_dlg);
1242 /* restore history direction */
1243 if (query_dlg->y < widget->y)
1244 z = g_list_reverse (z);
1246 g_list_foreach (*history, (GFunc) g_free, NULL);
1247 g_list_free (*history);
1248 *history = g_list_last (z);
1250 return r;
1253 static void
1254 do_show_hist (WInput *in)
1256 char *r;
1258 r = show_hist (&in->history, &in->widget);
1259 if (r != NULL) {
1260 assign_text (in, r);
1261 g_free (r);
1265 /* }}} history display */
1267 static void
1268 input_destroy (WInput *in)
1270 if (!in){
1271 fprintf (stderr, "Internal error: null Input *\n");
1272 exit (1);
1275 new_input (in);
1277 if (in->history){
1278 if (!in->is_password) /* don't save passwords ;-) */
1279 history_put (in->history_name, in->history);
1281 in->history = g_list_first (in->history);
1282 g_list_foreach (in->history, (GFunc) g_free, NULL);
1283 g_list_free (in->history);
1286 g_free (in->buffer);
1287 free_completions (in);
1288 g_free (in->history_name);
1291 void
1292 input_disable_update (WInput *in)
1294 in->disable_update++;
1297 void
1298 input_enable_update (WInput *in)
1300 in->disable_update--;
1301 update_input (in, 0);
1305 static void
1306 push_history (WInput *in, const char *text)
1308 /* input widget where urls with passwords are entered without any
1309 vfs prefix */
1310 const char *password_input_fields[] = {
1311 N_(" Link to a remote machine "),
1312 N_(" FTP to machine "),
1313 N_(" SMB link to machine ")
1315 const size_t ELEMENTS = (sizeof (password_input_fields) /
1316 sizeof (password_input_fields[0]));
1318 char *t;
1319 size_t i;
1320 gboolean empty;
1322 if (text == NULL)
1323 return;
1325 #ifdef ENABLE_NLS
1326 for (i = 0; i < ELEMENTS; i++)
1327 password_input_fields[i] = _(password_input_fields[i]);
1328 #endif
1330 t = g_strstrip (g_strdup (text));
1331 empty = *t == '\0';
1332 g_free (t);
1333 t = g_strdup (empty ? "" : text);
1335 if (in->history_name != NULL) {
1336 const char *p = in->history_name + 3;
1338 for (i = 0; i < ELEMENTS; i++)
1339 if (strcmp (p, password_input_fields[i]) == 0)
1340 break;
1342 strip_password (t, i >= ELEMENTS);
1345 in->history = list_append_unique (in->history, t);
1346 in->need_push = 0;
1349 /* Cleans the input line and adds the current text to the history */
1350 void
1351 new_input (WInput *in)
1353 push_history (in, in->buffer);
1354 in->need_push = 1;
1355 in->buffer[0] = '\0';
1356 in->point = 0;
1357 in->charpoint = 0;
1358 in->mark = 0;
1359 free_completions (in);
1360 update_input (in, 0);
1363 static void
1364 move_buffer_backward (WInput *in, int start, int end)
1366 int i, pos, len;
1367 int str_len = str_length (in->buffer);
1368 if (start >= str_len || end > str_len + 1) return;
1370 pos = str_offset_to_pos (in->buffer, start);
1371 len = str_offset_to_pos (in->buffer, end) - pos;
1373 for (i = pos; in->buffer[i + len - 1]; i++)
1374 in->buffer[i] = in->buffer[i + len];
1377 static cb_ret_t
1378 insert_char (WInput *in, int c_code)
1380 size_t i;
1381 int res;
1383 if (c_code == -1)
1384 return MSG_NOT_HANDLED;
1386 if (in->charpoint >= MB_LEN_MAX)
1387 return MSG_HANDLED;
1389 in->charbuf[in->charpoint] = c_code;
1390 in->charpoint++;
1392 res = str_is_valid_char (in->charbuf, in->charpoint);
1393 if (res < 0) {
1394 if (res != -2)
1395 in->charpoint = 0; /* broken multibyte char, skip */
1396 return MSG_HANDLED;
1399 in->need_push = 1;
1400 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1401 /* Expand the buffer */
1402 size_t new_length = in->current_max_size +
1403 in->field_width + in->charpoint;
1404 char *narea = g_try_renew (char, in->buffer, new_length);
1405 if (narea){
1406 in->buffer = narea;
1407 in->current_max_size = new_length;
1411 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1412 /* bytes from begin */
1413 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1414 /* move chars */
1415 size_t rest_bytes = strlen (in->buffer + ins_point);
1417 for (i = rest_bytes + 1; i > 0; i--)
1418 in->buffer[ins_point + i + in->charpoint - 1] =
1419 in->buffer[ins_point + i - 1];
1421 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1422 in->point++;
1425 in->charpoint = 0;
1426 return MSG_HANDLED;
1429 static void
1430 beginning_of_line (WInput *in)
1432 in->point = 0;
1433 in->charpoint = 0;
1436 static void
1437 end_of_line (WInput *in)
1439 in->point = str_length (in->buffer);
1440 in->charpoint = 0;
1443 static void
1444 backward_char (WInput *in)
1446 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1448 if (in->point > 0) {
1449 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1451 in->charpoint = 0;
1454 static void
1455 forward_char (WInput *in)
1457 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1458 if (act[0] != '\0') {
1459 in->point+= str_cnext_noncomb_char (&act);
1461 in->charpoint = 0;
1464 static void
1465 forward_word (WInput * in)
1467 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1469 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1470 str_cnext_char (&p);
1471 in->point++;
1473 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1474 str_cnext_char (&p);
1475 in->point++;
1479 static void
1480 backward_word (WInput *in)
1482 const char *p;
1483 const char *p_tmp;
1485 for (
1486 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1487 (p != in->buffer) && (p[0] == '\0');
1488 str_cprev_char (&p), in->point--
1491 while (p != in->buffer) {
1492 p_tmp = p;
1493 str_cprev_char (&p);
1494 if (!str_isspace (p) && !str_ispunct (p)) {
1495 p = p_tmp;
1496 break;
1498 in->point--;
1500 while (p != in->buffer) {
1501 str_cprev_char (&p);
1502 if (str_isspace (p) || str_ispunct (p))
1503 break;
1505 in->point--;
1509 static void
1510 key_left (WInput *in)
1512 backward_char (in);
1515 static void
1516 key_ctrl_left (WInput *in)
1518 backward_word (in);
1521 static void
1522 key_right (WInput *in)
1524 forward_char (in);
1527 static void
1528 key_ctrl_right (WInput *in)
1530 forward_word (in);
1532 static void
1533 backward_delete (WInput *in)
1535 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1536 int start;
1538 if (in->point == 0)
1539 return;
1541 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1542 move_buffer_backward(in, start, in->point);
1543 in->charpoint = 0;
1544 in->need_push = 1;
1545 in->point = start;
1548 static void
1549 delete_char (WInput *in)
1551 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1552 int end = in->point;
1554 end+= str_cnext_noncomb_char (&act);
1556 move_buffer_backward(in, in->point, end);
1557 in->charpoint = 0;
1558 in->need_push = 1;
1561 static void
1562 copy_region (WInput *in, int x_first, int x_last)
1564 int first = min (x_first, x_last);
1565 int last = max (x_first, x_last);
1567 if (last == first) {
1568 /* Copy selected files to clipboard */
1569 panel_save_curent_file_to_clip_file ();
1570 return;
1573 g_free (kill_buffer);
1575 first = str_offset_to_pos (in->buffer, first);
1576 last = str_offset_to_pos (in->buffer, last);
1578 kill_buffer = g_strndup(in->buffer + first, last - first);
1579 save_text_to_clip_file (kill_buffer);
1582 static void
1583 delete_region (WInput *in, int x_first, int x_last)
1585 int first = min (x_first, x_last);
1586 int last = max (x_first, x_last);
1587 size_t len;
1589 in->point = first;
1590 if (in->mark > first)
1591 in->mark = first;
1592 last = str_offset_to_pos (in->buffer, last);
1593 first = str_offset_to_pos (in->buffer, first);
1594 len = strlen (&in->buffer[last]) + 1;
1595 memmove (&in->buffer[first], &in->buffer[last], len);
1596 in->charpoint = 0;
1597 in->need_push = 1;
1600 static void
1601 kill_word (WInput *in)
1603 int old_point = in->point;
1604 int new_point;
1606 forward_word (in);
1607 new_point = in->point;
1608 in->point = old_point;
1610 copy_region (in, old_point, new_point);
1611 delete_region (in, old_point, new_point);
1612 in->need_push = 1;
1613 in->charpoint = 0;
1614 in->charpoint = 0;
1617 static void
1618 back_kill_word (WInput *in)
1620 int old_point = in->point;
1621 int new_point;
1623 backward_word (in);
1624 new_point = in->point;
1625 in->point = old_point;
1627 copy_region (in, old_point, new_point);
1628 delete_region (in, old_point, new_point);
1629 in->need_push = 1;
1632 static void
1633 set_mark (WInput *in)
1635 in->mark = in->point;
1638 static void
1639 kill_save (WInput *in)
1641 copy_region (in, in->mark, in->point);
1644 static void
1645 kill_region (WInput *in)
1647 kill_save (in);
1648 delete_region (in, in->point, in->mark);
1651 static void
1652 clear_region (WInput *in)
1654 delete_region (in, in->point, in->mark);
1657 static void
1658 yank (WInput *in)
1660 char *p;
1662 if (!kill_buffer)
1663 return;
1664 in->charpoint = 0;
1665 for (p = kill_buffer; *p; p++)
1666 insert_char (in, *p);
1667 in->charpoint = 0;
1670 static void
1671 kill_line (WInput *in)
1673 int chp = str_offset_to_pos (in->buffer, in->point);
1674 g_free (kill_buffer);
1675 kill_buffer = g_strdup (&in->buffer[chp]);
1676 in->buffer[chp] = '\0';
1677 in->charpoint = 0;
1680 static void
1681 ins_from_clip (WInput *in)
1683 char *p = NULL;
1685 if (load_text_from_clip_file (&p)) {
1686 char *pp;
1688 for (pp = p; *pp != '\0'; pp++)
1689 insert_char (in, *pp);
1691 g_free (p);
1695 void
1696 assign_text (WInput *in, const char *text)
1698 free_completions (in);
1699 g_free (in->buffer);
1700 in->buffer = g_strdup (text); /* was in->buffer->text */
1701 in->current_max_size = strlen (in->buffer) + 1;
1702 in->point = str_length (in->buffer);
1703 in->mark = 0;
1704 in->need_push = 1;
1705 in->charpoint = 0;
1708 static void
1709 hist_prev (WInput *in)
1711 GList *prev;
1713 if (!in->history)
1714 return;
1716 if (in->need_push)
1717 push_history (in, in->buffer);
1719 prev = g_list_previous (in->history);
1720 if (prev != NULL) {
1721 in->history = prev;
1722 assign_text (in, (char *) prev->data);
1723 in->need_push = 0;
1727 static void
1728 hist_next (WInput *in)
1730 if (in->need_push) {
1731 push_history (in, in->buffer);
1732 assign_text (in, "");
1733 return;
1736 if (!in->history)
1737 return;
1739 if (!in->history->next) {
1740 assign_text (in, "");
1741 return;
1744 in->history = g_list_next (in->history);
1745 assign_text (in, (char *) in->history->data);
1746 in->need_push = 0;
1749 static void
1750 port_region_marked_for_delete (WInput *in)
1752 in->buffer[0] = '\0';
1753 in->point = 0;
1754 in->first = 0;
1755 in->charpoint = 0;
1758 static cb_ret_t
1759 input_execute_cmd (WInput *in, unsigned long command)
1761 cb_ret_t res = MSG_HANDLED;
1763 switch (command) {
1764 case CK_InputBol:
1765 beginning_of_line (in);
1766 break;
1767 case CK_InputEol:
1768 end_of_line (in);
1769 break;
1770 case CK_InputMoveLeft:
1771 key_left (in);
1772 break;
1773 case CK_InputWordLeft:
1774 key_ctrl_left (in);
1775 break;
1776 case CK_InputMoveRight:
1777 key_right (in);
1778 break;
1779 case CK_InputWordRight:
1780 key_ctrl_right (in);
1781 break;
1782 case CK_InputBackwardChar:
1783 backward_char (in);
1784 break;
1785 case CK_InputBackwardWord:
1786 backward_word (in);
1787 break;
1788 case CK_InputForwardChar:
1789 forward_char (in);
1790 break;
1791 case CK_InputForwardWord:
1792 forward_word (in);
1793 break;
1794 case CK_InputBackwardDelete:
1795 backward_delete (in);
1796 break;
1797 case CK_InputDeleteChar:
1798 delete_char (in);
1799 break;
1800 case CK_InputKillWord:
1801 kill_word (in);
1802 break;
1803 case CK_InputBackwardKillWord:
1804 back_kill_word (in);
1805 break;
1806 case CK_InputSetMark:
1807 set_mark (in);
1808 break;
1809 case CK_InputKillRegion:
1810 kill_region (in);
1811 break;
1812 case CK_InputClearLine:
1813 clear_region (in);
1814 break;
1815 case CK_InputKillSave:
1816 kill_save (in);
1817 break;
1818 case CK_InputYank:
1819 yank (in);
1820 break;
1821 case CK_InputPaste:
1822 ins_from_clip (in);
1823 break;
1824 case CK_InputKillLine:
1825 kill_line (in);
1826 break;
1827 case CK_InputHistoryPrev:
1828 hist_prev (in);
1829 break;
1830 case CK_InputHistoryNext:
1831 hist_next (in);
1832 break;
1833 case CK_InputHistoryShow:
1834 do_show_hist (in);
1835 break;
1836 case CK_InputComplete:
1837 complete (in);
1838 break;
1839 default:
1840 res = MSG_NOT_HANDLED;
1843 return res;
1846 /* This function is a test for a special input key used in complete.c */
1847 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1848 and 2 if it is a complete key */
1850 is_in_input_map (WInput *in, int key)
1852 size_t i;
1853 for (i = 0; input_map[i].key != 0; i++)
1854 if (key == input_map[i].key) {
1855 input_execute_cmd (in, input_map[i].command);
1856 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1858 return 0;
1861 cb_ret_t
1862 handle_char (WInput *in, int key)
1864 cb_ret_t v;
1865 int i;
1867 v = MSG_NOT_HANDLED;
1869 if (quote) {
1870 free_completions (in);
1871 v = insert_char (in, key);
1872 update_input (in, 1);
1873 quote = 0;
1874 return v;
1876 for (i = 0; input_map[i].key; i++) {
1877 if (key == input_map[i].key) {
1878 if (input_map[i].command != CK_InputComplete)
1879 free_completions (in);
1880 input_execute_cmd (in, input_map[i].command);
1881 update_input (in, 1);
1882 v = MSG_HANDLED;
1883 break;
1886 if (input_map[i].command == 0) {
1887 if (key > 255)
1888 return MSG_NOT_HANDLED;
1889 if (in->first)
1890 port_region_marked_for_delete (in);
1891 free_completions (in);
1892 v = insert_char (in, key);
1894 update_input (in, 1);
1895 return v;
1898 /* Inserts text in input line */
1899 void
1900 stuff (WInput *in, const char *text, int insert_extra_space)
1902 input_disable_update (in);
1903 while (*text != '\0')
1904 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1905 if (insert_extra_space)
1906 handle_char (in, ' ');
1907 input_enable_update (in);
1908 update_input (in, 1);
1911 void
1912 input_set_point (WInput *in, int pos)
1914 int max_pos = str_length (in->buffer);
1916 if (pos > max_pos)
1917 pos = max_pos;
1918 if (pos != in->point)
1919 free_completions (in);
1920 in->point = pos;
1921 in->charpoint = 0;
1922 update_input (in, 1);
1925 cb_ret_t
1926 input_callback (Widget *w, widget_msg_t msg, int parm)
1928 WInput *in = (WInput *) w;
1929 cb_ret_t v;
1931 switch (msg) {
1932 case WIDGET_KEY:
1933 if (parm == XCTRL ('q')) {
1934 quote = 1;
1935 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1936 quote = 0;
1937 return v;
1940 /* Keys we want others to handle */
1941 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1942 || parm == KEY_F (10) || parm == '\n')
1943 return MSG_NOT_HANDLED;
1945 /* When pasting multiline text, insert literal Enter */
1946 if ((parm & ~KEY_M_MASK) == '\n') {
1947 quote = 1;
1948 v = handle_char (in, '\n');
1949 quote = 0;
1950 return v;
1953 return handle_char (in, parm);
1955 case WIDGET_COMMAND:
1956 return input_execute_cmd (in, parm);
1958 case WIDGET_FOCUS:
1959 case WIDGET_UNFOCUS:
1960 case WIDGET_DRAW:
1961 update_input (in, 0);
1962 return MSG_HANDLED;
1964 case WIDGET_CURSOR:
1965 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1966 - in->term_first_shown);
1967 return MSG_HANDLED;
1969 case WIDGET_DESTROY:
1970 input_destroy (in);
1971 return MSG_HANDLED;
1973 default:
1974 return default_proc (msg, parm);
1978 static int
1979 input_event (Gpm_Event * event, void *data)
1981 WInput *in = data;
1983 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1984 dlg_select_widget (in);
1986 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1987 && should_show_history_button (in)) {
1988 do_show_hist (in);
1989 } else {
1990 in->point = str_length (in->buffer);
1991 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
1992 in->point = str_column_to_pos (in->buffer, event->x
1993 + in->term_first_shown - 1);
1995 update_input (in, 1);
1997 return MOU_NORMAL;
2000 WInput *
2001 input_new (int y, int x, int color, int width, const char *def_text,
2002 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2004 WInput *in = g_new (WInput, 1);
2005 size_t initial_buffer_len;
2007 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2009 /* history setup */
2010 in->history_name = NULL;
2011 in->history = NULL;
2012 if ((histname != NULL) && (*histname != '\0')) {
2013 in->history_name = g_strdup (histname);
2014 in->history = history_get (histname);
2017 if (def_text == NULL)
2018 def_text = "";
2019 else if (def_text == INPUT_LAST_TEXT) {
2020 if ((in->history != NULL) && (in->history->data != NULL))
2021 def_text = (char *) in->history->data;
2022 else
2023 def_text = "";
2026 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2027 in->widget.options |= W_IS_INPUT;
2028 in->completions = NULL;
2029 in->completion_flags = completion_flags;
2030 in->current_max_size = initial_buffer_len;
2031 in->buffer = g_new (char, initial_buffer_len);
2032 in->color = color;
2033 in->field_width = width;
2034 in->first = 1;
2035 in->term_first_shown = 0;
2036 in->disable_update = 0;
2037 in->mark = 0;
2038 in->need_push = 1;
2039 in->is_password = 0;
2041 strcpy (in->buffer, def_text);
2042 in->point = str_length (in->buffer);
2043 in->charpoint = 0;
2045 return in;
2049 /* Listbox widget */
2051 /* Should draw the scrollbar, but currently draws only
2052 * indications that there is more information
2055 static void
2056 listbox_entry_free (void *data)
2058 WLEntry *e = data;
2059 g_free (e->text);
2060 g_free (e);
2063 static void
2064 listbox_drawscroll (WListbox *l)
2066 const int max_line = l->widget.lines - 1;
2067 int line = 0;
2068 int i;
2070 /* Are we at the top? */
2071 widget_move (&l->widget, 0, l->widget.cols);
2072 if (l->top == 0)
2073 tty_print_one_vline ();
2074 else
2075 tty_print_char ('^');
2077 /* Are we at the bottom? */
2078 widget_move (&l->widget, max_line, l->widget.cols);
2079 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2080 tty_print_one_vline ();
2081 else
2082 tty_print_char ('v');
2084 /* Now draw the nice relative pointer */
2085 if (l->count != 0)
2086 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2088 for (i = 1; i < max_line; i++) {
2089 widget_move (&l->widget, i, l->widget.cols);
2090 if (i != line)
2091 tty_print_one_vline ();
2092 else
2093 tty_print_char ('*');
2097 static void
2098 listbox_draw (WListbox *l, gboolean focused)
2100 const Dlg_head *h = l->widget.parent;
2101 const int normalc = DLG_NORMALC (h);
2102 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2104 GList *le;
2105 int pos;
2106 int i;
2107 int sel_line = -1;
2109 le = g_list_nth (l->list, l->top);
2110 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2111 pos = (le == NULL) ? 0 : l->top;
2113 for (i = 0; i < l->widget.lines; i++) {
2114 const char *text;
2116 /* Display the entry */
2117 if (pos == l->pos && sel_line == -1) {
2118 sel_line = i;
2119 tty_setcolor (selc);
2120 } else
2121 tty_setcolor (normalc);
2123 widget_move (&l->widget, i, 1);
2125 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2126 text = "";
2127 else {
2128 WLEntry *e = (WLEntry *) le->data;
2129 text = e->text;
2130 le = g_list_next (le);
2131 pos++;
2134 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2137 l->cursor_y = sel_line;
2139 if (l->scrollbar && (l->count > l->widget.lines)) {
2140 tty_setcolor (normalc);
2141 listbox_drawscroll (l);
2145 static int
2146 listbox_check_hotkey (WListbox *l, int key)
2148 int i;
2149 GList *le;
2151 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2152 WLEntry *e = (WLEntry *) le->data;
2154 if (e->hotkey == key)
2155 return i;
2158 return (-1);
2161 /* Selects the last entry and scrolls the list to the bottom */
2162 void
2163 listbox_select_last (WListbox *l)
2165 l->pos = l->count - 1;
2166 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2169 /* Selects the first entry and scrolls the list to the top */
2170 void
2171 listbox_select_first (WListbox *l)
2173 l->pos = l->top = 0;
2176 void
2177 listbox_set_list (WListbox *l, GList *list)
2179 listbox_remove_list (l);
2181 if (l != NULL) {
2182 l->list = list;
2183 l->top = l->pos = 0;
2184 l->count = g_list_length (list);
2188 void
2189 listbox_remove_list (WListbox *l)
2191 if ((l != NULL) && (l->count != 0)) {
2192 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2193 g_list_free (l->list);
2194 l->list = NULL;
2195 l->count = l->pos = l->top = 0;
2199 void
2200 listbox_remove_current (WListbox *l)
2202 if ((l != NULL) && (l->count != 0)) {
2203 GList *current;
2205 current = g_list_nth (l->list, l->pos);
2206 l->list = g_list_remove_link (l->list, current);
2207 listbox_entry_free ((WLEntry *) current->data);
2208 g_list_free_1 (current);
2209 l->count--;
2211 if (l->count == 0)
2212 l->top = l->pos = 0;
2213 else if (l->pos >= l->count)
2214 l->pos = l->count - 1;
2218 void
2219 listbox_select_entry (WListbox *l, int dest)
2221 GList *le;
2222 int pos;
2223 gboolean top_seen = FALSE;
2225 if (dest < 0)
2226 return;
2228 /* Special case */
2229 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le)) {
2230 if (pos == l->top)
2231 top_seen = TRUE;
2233 if (pos == dest) {
2234 l->pos = dest;
2235 if (!top_seen)
2236 l->top = l->pos;
2237 else
2238 if (l->pos - l->top >= l->widget.lines)
2239 l->top = l->pos - l->widget.lines + 1;
2240 return;
2244 /* If we are unable to find it, set decent values */
2245 l->pos = l->top = 0;
2248 /* Selects from base the pos element */
2249 static int
2250 listbox_select_pos (WListbox *l, int base, int pos)
2252 int last = l->count - 1;
2254 base += pos;
2255 if (base >= last)
2256 base = last;
2258 return base;
2261 static void
2262 listbox_fwd (WListbox *l)
2264 if (l->pos + 1 >= l->count)
2265 listbox_select_first (l);
2266 else
2267 listbox_select_entry (l, l->pos + 1);
2270 static void
2271 listbox_back (WListbox *l)
2273 if (l->pos <= 0)
2274 listbox_select_last (l);
2275 else
2276 listbox_select_entry (l, l->pos - 1);
2279 /* Return MSG_HANDLED if we want a redraw */
2280 static cb_ret_t
2281 listbox_key (WListbox *l, int key)
2283 int i;
2285 cb_ret_t j = MSG_NOT_HANDLED;
2287 if (l->list == NULL)
2288 return MSG_NOT_HANDLED;
2290 /* focus on listbox item N by '0'..'9' keys */
2291 if (key >= '0' && key <= '9') {
2292 int oldpos = l->pos;
2293 listbox_select_entry (l, key - '0');
2295 /* need scroll to item? */
2296 if (abs (oldpos - l->pos) > l->widget.lines)
2297 l->top = l->pos;
2299 return MSG_HANDLED;
2302 switch (key){
2303 case KEY_HOME:
2304 case KEY_A1:
2305 case ALT ('<'):
2306 listbox_select_first (l);
2307 return MSG_HANDLED;
2309 case KEY_END:
2310 case KEY_C1:
2311 case ALT ('>'):
2312 listbox_select_last (l);
2313 return MSG_HANDLED;
2315 case XCTRL('p'):
2316 case KEY_UP:
2317 listbox_back (l);
2318 return MSG_HANDLED;
2320 case XCTRL('n'):
2321 case KEY_DOWN:
2322 listbox_fwd (l);
2323 return MSG_HANDLED;
2325 case KEY_NPAGE:
2326 case XCTRL('v'):
2327 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++) {
2328 listbox_fwd (l);
2329 j = MSG_HANDLED;
2331 break;
2333 case KEY_PPAGE:
2334 case ALT('v'):
2335 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++) {
2336 listbox_back (l);
2337 j = MSG_HANDLED;
2339 break;
2341 case KEY_DC:
2342 case 'd':
2343 if (l->deletable) {
2344 gboolean is_last = (l->pos + 1 >= l->count);
2345 gboolean is_more = (l->top + l->widget.lines >= l->count);
2347 listbox_remove_current (l);
2348 if ((l->top > 0) && (is_last || is_more))
2349 l->top--;
2351 return MSG_HANDLED;
2353 case (KEY_M_SHIFT | KEY_DC):
2354 case 'D':
2355 if (l->deletable && confirm_history_cleanup
2356 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2357 && (query_dialog (Q_("DialogTitle|History cleanup"),
2358 _("Do you want clean this history?"),
2359 D_ERROR, 2, _("&Yes"), _("&No")) == 0)) {
2360 listbox_remove_list (l);
2361 j = MSG_HANDLED;
2363 break;
2365 default:
2366 break;
2369 return j;
2372 static inline void
2373 listbox_destroy (WListbox *l)
2375 /* don't delete list in modifable listbox */
2376 if (!l->deletable)
2377 listbox_remove_list (l);
2380 static cb_ret_t
2381 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2383 WListbox *l = (WListbox *) w;
2384 Dlg_head *h = l->widget.parent;
2385 cb_ret_t ret_code;
2387 switch (msg) {
2388 case WIDGET_INIT:
2389 return MSG_HANDLED;
2391 case WIDGET_HOTKEY:
2393 int pos, action;
2395 pos = listbox_check_hotkey (l, parm);
2396 if (pos < 0)
2397 return MSG_NOT_HANDLED;
2399 listbox_select_entry (l, pos);
2400 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2402 if (l->cback != NULL)
2403 action = l->cback (l);
2404 else
2405 action = LISTBOX_DONE;
2407 if (action == LISTBOX_DONE) {
2408 h->ret_value = B_ENTER;
2409 dlg_stop (h);
2412 return MSG_HANDLED;
2415 case WIDGET_KEY:
2416 ret_code = listbox_key (l, parm);
2417 if (ret_code != MSG_NOT_HANDLED) {
2418 listbox_draw (l, TRUE);
2419 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2421 return ret_code;
2423 case WIDGET_CURSOR:
2424 widget_move (&l->widget, l->cursor_y, 0);
2425 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2426 return MSG_HANDLED;
2428 case WIDGET_FOCUS:
2429 case WIDGET_UNFOCUS:
2430 case WIDGET_DRAW:
2431 listbox_draw (l, msg != WIDGET_UNFOCUS);
2432 return MSG_HANDLED;
2434 case WIDGET_DESTROY:
2435 listbox_destroy (l);
2436 return MSG_HANDLED;
2438 case WIDGET_RESIZED:
2439 return MSG_HANDLED;
2441 default:
2442 return default_proc (msg, parm);
2446 static int
2447 listbox_event (Gpm_Event *event, void *data)
2449 WListbox *l = data;
2450 int i;
2452 Dlg_head *h = l->widget.parent;
2454 /* Single click */
2455 if (event->type & GPM_DOWN)
2456 dlg_select_widget (l);
2458 if (l->list == NULL)
2459 return MOU_NORMAL;
2461 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2462 int ret = MOU_REPEAT;
2464 if (event->x < 0 || event->x > l->widget.cols)
2465 return ret;
2467 if (event->y < 1)
2468 for (i = -event->y; i >= 0; i--)
2469 listbox_back (l);
2470 else if (event->y > l->widget.lines)
2471 for (i = event->y - l->widget.lines; i > 0; i--)
2472 listbox_fwd (l);
2473 else if (event->buttons & GPM_B_UP) {
2474 listbox_back (l);
2475 ret = MOU_NORMAL;
2476 } else if (event->buttons & GPM_B_DOWN) {
2477 listbox_fwd (l);
2478 ret = MOU_NORMAL;
2479 } else
2480 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2482 /* We need to refresh ourselves since the dialog manager doesn't */
2483 /* know about this event */
2484 listbox_draw (l, TRUE);
2485 return ret;
2488 /* Double click */
2489 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2490 int action;
2492 if (event->x < 0 || event->x >= l->widget.cols
2493 || event->y < 1 || event->y > l->widget.lines)
2494 return MOU_NORMAL;
2496 dlg_select_widget (l);
2497 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2499 if (l->cback != NULL)
2500 action = l->cback (l);
2501 else
2502 action = LISTBOX_DONE;
2504 if (action == LISTBOX_DONE) {
2505 h->ret_value = B_ENTER;
2506 dlg_stop (h);
2507 return MOU_NORMAL;
2510 return MOU_NORMAL;
2513 WListbox *
2514 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2516 WListbox *l = g_new (WListbox, 1);
2518 if (height <= 0)
2519 height = 1;
2521 init_widget (&l->widget, y, x, height, width,
2522 listbox_callback, listbox_event);
2524 l->list = NULL;
2525 l->top = l->pos = 0;
2526 l->count = 0;
2527 l->deletable = deletable;
2528 l->cback = callback;
2529 l->allow_duplicates = TRUE;
2530 l->scrollbar = !tty_is_slow ();
2531 widget_want_hotkey (l->widget, 1);
2533 return l;
2536 static int
2537 listbox_entry_cmp (const void *a, const void *b)
2539 const WLEntry *ea = (const WLEntry *) a;
2540 const WLEntry *eb = (const WLEntry *) b;
2542 return strcmp (ea->text, eb->text);
2545 /* Listbox item adding function */
2546 static inline void
2547 listbox_append_item (WListbox *l, WLEntry *e, listbox_append_t pos)
2549 switch (pos) {
2550 case LISTBOX_APPEND_AT_END:
2551 l->list = g_list_append (l->list, e);
2552 break;
2554 case LISTBOX_APPEND_BEFORE:
2555 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2556 if (l->pos > 0)
2557 l->pos--;
2558 break;
2560 case LISTBOX_APPEND_AFTER:
2561 l->list = g_list_insert (l->list, e, l->pos + 1);
2562 break;
2564 case LISTBOX_APPEND_SORTED:
2565 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2566 break;
2568 default:
2569 return;
2572 l->count++;
2575 char *
2576 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey,
2577 const char *text, void *data)
2579 WLEntry *entry;
2581 if (l == NULL)
2582 return NULL;
2584 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2585 return NULL;
2587 entry = g_new (WLEntry, 1);
2588 entry->text = g_strdup (text);
2589 entry->data = data;
2590 entry->hotkey = hotkey;
2592 listbox_append_item (l, entry, pos);
2594 return entry->text;
2598 listbox_search_text (WListbox *l, const char *text)
2600 if (l != NULL) {
2601 int i;
2602 GList *le;
2604 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2605 WLEntry *e = (WLEntry *) le->data;
2607 if (strcmp (e->text, text) == 0)
2608 return i;
2612 return (-1);
2615 /* Returns the current string text as well as the associated extra data */
2616 void
2617 listbox_get_current (WListbox *l, char **string, void **extra)
2619 WLEntry *e = NULL;
2620 gboolean ok;
2622 if (l != NULL)
2623 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2625 ok = (e != NULL);
2627 if (string != NULL)
2628 *string = ok ? e->text : NULL;
2630 if (extra != NULL)
2631 *extra = ok ? e->data : NULL;
2635 /* ButtonBar widget */
2637 /* returns TRUE if a function has been called, FALSE otherwise. */
2638 static gboolean
2639 buttonbar_call (WButtonBar *bb, int i)
2641 cb_ret_t ret = MSG_NOT_HANDLED;
2643 if (bb != NULL)
2644 ret = bb->widget.parent->callback (bb->widget.parent,
2645 (Widget *) bb, DLG_ACTION,
2646 bb->labels[i].command,
2647 bb->labels[i].receiver);
2648 return ret;
2651 /* calculate width of one button, width is never lesser than 7 */
2652 static int
2653 buttonbat_get_button_width (void)
2655 int result = COLS / BUTTONBAR_LABELS_NUM;
2656 return (result >= 7) ? result : 7;
2659 static cb_ret_t
2660 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2662 WButtonBar *bb = (WButtonBar *) w;
2663 int i;
2664 const char *text;
2666 switch (msg) {
2667 case WIDGET_FOCUS:
2668 return MSG_NOT_HANDLED;
2670 case WIDGET_HOTKEY:
2671 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2672 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2673 return MSG_HANDLED;
2674 return MSG_NOT_HANDLED;
2676 case WIDGET_DRAW:
2677 if (bb->visible) {
2678 int offset = 0;
2679 int count_free_positions;
2681 widget_move (&bb->widget, 0, 0);
2682 tty_setcolor (DEFAULT_COLOR);
2683 bb->btn_width = buttonbat_get_button_width ();
2684 tty_printf ("%-*s", bb->widget.cols, "");
2685 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2687 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2688 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2689 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2690 tty_printf ("%2d", i + 1);
2691 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2692 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2693 tty_print_string (str_fit_to_term (
2694 text,
2695 bb->btn_width - 2 + (int)(offset < count_free_positions),
2696 J_LEFT_FIT));
2698 if (count_free_positions != 0 && offset < count_free_positions)
2699 offset++;
2702 return MSG_HANDLED;
2704 case WIDGET_DESTROY:
2705 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2706 g_free (bb->labels[i].text);
2707 return MSG_HANDLED;
2709 default:
2710 return default_proc (msg, parm);
2714 static int
2715 buttonbar_event (Gpm_Event *event, void *data)
2717 WButtonBar *bb = data;
2718 int button;
2720 if (!(event->type & GPM_UP))
2721 return MOU_NORMAL;
2722 if (event->y == 2)
2723 return MOU_NORMAL;
2724 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2725 if (button < BUTTONBAR_LABELS_NUM)
2726 buttonbar_call (bb, button);
2727 return MOU_NORMAL;
2730 WButtonBar *
2731 buttonbar_new (gboolean visible)
2733 WButtonBar *bb;
2735 bb = g_new0 (WButtonBar, 1);
2737 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2738 buttonbar_callback, buttonbar_event);
2739 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2740 bb->visible = visible;
2741 widget_want_hotkey (bb->widget, 1);
2742 widget_want_cursor (bb->widget, 0);
2743 bb->btn_width = buttonbat_get_button_width ();
2745 return bb;
2748 static void
2749 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2751 g_free (bb->labels[lc_index - 1].text);
2752 bb->labels[lc_index - 1].text = g_strdup (text);
2755 /* Find ButtonBar widget in the dialog */
2756 WButtonBar *
2757 find_buttonbar (const Dlg_head *h)
2759 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2762 void
2763 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2764 const struct global_keymap_t *keymap, const Widget *receiver)
2766 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2767 unsigned long command = CK_Ignore_Key;
2769 if (keymap != NULL)
2770 command = lookup_keymap_command (keymap, KEY_F (idx));
2772 if ((text == NULL) || (text[0] == '\0'))
2773 set_label_text (bb, idx, "");
2774 else
2775 set_label_text (bb, idx, text);
2777 bb->labels[idx - 1].command = command;
2778 bb->labels[idx - 1].receiver = (Widget *) receiver;
2782 void
2783 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2785 bb->visible = visible;
2788 void
2789 buttonbar_redraw (WButtonBar *bb)
2791 if (bb != NULL)
2792 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2795 static cb_ret_t
2796 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2798 WGroupbox *g = (WGroupbox *) w;
2800 switch (msg) {
2801 case WIDGET_INIT:
2802 return MSG_HANDLED;
2804 case WIDGET_FOCUS:
2805 return MSG_NOT_HANDLED;
2807 case WIDGET_DRAW:
2808 tty_setcolor (COLOR_NORMAL);
2809 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2810 g->widget.x - g->widget.parent->x, g->widget.lines,
2811 g->widget.cols);
2813 tty_setcolor (COLOR_HOT_NORMAL);
2814 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2815 g->widget.x - g->widget.parent->x + 1);
2816 tty_print_string (g->title);
2817 return MSG_HANDLED;
2819 case WIDGET_DESTROY:
2820 g_free (g->title);
2821 return MSG_HANDLED;
2823 default:
2824 return default_proc (msg, parm);
2828 WGroupbox *
2829 groupbox_new (int y, int x, int height, int width, const char *title)
2831 WGroupbox *g = g_new (WGroupbox, 1);
2833 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2835 g->widget.options &= ~W_WANT_CURSOR;
2836 widget_want_hotkey (g->widget, 0);
2838 /* Strip existing spaces, add one space before and after the title */
2839 if (title) {
2840 char *t;
2841 t = g_strstrip (g_strdup (title));
2842 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2843 g_free (t);
2846 return g;