Ticket #1809 (invalid length of nonprintable chars)
[kaloumi3.git] / src / widget.c
blob19c7eb2fbb615976de5205f9fa9a8f5acb280c57
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 "global.h"
45 #include "../src/tty/tty.h"
46 #include "../src/skin/skin.h"
47 #include "../src/tty/mouse.h"
48 #include "../src/tty/key.h" /* XCTRL and ALT macros */
50 #include "../src/mcconfig/mcconfig.h" /* for history loading and saving */
52 #include "dialog.h"
53 #include "widget.h"
54 #include "wtools.h"
55 #include "strutil.h"
57 #include "cmddef.h" /* CK_ cmd name const */
58 #include "keybind.h" /* global_keymap_t */
59 #include "fileloc.h"
60 #include "panel.h" /* current_panel */
62 const global_keymap_t *input_map;
64 static void
65 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
67 Dlg_head *h = w->parent;
69 tty_setcolor (hotkey
70 ? (focused
71 ? DLG_HOT_FOCUSC (h)
72 : DLG_HOT_NORMALC (h))
73 : (focused
74 ? DLG_FOCUSC (h)
75 : DLG_NORMALC (h)));
78 struct hotkey_t
79 parse_hotkey (const char *text)
81 struct hotkey_t result;
82 const char *cp, *p;
84 /* search for '&', that is not on the of text */
85 cp = strchr (text, '&');
86 if (cp != NULL && cp[1] != '\0') {
87 result.start = g_strndup (text, cp - text);
89 /* skip '&' */
90 cp++;
91 p = str_cget_next_char (cp);
92 result.hotkey = g_strndup (cp, p - cp);
94 cp = p;
95 result.end = g_strdup (cp);
96 } else {
97 result.start = g_strdup (text);
98 result.hotkey = NULL;
99 result.end = NULL;
102 return result;
104 void
105 release_hotkey (const struct hotkey_t hotkey)
107 g_free (hotkey.start);
108 g_free (hotkey.hotkey);
109 g_free (hotkey.end);
113 hotkey_width (const struct hotkey_t hotkey)
115 int result;
117 result = str_term_width1 (hotkey.start);
118 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
119 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
120 return result;
123 static void
124 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
126 widget_selectcolor (w, focused, FALSE);
127 tty_print_string (hotkey.start);
129 if (hotkey.hotkey != NULL) {
130 widget_selectcolor (w, focused, TRUE);
131 tty_print_string (hotkey.hotkey);
132 widget_selectcolor (w, focused, FALSE);
135 if (hotkey.end != NULL)
136 tty_print_string (hotkey.end);
140 /* Default callback for widgets */
141 cb_ret_t
142 default_proc (widget_msg_t msg, int parm)
144 (void) parm;
146 switch (msg) {
147 case WIDGET_INIT:
148 case WIDGET_FOCUS:
149 case WIDGET_UNFOCUS:
150 case WIDGET_DRAW:
151 case WIDGET_DESTROY:
152 case WIDGET_CURSOR:
153 case WIDGET_IDLE:
154 return MSG_HANDLED;
156 default:
157 return MSG_NOT_HANDLED;
161 static int button_event (Gpm_Event *event, void *);
163 int quote = 0;
165 static cb_ret_t
166 button_callback (Widget *w, widget_msg_t msg, int parm)
168 WButton *b = (WButton *) w;
169 int stop = 0;
170 int off = 0;
171 Dlg_head *h = b->widget.parent;
173 switch (msg) {
174 case WIDGET_HOTKEY:
176 * Don't let the default button steal Enter from the current
177 * button. This is a workaround for the flawed event model
178 * when hotkeys are sent to all widgets before the key is
179 * handled by the current widget.
181 if (parm == '\n' && h->current == &b->widget) {
182 button_callback (w, WIDGET_KEY, ' ');
183 return MSG_HANDLED;
186 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
187 button_callback (w, WIDGET_KEY, ' ');
188 return MSG_HANDLED;
191 if (b->text.hotkey != NULL) {
192 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
193 g_ascii_tolower ((gchar)parm)) {
194 button_callback (w, WIDGET_KEY, ' ');
195 return MSG_HANDLED;
198 return MSG_NOT_HANDLED;
200 case WIDGET_KEY:
201 if (parm != ' ' && parm != '\n')
202 return MSG_NOT_HANDLED;
204 if (b->callback)
205 stop = (*b->callback) (b->action);
206 if (!b->callback || stop) {
207 h->ret_value = b->action;
208 dlg_stop (h);
210 return MSG_HANDLED;
212 case WIDGET_CURSOR:
213 switch (b->flags) {
214 case DEFPUSH_BUTTON:
215 off = 3;
216 break;
217 case NORMAL_BUTTON:
218 off = 2;
219 break;
220 case NARROW_BUTTON:
221 off = 1;
222 break;
223 case HIDDEN_BUTTON:
224 default:
225 off = 0;
226 break;
228 widget_move (&b->widget, 0, b->hotpos + off);
229 return MSG_HANDLED;
231 case WIDGET_UNFOCUS:
232 case WIDGET_FOCUS:
233 case WIDGET_DRAW:
234 if (msg == WIDGET_UNFOCUS)
235 b->selected = 0;
236 else if (msg == WIDGET_FOCUS)
237 b->selected = 1;
239 widget_selectcolor (w, b->selected, FALSE);
240 widget_move (w, 0, 0);
242 switch (b->flags) {
243 case DEFPUSH_BUTTON:
244 tty_print_string ("[< ");
245 break;
246 case NORMAL_BUTTON:
247 tty_print_string ("[ ");
248 break;
249 case NARROW_BUTTON:
250 tty_print_string ("[");
251 break;
252 case HIDDEN_BUTTON:
253 default:
254 return MSG_HANDLED;
257 draw_hotkey (w, b->text, b->selected);
259 switch (b->flags) {
260 case DEFPUSH_BUTTON:
261 tty_print_string (" >]");
262 break;
263 case NORMAL_BUTTON:
264 tty_print_string (" ]");
265 break;
266 case NARROW_BUTTON:
267 tty_print_string ("]");
268 break;
270 return MSG_HANDLED;
272 case WIDGET_DESTROY:
273 release_hotkey (b->text);
274 return MSG_HANDLED;
276 default:
277 return default_proc (msg, parm);
281 static int
282 button_event (Gpm_Event *event, void *data)
284 WButton *b = data;
286 if (event->type & (GPM_DOWN|GPM_UP)){
287 Dlg_head *h = b->widget.parent;
288 dlg_select_widget (b);
289 if (event->type & GPM_UP){
290 button_callback ((Widget *) data, WIDGET_KEY, ' ');
291 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
292 return MOU_NORMAL;
295 return MOU_NORMAL;
299 button_get_len (const WButton *b)
301 int ret = hotkey_width (b->text);
302 switch (b->flags) {
303 case DEFPUSH_BUTTON:
304 ret += 6;
305 break;
306 case NORMAL_BUTTON:
307 ret += 4;
308 break;
309 case NARROW_BUTTON:
310 ret += 2;
311 break;
312 case HIDDEN_BUTTON:
313 default:
314 return 0;
316 return ret;
319 WButton *
320 button_new (int y, int x, int action, int flags, const char *text,
321 bcback callback)
323 WButton *b = g_new (WButton, 1);
325 b->action = action;
326 b->flags = flags;
327 b->text = parse_hotkey (text);
329 init_widget (&b->widget, y, x, 1, button_get_len (b),
330 button_callback, button_event);
332 b->selected = 0;
333 b->callback = callback;
334 widget_want_hotkey (b->widget, 1);
335 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
337 return b;
340 const char *
341 button_get_text (const WButton *b)
343 if (b->text.hotkey != NULL)
344 return g_strconcat (b->text.start, "&", b->text.hotkey,
345 b->text.end, NULL);
346 else
347 return g_strdup (b->text.start);
350 void
351 button_set_text (WButton *b, const char *text)
353 release_hotkey (b->text);
354 b->text = parse_hotkey (text);
355 b->widget.cols = button_get_len (b);
356 dlg_redraw (b->widget.parent);
360 /* Radio button widget */
361 static int radio_event (Gpm_Event *event, void *);
363 static cb_ret_t
364 radio_callback (Widget *w, widget_msg_t msg, int parm)
366 WRadio *r = (WRadio *) w;
367 int i;
368 Dlg_head *h = r->widget.parent;
370 switch (msg) {
371 case WIDGET_HOTKEY:
373 int lp = g_ascii_tolower ((gchar)parm);
375 for (i = 0; i < r->count; i++) {
376 if (r->texts[i].hotkey != NULL) {
377 int c = g_ascii_tolower ((gchar)r->texts[i].hotkey[0]);
379 if (c != lp)
380 continue;
381 r->pos = i;
383 /* Take action */
384 radio_callback (w, WIDGET_KEY, ' ');
385 return MSG_HANDLED;
389 return MSG_NOT_HANDLED;
391 case WIDGET_KEY:
392 switch (parm) {
393 case ' ':
394 r->sel = r->pos;
395 h->callback (h, w, DLG_ACTION, 0, NULL);
396 radio_callback (w, WIDGET_FOCUS, ' ');
397 return MSG_HANDLED;
399 case KEY_UP:
400 case KEY_LEFT:
401 if (r->pos > 0) {
402 r->pos--;
403 return MSG_HANDLED;
405 return MSG_NOT_HANDLED;
407 case KEY_DOWN:
408 case KEY_RIGHT:
409 if (r->count - 1 > r->pos) {
410 r->pos++;
411 return MSG_HANDLED;
414 return MSG_NOT_HANDLED;
416 case WIDGET_CURSOR:
417 h->callback (h, w, DLG_ACTION, 0, NULL);
418 radio_callback (w, WIDGET_FOCUS, ' ');
419 widget_move (&r->widget, r->pos, 1);
420 return MSG_HANDLED;
422 case WIDGET_UNFOCUS:
423 case WIDGET_FOCUS:
424 case WIDGET_DRAW:
425 for (i = 0; i < r->count; i++) {
426 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
427 widget_selectcolor (w, focused, FALSE);
428 widget_move (&r->widget, i, 0);
429 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
430 draw_hotkey (w, r->texts[i], focused);
432 return MSG_HANDLED;
434 case WIDGET_DESTROY:
435 for (i = 0; i < r->count; i++) {
436 release_hotkey (r->texts[i]);
438 g_free (r->texts);
439 return MSG_HANDLED;
441 default:
442 return default_proc (msg, parm);
446 static int
447 radio_event (Gpm_Event *event, void *data)
449 WRadio *r = data;
450 Widget *w = data;
452 if (event->type & (GPM_DOWN|GPM_UP)){
453 Dlg_head *h = r->widget.parent;
455 r->pos = event->y - 1;
456 dlg_select_widget (r);
457 if (event->type & GPM_UP){
458 radio_callback (w, WIDGET_KEY, ' ');
459 radio_callback (w, WIDGET_FOCUS, 0);
460 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
461 return MOU_NORMAL;
464 return MOU_NORMAL;
467 WRadio *
468 radio_new (int y, int x, int count, const char **texts)
470 WRadio *result = g_new (WRadio, 1);
471 int i, max, m;
473 /* Compute the longest string */
474 result->texts = g_new (struct hotkey_t, count);
476 max = 0;
477 for (i = 0; i < count; i++){
478 result->texts[i] = parse_hotkey (texts[i]);
479 m = hotkey_width (result->texts[i]);
480 if (m > max)
481 max = m;
484 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
485 result->state = 1;
486 result->pos = 0;
487 result->sel = 0;
488 result->count = count;
489 widget_want_hotkey (result->widget, 1);
491 return result;
495 /* Checkbutton widget */
497 static int check_event (Gpm_Event *event, void *);
499 static cb_ret_t
500 check_callback (Widget *w, widget_msg_t msg, int parm)
502 WCheck *c = (WCheck *) w;
503 Dlg_head *h = c->widget.parent;
505 switch (msg) {
506 case WIDGET_HOTKEY:
507 if (c->text.hotkey != NULL) {
508 if (g_ascii_tolower ((gchar)c->text.hotkey[0]) ==
509 g_ascii_tolower ((gchar)parm)) {
511 check_callback (w, WIDGET_KEY, ' '); /* make action */
512 return MSG_HANDLED;
515 return MSG_NOT_HANDLED;
517 case WIDGET_KEY:
518 if (parm != ' ')
519 return MSG_NOT_HANDLED;
520 c->state ^= C_BOOL;
521 c->state ^= C_CHANGE;
522 h->callback (h, w, DLG_ACTION, 0, NULL);
523 check_callback (w, WIDGET_FOCUS, ' ');
524 return MSG_HANDLED;
526 case WIDGET_CURSOR:
527 widget_move (&c->widget, 0, 1);
528 return MSG_HANDLED;
530 case WIDGET_FOCUS:
531 case WIDGET_UNFOCUS:
532 case WIDGET_DRAW:
533 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
534 widget_move (&c->widget, 0, 0);
535 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
536 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
537 return MSG_HANDLED;
539 case WIDGET_DESTROY:
540 release_hotkey (c->text);
541 return MSG_HANDLED;
543 default:
544 return default_proc (msg, parm);
548 static int
549 check_event (Gpm_Event *event, void *data)
551 WCheck *c = data;
552 Widget *w = data;
554 if (event->type & (GPM_DOWN|GPM_UP)){
555 Dlg_head *h = c->widget.parent;
557 dlg_select_widget (c);
558 if (event->type & GPM_UP){
559 check_callback (w, WIDGET_KEY, ' ');
560 check_callback (w, WIDGET_FOCUS, 0);
561 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
562 return MOU_NORMAL;
565 return MOU_NORMAL;
568 WCheck *
569 check_new (int y, int x, int state, const char *text)
571 WCheck *c = g_new (WCheck, 1);
573 c->text = parse_hotkey (text);
575 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
576 check_callback, check_event);
577 c->state = state ? C_BOOL : 0;
578 widget_want_hotkey (c->widget, 1);
580 return c;
583 static gboolean
584 save_text_to_clip_file (const char *text)
586 int file;
587 char *fname = NULL;
589 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
590 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
591 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
592 g_free (fname);
594 if (file == -1)
595 return FALSE;
597 mc_write (file, (char *) text, strlen (text));
598 mc_close (file);
599 return TRUE;
602 static gboolean
603 load_text_from_clip_file (char **text)
605 char buf[BUF_LARGE];
606 FILE *f;
607 char *fname = NULL;
608 gboolean first = TRUE;
610 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
611 f = fopen (fname, "r");
612 g_free (fname);
614 if (f == NULL)
615 return FALSE;
617 *text = NULL;
619 while (fgets (buf, sizeof (buf), f)) {
620 size_t len;
622 len = strlen (buf);
623 if ( len > 0 ) {
624 if (buf[len - 1] == '\n')
625 buf[len - 1] = '\0';
627 if (first) {
628 first = FALSE;
629 *text = g_strdup (buf);
630 } else {
631 /* remove \n on EOL */
632 char *tmp;
634 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
635 g_free (*text);
636 *text = tmp;
641 fclose (f);
643 return (*text != NULL);
646 static gboolean
647 panel_save_curent_file_to_clip_file (void)
649 gboolean res;
651 if (current_panel->marked == 0)
652 res = save_text_to_clip_file (selection (current_panel)->fname);
653 else {
654 int i;
655 gboolean first = TRUE;
656 char *flist = NULL;
658 for (i = 0; i < current_panel->count; i++)
659 if (current_panel->dir.list[i].f.marked != 0) { /* Skip the unmarked ones */
660 if (first) {
661 flist = g_strdup (current_panel->dir.list[i].fname);
662 first = FALSE;
663 } else {
664 /* Add empty lines after the file */
665 char *tmp;
667 tmp = g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
668 g_free (flist);
669 flist = tmp;
673 if (flist != NULL) {
674 res = save_text_to_clip_file (flist);
675 g_free (flist);
678 return res;
682 /* Label widget */
684 static cb_ret_t
685 label_callback (Widget *w, widget_msg_t msg, int parm)
687 WLabel *l = (WLabel *) w;
688 Dlg_head *h = l->widget.parent;
690 switch (msg) {
691 case WIDGET_INIT:
692 return MSG_HANDLED;
694 /* We don't want to get the focus */
695 case WIDGET_FOCUS:
696 return MSG_NOT_HANDLED;
698 case WIDGET_DRAW:
700 char *p = l->text, *q, c = 0;
701 int y = 0;
703 if (!l->text)
704 return MSG_HANDLED;
706 if (l->transparent)
707 tty_setcolor (DEFAULT_COLOR);
708 else
709 tty_setcolor (DLG_NORMALC (h));
711 for (;;) {
712 q = strchr (p, '\n');
713 if (q != NULL) {
714 c = q[0];
715 q[0] = '\0';
718 widget_move (&l->widget, y, 0);
719 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
721 if (q == NULL)
722 break;
723 q[0] = c;
724 p = q + 1;
725 y++;
727 return MSG_HANDLED;
730 case WIDGET_DESTROY:
731 g_free (l->text);
732 return MSG_HANDLED;
734 default:
735 return default_proc (msg, parm);
739 void
740 label_set_text (WLabel *label, const char *text)
742 int newcols = label->widget.cols;
743 int newlines;
745 if (label->text && text && !strcmp (label->text, text))
746 return; /* Flickering is not nice */
748 g_free (label->text);
750 if (text != NULL) {
751 label->text = g_strdup (text);
752 if (label->auto_adjust_cols) {
753 str_msg_term_size (text, &newlines, &newcols);
754 if (newcols > label->widget.cols)
755 label->widget.cols = newcols;
756 if (newlines > label->widget.lines)
757 label->widget.lines = newlines;
759 } else label->text = NULL;
761 if (label->widget.parent)
762 label_callback ((Widget *) label, WIDGET_DRAW, 0);
764 if (newcols < label->widget.cols)
765 label->widget.cols = newcols;
768 WLabel *
769 label_new (int y, int x, const char *text)
771 WLabel *l;
772 int cols = 1;
773 int lines = 1;
775 if (text != NULL)
776 str_msg_term_size (text, &lines, &cols);
778 l = g_new (WLabel, 1);
779 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
780 l->text = (text != NULL) ? g_strdup (text) : NULL;
781 l->auto_adjust_cols = 1;
782 l->transparent = 0;
783 widget_want_cursor (l->widget, 0);
784 return l;
788 /* Gauge widget (progress indicator) */
789 /* Currently width is hardcoded here for text mode */
790 #define gauge_len 47
792 static cb_ret_t
793 gauge_callback (Widget *w, widget_msg_t msg, int parm)
795 WGauge *g = (WGauge *) w;
796 Dlg_head *h = g->widget.parent;
798 if (msg == WIDGET_INIT)
799 return MSG_HANDLED;
801 /* We don't want to get the focus */
802 if (msg == WIDGET_FOCUS)
803 return MSG_NOT_HANDLED;
805 if (msg == WIDGET_DRAW){
806 widget_move (&g->widget, 0, 0);
807 tty_setcolor (DLG_NORMALC (h));
808 if (!g->shown)
809 tty_printf ("%*s", gauge_len, "");
810 else {
811 int percentage, columns;
812 long total = g->max, done = g->current;
814 if (total <= 0 || done < 0) {
815 done = 0;
816 total = 100;
818 if (done > total)
819 done = total;
820 while (total > 65535) {
821 total /= 256;
822 done /= 256;
824 percentage = (200 * done / total + 1) / 2;
825 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
826 tty_print_char ('[');
827 tty_setcolor (GAUGE_COLOR);
828 tty_printf ("%*s", (int) columns, "");
829 tty_setcolor (DLG_NORMALC (h));
830 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
832 return MSG_HANDLED;
835 return default_proc (msg, parm);
838 void
839 gauge_set_value (WGauge *g, int max, int current)
841 if (g->current == current && g->max == max)
842 return; /* Do not flicker */
843 if (max == 0)
844 max = 1; /* I do not like division by zero :) */
846 g->current = current;
847 g->max = max;
848 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
851 void
852 gauge_show (WGauge *g, int shown)
854 if (g->shown == shown)
855 return;
856 g->shown = shown;
857 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
860 WGauge *
861 gauge_new (int y, int x, int shown, int max, int current)
863 WGauge *g = g_new (WGauge, 1);
865 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
866 g->shown = shown;
867 if (max == 0)
868 max = 1; /* I do not like division by zero :) */
869 g->max = max;
870 g->current = current;
871 widget_want_cursor (g->widget, 0);
872 return g;
876 /* Input widget */
878 /* {{{ history button */
880 #define LARGE_HISTORY_BUTTON 1
882 #ifdef LARGE_HISTORY_BUTTON
883 # define HISTORY_BUTTON_WIDTH 3
884 #else
885 # define HISTORY_BUTTON_WIDTH 1
886 #endif
888 #define should_show_history_button(in) \
889 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
891 static void draw_history_button (WInput * in)
893 char c;
894 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
895 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
896 #ifdef LARGE_HISTORY_BUTTON
898 Dlg_head *h;
899 h = in->widget.parent;
900 tty_setcolor (NORMAL_COLOR);
901 tty_print_string ("[ ]");
902 /* Too distracting: tty_setcolor (MARKED_COLOR); */
903 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
904 tty_print_char (c);
906 #else
907 tty_setcolor (MARKED_COLOR);
908 tty_print_char (c);
909 #endif
912 /* }}} history button */
915 /* Input widgets now have a global kill ring */
916 /* Pointer to killed data */
917 static char *kill_buffer = 0;
919 void
920 update_input (WInput *in, int clear_first)
922 int has_history = 0;
923 int i;
924 int buf_len = str_length (in->buffer);
925 const char *cp;
926 int pw;
928 if (should_show_history_button (in))
929 has_history = HISTORY_BUTTON_WIDTH;
931 if (in->disable_update)
932 return;
934 pw = str_term_width2 (in->buffer, in->point);
936 /* Make the point visible */
937 if ((pw < in->term_first_shown) ||
938 (pw >= in->term_first_shown + in->field_width - has_history)) {
940 in->term_first_shown = pw - (in->field_width / 3);
941 if (in->term_first_shown < 0)
942 in->term_first_shown = 0;
945 /* Adjust the mark */
946 if (in->mark > buf_len)
947 in->mark = buf_len;
949 if (has_history)
950 draw_history_button (in);
952 tty_setcolor (in->color);
954 widget_move (&in->widget, 0, 0);
956 if (!in->is_password) {
957 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
958 in->field_width - has_history));
959 } else {
960 cp = in->buffer;
961 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
962 if (i >= 0) {
963 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
965 if (cp[0] != '\0') str_cnext_char (&cp);
969 if (clear_first)
970 in->first = 0;
973 void
974 winput_set_origin (WInput *in, int x, int field_width)
976 in->widget.x = x;
977 in->field_width = in->widget.cols = field_width;
978 update_input (in, 0);
981 /* {{{ history saving and loading */
983 int num_history_items_recorded = 60;
986 This loads and saves the history of an input line to and from the
987 widget. It is called with the widgets history name on creation of the
988 widget, and returns the GList list. It stores histories in the file
989 ~/.mc/history in using the profile code.
991 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
992 function) then input_new assigns the default text to be the last text
993 entered, or "" if not found.
996 GList *
997 history_get (const char *input_name)
999 size_t i;
1000 GList *hist = NULL;
1001 char *profile;
1002 mc_config_t *cfg;
1003 char **keys;
1004 size_t keys_num = 0;
1005 char *this_entry;
1007 if (!num_history_items_recorded) /* this is how to disable */
1008 return NULL;
1009 if (!input_name || !*input_name)
1010 return NULL;
1012 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1013 cfg = mc_config_init (profile);
1015 /* get number of keys */
1016 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1017 g_strfreev (keys);
1019 for (i = 0; i < keys_num; i++) {
1020 char key_name[BUF_TINY];
1021 g_snprintf (key_name, sizeof (key_name), "%lu", (unsigned long)i);
1022 this_entry = mc_config_get_string (cfg, input_name, key_name, "");
1024 if (this_entry && *this_entry)
1025 hist = list_append_unique (hist, this_entry);
1028 mc_config_deinit (cfg);
1029 g_free (profile);
1031 /* return pointer to the last entry in the list */
1032 return g_list_last (hist);
1035 void
1036 history_put (const char *input_name, GList *h)
1038 int i;
1039 char *profile;
1040 mc_config_t *cfg;
1042 if (!input_name)
1043 return;
1045 if (!*input_name)
1046 return;
1048 if (!h)
1049 return;
1051 if (!num_history_items_recorded) /* this is how to disable */
1052 return;
1054 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1056 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
1057 close (i);
1059 /* Make sure the history is only readable by the user */
1060 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1061 g_free (profile);
1062 return;
1065 /* go to end of list */
1066 h = g_list_last (h);
1068 /* go back 60 places */
1069 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
1070 h = g_list_previous (h);
1072 cfg = mc_config_init(profile);
1074 if (input_name)
1075 mc_config_del_group(cfg,input_name);
1077 /* dump histories into profile */
1078 for (i = 0; h; h = g_list_next (h)) {
1079 char *text;
1081 text = (char *) h->data;
1083 /* We shouldn't have null entries, but let's be sure */
1084 if (text && *text) {
1085 char key_name[BUF_TINY];
1086 g_snprintf (key_name, sizeof (key_name), "%d", i++);
1087 mc_config_set_string(cfg,input_name, key_name, text);
1091 mc_config_save_file (cfg, NULL);
1092 mc_config_deinit(cfg);
1093 g_free (profile);
1096 /* }}} history saving and loading */
1099 /* {{{ history display */
1101 static const char *
1102 i18n_htitle (void)
1104 return _(" History ");
1107 static void
1108 listbox_fwd (WListbox *l)
1110 if (l->current != l->list->prev)
1111 listbox_select_entry (l, l->current->next);
1112 else
1113 listbox_select_first (l);
1116 typedef struct {
1117 Widget *widget;
1118 int count;
1119 size_t maxlen;
1120 } dlg_hist_data;
1122 static cb_ret_t
1123 dlg_hist_reposition (Dlg_head *dlg_head)
1125 dlg_hist_data *data;
1126 int x = 0, y, he, wi;
1128 /* guard checks */
1129 if ((dlg_head == NULL)
1130 || (dlg_head->data == NULL))
1131 return MSG_NOT_HANDLED;
1133 data = (dlg_hist_data *) dlg_head->data;
1135 y = data->widget->y;
1136 he = data->count + 2;
1138 if (he <= y || y > (LINES - 6)) {
1139 he = min (he, y - 1);
1140 y -= he;
1141 } else {
1142 y++;
1143 he = min (he, LINES - y);
1146 if (data->widget->x > 2)
1147 x = data->widget->x - 2;
1149 wi = data->maxlen + 4;
1151 if ((wi + x) > COLS) {
1152 wi = min (wi, COLS);
1153 x = COLS - wi;
1156 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1158 return MSG_HANDLED;
1161 static cb_ret_t
1162 dlg_hist_callback (Dlg_head *h, Widget *sender,
1163 dlg_msg_t msg, int parm, void *data)
1165 switch (msg) {
1166 case DLG_RESIZE:
1167 return dlg_hist_reposition (h);
1169 default:
1170 return default_dlg_callback (h, sender, msg, parm, data);
1174 char *
1175 show_hist (GList *history, Widget *widget)
1177 GList *hi, *z;
1178 size_t maxlen, i, count = 0;
1179 char *q, *r = NULL;
1180 Dlg_head *query_dlg;
1181 WListbox *query_list;
1182 dlg_hist_data hist_data;
1184 if (history == NULL)
1185 return NULL;
1187 maxlen = str_term_width1 (i18n_htitle ());
1189 z = g_list_first (history);
1190 hi = z;
1191 while (hi) {
1192 i = str_term_width1 ((char *) hi->data);
1193 maxlen = max (maxlen, i);
1194 count++;
1195 hi = g_list_next (hi);
1198 hist_data.maxlen = maxlen;
1199 hist_data.widget = widget;
1200 hist_data.count = count;
1202 query_dlg =
1203 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1204 "[History-query]", i18n_htitle (), DLG_COMPACT);
1205 query_dlg->data = &hist_data;
1207 query_list = listbox_new (1, 1, 2, 2, NULL);
1209 /* this call makes list stick to all sides of dialog, effectively make
1210 it be resized with dialog */
1211 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1213 /* to avoid diplicating of (calculating sizes in two places)
1214 code, call dlg_hist_callback function here, to set dialog and
1215 controls positions.
1216 The main idea - create 4x4 dialog and add 2x2 list in
1217 center of it, and let dialog function resize it to needed
1218 size. */
1219 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1221 if (query_dlg->y < widget->y) {
1222 /* traverse */
1223 hi = z;
1224 while (hi) {
1225 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1226 0, (char *) hi->data, NULL);
1227 hi = g_list_next (hi);
1229 listbox_select_last (query_list);
1230 } else {
1231 /* traverse backwards */
1232 hi = g_list_last (history);
1233 while (hi) {
1234 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1235 0, (char *) hi->data, NULL);
1236 hi = g_list_previous (hi);
1240 if (run_dlg (query_dlg) != B_CANCEL) {
1241 listbox_get_current (query_list, &q, NULL);
1242 if (q != NULL)
1243 r = g_strdup (q);
1245 destroy_dlg (query_dlg);
1246 return r;
1249 static void
1250 do_show_hist (WInput *in)
1252 char *r;
1253 r = show_hist (in->history, &in->widget);
1254 if (r) {
1255 assign_text (in, r);
1256 g_free (r);
1260 /* }}} history display */
1262 static void
1263 input_destroy (WInput *in)
1265 if (!in){
1266 fprintf (stderr, "Internal error: null Input *\n");
1267 exit (1);
1270 new_input (in);
1272 if (in->history){
1273 if (!in->is_password) /* don't save passwords ;-) */
1274 history_put (in->history_name, in->history);
1276 in->history = g_list_first (in->history);
1277 g_list_foreach (in->history, (GFunc) g_free, NULL);
1278 g_list_free (in->history);
1281 g_free (in->buffer);
1282 free_completions (in);
1283 g_free (in->history_name);
1286 void
1287 input_disable_update (WInput *in)
1289 in->disable_update++;
1292 void
1293 input_enable_update (WInput *in)
1295 in->disable_update--;
1296 update_input (in, 0);
1299 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1302 push_history (WInput *in, const char *text)
1304 static int i18n;
1305 /* input widget where urls with passwords are entered without any
1306 vfs prefix */
1307 static const char *password_input_fields[] = {
1308 N_(" Link to a remote machine "),
1309 N_(" FTP to machine "),
1310 N_(" SMB link to machine ")
1312 char *t;
1313 const char *p;
1314 size_t i;
1316 if (!i18n) {
1317 i18n = 1;
1318 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1319 password_input_fields[i] = _(password_input_fields[i]);
1322 for (p = text; *p == ' ' || *p == '\t'; p++);
1323 if (!*p)
1324 return 0;
1326 if (in->history) {
1327 /* Avoid duplicated entries */
1328 in->history = g_list_last (in->history);
1329 if (!strcmp ((char *) in->history->data, text))
1330 return 1;
1333 t = g_strdup (text);
1335 if (in->history_name) {
1336 p = in->history_name + 3;
1337 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1338 if (strcmp (p, password_input_fields[i]) == 0)
1339 break;
1340 if (i < ELEMENTS (password_input_fields))
1341 strip_password (t, 0);
1342 else
1343 strip_password (t, 1);
1346 in->history = list_append_unique (in->history, t);
1347 in->need_push = 0;
1349 return 2;
1352 #undef ELEMENTS
1354 /* Cleans the input line and adds the current text to the history */
1355 void
1356 new_input (WInput *in)
1358 if (in->buffer)
1359 push_history (in, in->buffer);
1360 in->need_push = 1;
1361 in->buffer[0] = '\0';
1362 in->point = 0;
1363 in->charpoint = 0;
1364 in->mark = 0;
1365 free_completions (in);
1366 update_input (in, 0);
1369 static void
1370 move_buffer_backward (WInput *in, int start, int end)
1372 int i, pos, len;
1373 int str_len = str_length (in->buffer);
1374 if (start >= str_len || end > str_len + 1) return;
1376 pos = str_offset_to_pos (in->buffer, start);
1377 len = str_offset_to_pos (in->buffer, end) - pos;
1379 for (i = pos; in->buffer[i + len - 1]; i++)
1380 in->buffer[i] = in->buffer[i + len];
1383 static cb_ret_t
1384 insert_char (WInput *in, int c_code)
1386 size_t i;
1387 int res;
1389 if (c_code == -1)
1390 return MSG_NOT_HANDLED;
1392 if (in->charpoint >= MB_LEN_MAX)
1393 return MSG_HANDLED;
1395 in->charbuf[in->charpoint] = c_code;
1396 in->charpoint++;
1398 res = str_is_valid_char (in->charbuf, in->charpoint);
1399 if (res < 0) {
1400 if (res != -2)
1401 in->charpoint = 0; /* broken multibyte char, skip */
1402 return MSG_HANDLED;
1405 in->need_push = 1;
1406 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1407 /* Expand the buffer */
1408 size_t new_length = in->current_max_size +
1409 in->field_width + in->charpoint;
1410 char *narea = g_try_renew (char, in->buffer, new_length);
1411 if (narea){
1412 in->buffer = narea;
1413 in->current_max_size = new_length;
1417 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1418 /* bytes from begin */
1419 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1420 /* move chars */
1421 size_t rest_bytes = strlen (in->buffer + ins_point);
1423 for (i = rest_bytes + 1; i > 0; i--)
1424 in->buffer[ins_point + i + in->charpoint - 1] =
1425 in->buffer[ins_point + i - 1];
1427 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1428 in->point++;
1431 in->charpoint = 0;
1432 return MSG_HANDLED;
1435 static void
1436 beginning_of_line (WInput *in)
1438 in->point = 0;
1439 in->charpoint = 0;
1442 static void
1443 end_of_line (WInput *in)
1445 in->point = str_length (in->buffer);
1446 in->charpoint = 0;
1449 static void
1450 backward_char (WInput *in)
1452 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1454 if (in->point > 0) {
1455 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1457 in->charpoint = 0;
1460 static void
1461 forward_char (WInput *in)
1463 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1464 if (act[0] != '\0') {
1465 in->point+= str_cnext_noncomb_char (&act);
1467 in->charpoint = 0;
1470 static void
1471 forward_word (WInput * in)
1473 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1475 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1476 str_cnext_char (&p);
1477 in->point++;
1479 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1480 str_cnext_char (&p);
1481 in->point++;
1485 static void
1486 backward_word (WInput *in)
1488 const char *p;
1489 const char *p_tmp;
1491 for (
1492 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1493 (p != in->buffer) && (p[0] == '\0');
1494 str_cprev_char (&p), in->point--
1497 while (p != in->buffer) {
1498 p_tmp = p;
1499 str_cprev_char (&p);
1500 if (!str_isspace (p) && !str_ispunct (p)) {
1501 p = p_tmp;
1502 break;
1504 in->point--;
1506 while (p != in->buffer) {
1507 str_cprev_char (&p);
1508 if (str_isspace (p) || str_ispunct (p))
1509 break;
1511 in->point--;
1515 static void
1516 key_left (WInput *in)
1518 backward_char (in);
1521 static void
1522 key_ctrl_left (WInput *in)
1524 backward_word (in);
1527 static void
1528 key_right (WInput *in)
1530 forward_char (in);
1533 static void
1534 key_ctrl_right (WInput *in)
1536 forward_word (in);
1538 static void
1539 backward_delete (WInput *in)
1541 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1542 int start;
1544 if (in->point == 0)
1545 return;
1547 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1548 move_buffer_backward(in, start, in->point);
1549 in->charpoint = 0;
1550 in->need_push = 1;
1551 in->point = start;
1554 static void
1555 delete_char (WInput *in)
1557 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1558 int end = in->point;
1560 end+= str_cnext_noncomb_char (&act);
1562 move_buffer_backward(in, in->point, end);
1563 in->charpoint = 0;
1564 in->need_push = 1;
1567 static void
1568 copy_region (WInput *in, int x_first, int x_last)
1570 int first = min (x_first, x_last);
1571 int last = max (x_first, x_last);
1573 if (last == first) {
1574 /* Copy selected files to clipboard */
1575 panel_save_curent_file_to_clip_file ();
1576 return;
1579 g_free (kill_buffer);
1581 first = str_offset_to_pos (in->buffer, first);
1582 last = str_offset_to_pos (in->buffer, last);
1584 kill_buffer = g_strndup(in->buffer + first, last - first);
1585 save_text_to_clip_file (kill_buffer);
1588 static void
1589 delete_region (WInput *in, int x_first, int x_last)
1591 int first = min (x_first, x_last);
1592 int last = max (x_first, x_last);
1593 size_t len;
1595 in->point = first;
1596 in->mark = first;
1597 last = str_offset_to_pos (in->buffer, last);
1598 first = str_offset_to_pos (in->buffer, first);
1599 len = strlen (&in->buffer[last]) + 1;
1600 memmove (&in->buffer[first], &in->buffer[last], len);
1601 in->charpoint = 0;
1602 in->need_push = 1;
1605 static void
1606 kill_word (WInput *in)
1608 int old_point = in->point;
1609 int new_point;
1611 forward_word (in);
1612 new_point = in->point;
1613 in->point = old_point;
1615 copy_region (in, old_point, new_point);
1616 delete_region (in, old_point, new_point);
1617 in->need_push = 1;
1618 in->charpoint = 0;
1619 in->charpoint = 0;
1622 static void
1623 back_kill_word (WInput *in)
1625 int old_point = in->point;
1626 int new_point;
1628 backward_word (in);
1629 new_point = in->point;
1630 in->point = old_point;
1632 copy_region (in, old_point, new_point);
1633 delete_region (in, old_point, new_point);
1634 in->need_push = 1;
1637 static void
1638 set_mark (WInput *in)
1640 in->mark = in->point;
1643 static void
1644 kill_save (WInput *in)
1646 copy_region (in, in->mark, in->point);
1649 static void
1650 kill_region (WInput *in)
1652 kill_save (in);
1653 delete_region (in, in->point, in->mark);
1656 static void
1657 clear_region (WInput *in)
1659 delete_region (in, in->point, in->mark);
1662 static void
1663 yank (WInput *in)
1665 char *p;
1667 if (!kill_buffer)
1668 return;
1669 in->charpoint = 0;
1670 for (p = kill_buffer; *p; p++)
1671 insert_char (in, *p);
1672 in->charpoint = 0;
1675 static void
1676 kill_line (WInput *in)
1678 int chp = str_offset_to_pos (in->buffer, in->point);
1679 g_free (kill_buffer);
1680 kill_buffer = g_strdup (&in->buffer[chp]);
1681 in->buffer[chp] = '\0';
1682 in->charpoint = 0;
1685 static void
1686 ins_from_clip (WInput *in)
1688 char *p = NULL;
1690 if (load_text_from_clip_file (&p)) {
1691 char *pp;
1693 for (pp = p; *pp != '\0'; pp++)
1694 insert_char (in, *pp);
1696 g_free (p);
1700 void
1701 assign_text (WInput *in, const char *text)
1703 free_completions (in);
1704 g_free (in->buffer);
1705 in->buffer = g_strdup (text); /* was in->buffer->text */
1706 in->current_max_size = strlen (in->buffer) + 1;
1707 in->point = str_length (in->buffer);
1708 in->mark = 0;
1709 in->need_push = 1;
1710 in->charpoint = 0;
1713 static void
1714 hist_prev (WInput *in)
1716 if (!in->history)
1717 return;
1719 if (in->need_push) {
1720 switch (push_history (in, in->buffer)) {
1721 case 2:
1722 in->history = g_list_previous (in->history);
1723 break;
1724 case 1:
1725 if (in->history->prev)
1726 in->history = g_list_previous (in->history);
1727 break;
1728 case 0:
1729 break;
1731 } else if (in->history->prev)
1732 in->history = g_list_previous (in->history);
1733 else
1734 return;
1735 assign_text (in, (char *) in->history->data);
1736 in->need_push = 0;
1739 static void
1740 hist_next (WInput *in)
1742 if (in->need_push) {
1743 switch (push_history (in, in->buffer)) {
1744 case 2:
1745 assign_text (in, "");
1746 return;
1747 case 0:
1748 return;
1752 if (!in->history)
1753 return;
1755 if (!in->history->next) {
1756 assign_text (in, "");
1757 return;
1760 in->history = g_list_next (in->history);
1761 assign_text (in, (char *) in->history->data);
1762 in->need_push = 0;
1765 static void
1766 port_region_marked_for_delete (WInput *in)
1768 in->buffer[0] = '\0';
1769 in->point = 0;
1770 in->first = 0;
1771 in->charpoint = 0;
1774 static cb_ret_t
1775 input_execute_cmd (WInput *in, unsigned long command)
1777 cb_ret_t res = MSG_HANDLED;
1779 switch (command) {
1780 case CK_InputBol:
1781 beginning_of_line (in);
1782 break;
1783 case CK_InputEol:
1784 end_of_line (in);
1785 break;
1786 case CK_InputMoveLeft:
1787 key_left (in);
1788 break;
1789 case CK_InputWordLeft:
1790 key_ctrl_left (in);
1791 break;
1792 case CK_InputMoveRight:
1793 key_right (in);
1794 break;
1795 case CK_InputWordRight:
1796 key_ctrl_right (in);
1797 break;
1798 case CK_InputBackwardChar:
1799 backward_char (in);
1800 break;
1801 case CK_InputBackwardWord:
1802 backward_word (in);
1803 break;
1804 case CK_InputForwardChar:
1805 forward_char (in);
1806 break;
1807 case CK_InputForwardWord:
1808 forward_word (in);
1809 break;
1810 case CK_InputBackwardDelete:
1811 backward_delete (in);
1812 break;
1813 case CK_InputDeleteChar:
1814 delete_char (in);
1815 break;
1816 case CK_InputKillWord:
1817 kill_word (in);
1818 break;
1819 case CK_InputBackwardKillWord:
1820 back_kill_word (in);
1821 break;
1822 case CK_InputSetMark:
1823 set_mark (in);
1824 break;
1825 case CK_InputKillRegion:
1826 kill_region (in);
1827 break;
1828 case CK_InputClearLine:
1829 clear_region (in);
1830 break;
1831 case CK_InputKillSave:
1832 kill_save (in);
1833 break;
1834 case CK_InputYank:
1835 yank (in);
1836 break;
1837 case CK_InputPaste:
1838 ins_from_clip (in);
1839 break;
1840 case CK_InputKillLine:
1841 kill_line (in);
1842 break;
1843 case CK_InputHistoryPrev:
1844 hist_prev (in);
1845 break;
1846 case CK_InputHistoryNext:
1847 hist_next (in);
1848 break;
1849 case CK_InputHistoryShow:
1850 do_show_hist (in);
1851 break;
1852 case CK_InputComplete:
1853 complete (in);
1854 break;
1855 default:
1856 res = MSG_NOT_HANDLED;
1859 return res;
1862 /* This function is a test for a special input key used in complete.c */
1863 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1864 and 2 if it is a complete key */
1866 is_in_input_map (WInput *in, int key)
1868 size_t i;
1869 for (i = 0; input_map[i].key != 0; i++)
1870 if (key == input_map[i].key) {
1871 input_execute_cmd (in, input_map[i].command);
1872 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1874 return 0;
1877 cb_ret_t
1878 handle_char (WInput *in, int key)
1880 cb_ret_t v;
1881 int i;
1883 v = MSG_NOT_HANDLED;
1885 if (quote) {
1886 free_completions (in);
1887 v = insert_char (in, key);
1888 update_input (in, 1);
1889 quote = 0;
1890 return v;
1892 for (i = 0; input_map[i].key; i++) {
1893 if (key == input_map[i].key) {
1894 if (input_map[i].command != CK_InputComplete)
1895 free_completions (in);
1896 input_execute_cmd (in, input_map[i].command);
1897 update_input (in, 1);
1898 v = MSG_HANDLED;
1899 break;
1902 if (input_map[i].command == 0) {
1903 if (key > 255)
1904 return MSG_NOT_HANDLED;
1905 if (in->first)
1906 port_region_marked_for_delete (in);
1907 free_completions (in);
1908 v = insert_char (in, key);
1910 update_input (in, 1);
1911 return v;
1914 /* Inserts text in input line */
1915 void
1916 stuff (WInput *in, const char *text, int insert_extra_space)
1918 input_disable_update (in);
1919 while (*text)
1920 handle_char (in, *text++);
1921 if (insert_extra_space)
1922 handle_char (in, ' ');
1923 input_enable_update (in);
1924 update_input (in, 1);
1927 void
1928 input_set_point (WInput *in, int pos)
1930 int max_pos = str_length (in->buffer);
1932 if (pos > max_pos)
1933 pos = max_pos;
1934 if (pos != in->point)
1935 free_completions (in);
1936 in->point = pos;
1937 in->charpoint = 0;
1938 update_input (in, 1);
1941 cb_ret_t
1942 input_callback (Widget *w, widget_msg_t msg, int parm)
1944 WInput *in = (WInput *) w;
1945 cb_ret_t v;
1947 switch (msg) {
1948 case WIDGET_KEY:
1949 if (parm == XCTRL ('q')) {
1950 quote = 1;
1951 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1952 quote = 0;
1953 return v;
1956 /* Keys we want others to handle */
1957 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1958 || parm == KEY_F (10) || parm == '\n')
1959 return MSG_NOT_HANDLED;
1961 /* When pasting multiline text, insert literal Enter */
1962 if ((parm & ~KEY_M_MASK) == '\n') {
1963 quote = 1;
1964 v = handle_char (in, '\n');
1965 quote = 0;
1966 return v;
1969 return handle_char (in, parm);
1971 case WIDGET_COMMAND:
1972 return input_execute_cmd (in, parm);
1974 case WIDGET_FOCUS:
1975 case WIDGET_UNFOCUS:
1976 case WIDGET_DRAW:
1977 update_input (in, 0);
1978 return MSG_HANDLED;
1980 case WIDGET_CURSOR:
1981 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1982 - in->term_first_shown);
1983 return MSG_HANDLED;
1985 case WIDGET_DESTROY:
1986 input_destroy (in);
1987 return MSG_HANDLED;
1989 default:
1990 return default_proc (msg, parm);
1994 static int
1995 input_event (Gpm_Event * event, void *data)
1997 WInput *in = data;
1999 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2000 dlg_select_widget (in);
2002 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2003 && should_show_history_button (in)) {
2004 do_show_hist (in);
2005 } else {
2006 in->point = str_length (in->buffer);
2007 if (event->x + in->term_first_shown - 1 <
2008 str_term_width1 (in->buffer))
2010 in->point = str_column_to_pos (in->buffer, event->x
2011 + in->term_first_shown - 1);
2014 update_input (in, 1);
2016 return MOU_NORMAL;
2019 WInput *
2020 input_new (int y, int x, int color, int width, const char *def_text,
2021 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2023 WInput *in = g_new (WInput, 1);
2024 int initial_buffer_len;
2026 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2028 /* history setup */
2029 in->history = NULL;
2030 in->history_name = 0;
2031 if (histname) {
2032 if (*histname) {
2033 in->history_name = g_strdup (histname);
2034 in->history = history_get (histname);
2038 if (def_text == NULL)
2039 def_text = "";
2041 if (def_text == INPUT_LAST_TEXT) {
2042 def_text = "";
2043 if (in->history)
2044 if (in->history->data)
2045 def_text = (char *) in->history->data;
2047 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2048 in->widget.options |= W_IS_INPUT;
2049 in->completions = NULL;
2050 in->completion_flags = completion_flags;
2051 in->current_max_size = initial_buffer_len;
2052 in->buffer = g_new (char, initial_buffer_len);
2053 in->color = color;
2054 in->field_width = width;
2055 in->first = 1;
2056 in->term_first_shown = 0;
2057 in->disable_update = 0;
2058 in->mark = 0;
2059 in->need_push = 1;
2060 in->is_password = 0;
2062 strcpy (in->buffer, def_text);
2063 in->point = str_length (in->buffer);
2064 in->charpoint = 0;
2065 return in;
2068 /* Listbox widget */
2070 /* Should draw the scrollbar, but currently draws only
2071 * indications that there is more information
2073 static int listbox_cdiff (WLEntry *s, WLEntry *e);
2075 static void
2076 listbox_drawscroll (WListbox *l)
2078 int line = 0;
2079 int i, top;
2080 int max_line = l->widget.lines - 1;
2082 /* Are we at the top? */
2083 widget_move (&l->widget, 0, l->widget.cols);
2084 if (l->list == l->top)
2085 tty_print_one_vline ();
2086 else
2087 tty_print_char ('^');
2089 /* Are we at the bottom? */
2090 widget_move (&l->widget, max_line, l->widget.cols);
2091 top = listbox_cdiff (l->list, l->top);
2092 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
2093 tty_print_one_vline ();
2094 else
2095 tty_print_char ('v');
2097 /* Now draw the nice relative pointer */
2098 if (l->count != 0)
2099 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
2101 for (i = 1; i < max_line; i++){
2102 widget_move (&l->widget, i, l->widget.cols);
2103 if (i != line)
2104 tty_print_one_vline ();
2105 else
2106 tty_print_char ('*');
2110 static void
2111 listbox_draw (WListbox *l, gboolean focused)
2113 const Dlg_head *h = l->widget.parent;
2114 const int normalc = DLG_NORMALC (h);
2115 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2117 WLEntry *e;
2118 int i;
2119 int sel_line = -1;
2120 const char *text;
2122 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2123 /* Display the entry */
2124 if (e == l->current && sel_line == -1) {
2125 sel_line = i;
2126 tty_setcolor (selc);
2127 } else
2128 tty_setcolor (normalc);
2130 widget_move (&l->widget, i, 1);
2132 if ((i > 0 && e == l->list) || !l->list)
2133 text = "";
2134 else {
2135 text = e->text;
2136 e = e->next;
2138 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2140 l->cursor_y = sel_line;
2142 if (l->scrollbar && l->count > l->widget.lines) {
2143 tty_setcolor (normalc);
2144 listbox_drawscroll (l);
2148 /* Returns the number of items between s and e,
2149 must be on the same linked list */
2150 static int
2151 listbox_cdiff (WLEntry *s, WLEntry *e)
2153 int count;
2155 for (count = 0; s != e; count++)
2156 s = s->next;
2157 return count;
2160 static WLEntry *
2161 listbox_check_hotkey (WListbox *l, int key)
2163 int i;
2164 WLEntry *e;
2166 i = 0;
2167 e = l->list;
2168 if (!e)
2169 return NULL;
2171 while (1) {
2173 /* If we didn't find anything, return */
2174 if (i && e == l->list)
2175 return NULL;
2177 if (e->hotkey == key)
2178 return e;
2180 i++;
2181 e = e->next;
2185 /* Selects the last entry and scrolls the list to the bottom */
2186 void
2187 listbox_select_last (WListbox *l)
2189 unsigned int i;
2190 l->current = l->top = l->list->prev;
2191 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2192 l->top = l->top->prev;
2193 l->pos = l->count - 1;
2196 /* Selects the first entry and scrolls the list to the top */
2197 void
2198 listbox_select_first (WListbox *l)
2200 l->current = l->top = l->list;
2201 l->pos = 0;
2204 void
2205 listbox_remove_list (WListbox *l)
2207 WLEntry *p, *q;
2209 if (!l->count)
2210 return;
2212 p = l->list;
2214 while (l->count--) {
2215 q = p->next;
2216 g_free (p->text);
2217 g_free (p);
2218 p = q;
2220 l->pos = l->count = 0;
2221 l->list = l->top = l->current = 0;
2225 * bor 30.10.96: added force flag to remove *last* entry as well
2226 * bor 30.10.96: corrected selection bug if last entry was removed
2229 void
2230 listbox_remove_current (WListbox *l, int force)
2232 WLEntry *p;
2234 /* Ok, note: this won't allow for emtpy lists */
2235 if (!force && (!l->count || l->count == 1))
2236 return;
2238 l->count--;
2239 p = l->current;
2241 if (l->count) {
2242 l->current->next->prev = l->current->prev;
2243 l->current->prev->next = l->current->next;
2244 if (p->next == l->list) {
2245 l->current = p->prev;
2246 l->pos--;
2248 else
2249 l->current = p->next;
2251 if (p == l->list)
2252 l->list = l->top = p->next;
2253 } else {
2254 l->pos = 0;
2255 l->list = l->top = l->current = 0;
2258 g_free (p->text);
2259 g_free (p);
2262 /* Makes *e the selected entry (sets current and pos) */
2263 void
2264 listbox_select_entry (WListbox *l, WLEntry *dest)
2266 WLEntry *e;
2267 int pos;
2268 int top_seen;
2270 top_seen = 0;
2272 /* Special case */
2273 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2275 if (e == l->top)
2276 top_seen = 1;
2278 if (e == dest){
2279 l->current = e;
2280 if (top_seen){
2281 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2282 l->top = l->top->next;
2283 } else {
2284 l->top = l->current;
2286 l->pos = pos;
2287 return;
2290 /* If we are unable to find it, set decent values */
2291 l->current = l->top = l->list;
2292 l->pos = 0;
2295 /* Selects from base the pos element */
2296 static WLEntry *
2297 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2299 WLEntry *last = l->list->prev;
2301 if (base == last)
2302 return last;
2303 while (pos--){
2304 base = base->next;
2305 if (base == last)
2306 break;
2308 return base;
2311 static void
2312 listbox_back (WListbox *l)
2314 if (l->pos != 0)
2315 listbox_select_entry (l, l->current->prev);
2316 else
2317 listbox_select_last (l);
2320 /* Return MSG_HANDLED if we want a redraw */
2321 static cb_ret_t
2322 listbox_key (WListbox *l, int key)
2324 int i;
2326 cb_ret_t j = MSG_NOT_HANDLED;
2328 /* focus on listbox item N by '0'..'9' keys */
2329 if (key >= '0' && key <= '9') {
2330 int oldpos = l->pos;
2331 listbox_select_by_number(l, key - '0');
2333 /* need scroll to item? */
2334 if (abs(oldpos - l->pos) > l->widget.lines)
2335 l->top = l->current;
2337 return MSG_HANDLED;
2340 if (!l->list)
2341 return MSG_NOT_HANDLED;
2343 switch (key){
2344 case KEY_HOME:
2345 case KEY_A1:
2346 case ALT ('<'):
2347 listbox_select_first (l);
2348 return MSG_HANDLED;
2350 case KEY_END:
2351 case KEY_C1:
2352 case ALT ('>'):
2353 listbox_select_last (l);
2354 return MSG_HANDLED;
2356 case XCTRL('p'):
2357 case KEY_UP:
2358 listbox_back (l);
2359 return MSG_HANDLED;
2361 case XCTRL('n'):
2362 case KEY_DOWN:
2363 listbox_fwd (l);
2364 return MSG_HANDLED;
2366 case KEY_NPAGE:
2367 case XCTRL('v'):
2368 for (i = 0; ((i < l->widget.lines - 1)
2369 && (l->current != l->list->prev)); i++) {
2370 listbox_fwd (l);
2371 j = MSG_HANDLED;
2373 return j;
2375 case KEY_PPAGE:
2376 case ALT('v'):
2377 for (i = 0; ((i < l->widget.lines - 1)
2378 && (l->current != l->list)); i++) {
2379 listbox_back (l);
2380 j = MSG_HANDLED;
2382 return j;
2384 return MSG_NOT_HANDLED;
2387 static void
2388 listbox_destroy (WListbox *l)
2390 WLEntry *n, *p = l->list;
2391 int i;
2393 for (i = 0; i < l->count; i++){
2394 n = p->next;
2395 g_free (p->text);
2396 g_free (p);
2397 p = n;
2401 static cb_ret_t
2402 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2404 WListbox *l = (WListbox *) w;
2405 Dlg_head *h = l->widget.parent;
2406 WLEntry *e;
2407 cb_ret_t ret_code;
2409 switch (msg) {
2410 case WIDGET_INIT:
2411 return MSG_HANDLED;
2413 case WIDGET_HOTKEY:
2414 e = listbox_check_hotkey (l, parm);
2415 if (e != NULL) {
2416 int action;
2418 listbox_select_entry (l, e);
2419 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2421 if (l->cback)
2422 action = (*l->cback) (l);
2423 else
2424 action = LISTBOX_DONE;
2426 if (action == LISTBOX_DONE) {
2427 h->ret_value = B_ENTER;
2428 dlg_stop (h);
2430 return MSG_HANDLED;
2432 return MSG_NOT_HANDLED;
2434 case WIDGET_KEY:
2435 ret_code = listbox_key (l, parm);
2436 if (ret_code != MSG_NOT_HANDLED) {
2437 listbox_draw (l, TRUE);
2438 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2440 return ret_code;
2442 case WIDGET_CURSOR:
2443 widget_move (&l->widget, l->cursor_y, 0);
2444 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2445 return MSG_HANDLED;
2447 case WIDGET_FOCUS:
2448 case WIDGET_UNFOCUS:
2449 case WIDGET_DRAW:
2450 listbox_draw (l, msg != WIDGET_UNFOCUS);
2451 return MSG_HANDLED;
2453 case WIDGET_DESTROY:
2454 listbox_destroy (l);
2455 return MSG_HANDLED;
2457 case WIDGET_RESIZED:
2458 return MSG_HANDLED;
2460 default:
2461 return default_proc (msg, parm);
2465 static int
2466 listbox_event (Gpm_Event *event, void *data)
2468 WListbox *l = data;
2469 int i;
2471 Dlg_head *h = l->widget.parent;
2473 /* Single click */
2474 if (event->type & GPM_DOWN)
2475 dlg_select_widget (l);
2477 if (l->list == NULL)
2478 return MOU_NORMAL;
2480 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2481 int ret = MOU_REPEAT;
2483 if (event->x < 0 || event->x > l->widget.cols)
2484 return ret;
2486 if (event->y < 1)
2487 for (i = -event->y; i >= 0; i--)
2488 listbox_back (l);
2489 else if (event->y > l->widget.lines)
2490 for (i = event->y - l->widget.lines; i > 0; i--)
2491 listbox_fwd (l);
2492 else if (event->buttons & GPM_B_UP) {
2493 listbox_back (l);
2494 ret = MOU_NORMAL;
2495 } else if (event->buttons & GPM_B_DOWN) {
2496 listbox_fwd (l);
2497 ret = MOU_NORMAL;
2498 } else
2499 listbox_select_entry (l,
2500 listbox_select_pos (l, l->top,
2501 event->y - 1));
2503 /* We need to refresh ourselves since the dialog manager doesn't */
2504 /* know about this event */
2505 listbox_draw (l, TRUE);
2506 return ret;
2509 /* Double click */
2510 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2511 int action;
2513 if (event->x < 0 || event->x >= l->widget.cols
2514 || event->y < 1 || event->y > l->widget.lines)
2515 return MOU_NORMAL;
2517 dlg_select_widget (l);
2518 listbox_select_entry (l,
2519 listbox_select_pos (l, l->top,
2520 event->y - 1));
2522 if (l->cback)
2523 action = (*l->cback) (l);
2524 else
2525 action = LISTBOX_DONE;
2527 if (action == LISTBOX_DONE) {
2528 h->ret_value = B_ENTER;
2529 dlg_stop (h);
2530 return MOU_NORMAL;
2533 return MOU_NORMAL;
2536 WListbox *
2537 listbox_new (int y, int x, int height, int width, lcback callback)
2539 WListbox *l = g_new (WListbox, 1);
2541 if (height <= 0)
2542 height = 1;
2544 init_widget (&l->widget, y, x, height, width,
2545 listbox_callback, listbox_event);
2547 l->list = l->top = l->current = 0;
2548 l->pos = 0;
2549 l->count = 0;
2550 l->cback = callback;
2551 l->allow_duplicates = 1;
2552 l->scrollbar = !tty_is_slow ();
2553 widget_want_hotkey (l->widget, 1);
2555 return l;
2558 /* Listbox item adding function. They still lack a lot of functionality */
2559 /* any takers? */
2560 /* 1.11.96 bor: added pos argument to control placement of new entry */
2561 static void
2562 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2564 if (!l->list){
2565 l->list = e;
2566 l->top = e;
2567 l->current = e;
2568 e->next = l->list;
2569 e->prev = l->list;
2570 } else if (pos == LISTBOX_APPEND_AT_END) {
2571 e->next = l->list;
2572 e->prev = l->list->prev;
2573 l->list->prev->next = e;
2574 l->list->prev = e;
2575 } else if (pos == LISTBOX_APPEND_BEFORE){
2576 e->next = l->current;
2577 e->prev = l->current->prev;
2578 l->current->prev->next = e;
2579 l->current->prev = e;
2580 if (l->list == l->current) { /* move list one position down */
2581 l->list = e;
2582 l->top = e;
2584 } else if (pos == LISTBOX_APPEND_AFTER) {
2585 e->prev = l->current;
2586 e->next = l->current->next;
2587 l->current->next->prev = e;
2588 l->current->next = e;
2589 } else if (pos == LISTBOX_APPEND_SORTED) {
2590 WLEntry *w = l->list;
2592 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2593 w = w->next;
2594 if (w->next == l->list) {
2595 e->prev = w;
2596 e->next = l->list;
2597 w->next = e;
2598 l->list->prev = e;
2599 } else {
2600 e->next = w;
2601 e->prev = w->prev;
2602 w->prev->next = e;
2603 w->prev = e;
2606 l->count++;
2609 char *
2610 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2611 const char *text, void *data)
2613 WLEntry *entry;
2615 if (!l)
2616 return NULL;
2618 if (!l->allow_duplicates)
2619 if (listbox_search_text (l, text))
2620 return NULL;
2622 entry = g_new (WLEntry, 1);
2623 entry->text = g_strdup (text);
2624 entry->data = data;
2625 entry->hotkey = hotkey;
2627 listbox_append_item (l, entry, pos);
2629 return entry->text;
2632 /* Selects the nth entry in the listbox */
2633 void
2634 listbox_select_by_number (WListbox *l, int n)
2636 if (l->list != NULL)
2637 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2640 WLEntry *
2641 listbox_search_text (WListbox *l, const char *text)
2643 WLEntry *e;
2645 e = l->list;
2646 if (!e)
2647 return NULL;
2649 do {
2650 if(!strcmp (e->text, text))
2651 return e;
2652 e = e->next;
2653 } while (e!=l->list);
2655 return NULL;
2658 /* Returns the current string text as well as the associated extra data */
2659 void
2660 listbox_get_current (WListbox *l, char **string, char **extra)
2662 if (!l->current){
2663 *string = 0;
2664 *extra = 0;
2666 if (string && l->current)
2667 *string = l->current->text;
2668 if (extra && l->current)
2669 *extra = l->current->data;
2672 /* returns TRUE if a function has been called, FALSE otherwise. */
2673 static gboolean
2674 buttonbar_call (WButtonBar *bb, int i)
2676 cb_ret_t ret = MSG_NOT_HANDLED;
2678 if (bb != NULL)
2679 ret = bb->widget.parent->callback (bb->widget.parent,
2680 (Widget *) bb, DLG_ACTION,
2681 bb->labels[i].command,
2682 bb->labels[i].receiver);
2683 return ret;
2686 /* calculate width of one button, width is never lesser than 7 */
2687 static int
2688 buttonbat_get_button_width (void)
2690 int result = COLS / BUTTONBAR_LABELS_NUM;
2691 return (result >= 7) ? result : 7;
2694 static cb_ret_t
2695 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2697 WButtonBar *bb = (WButtonBar *) w;
2698 int i;
2699 const char *text;
2701 switch (msg) {
2702 case WIDGET_FOCUS:
2703 return MSG_NOT_HANDLED;
2705 case WIDGET_HOTKEY:
2706 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2707 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2708 return MSG_HANDLED;
2709 return MSG_NOT_HANDLED;
2711 case WIDGET_DRAW:
2712 if (bb->visible) {
2713 int offset = 0;
2714 int count_free_positions;
2716 widget_move (&bb->widget, 0, 0);
2717 tty_setcolor (DEFAULT_COLOR);
2718 bb->btn_width = buttonbat_get_button_width ();
2719 tty_printf ("%-*s", bb->widget.cols, "");
2720 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2722 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2723 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2724 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2725 tty_printf ("%2d", i + 1);
2726 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2727 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2728 tty_print_string (str_fit_to_term (
2729 text,
2730 bb->btn_width - 2 + (int)(offset < count_free_positions),
2731 J_LEFT_FIT));
2733 if (count_free_positions != 0 && offset < count_free_positions)
2734 offset++;
2737 return MSG_HANDLED;
2739 case WIDGET_DESTROY:
2740 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2741 g_free (bb->labels[i].text);
2742 return MSG_HANDLED;
2744 default:
2745 return default_proc (msg, parm);
2749 static int
2750 buttonbar_event (Gpm_Event *event, void *data)
2752 WButtonBar *bb = data;
2753 int button;
2755 if (!(event->type & GPM_UP))
2756 return MOU_NORMAL;
2757 if (event->y == 2)
2758 return MOU_NORMAL;
2759 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2760 if (button < BUTTONBAR_LABELS_NUM)
2761 buttonbar_call (bb, button);
2762 return MOU_NORMAL;
2765 WButtonBar *
2766 buttonbar_new (gboolean visible)
2768 WButtonBar *bb;
2770 bb = g_new0 (WButtonBar, 1);
2772 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2773 buttonbar_callback, buttonbar_event);
2774 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2775 bb->visible = visible;
2776 widget_want_hotkey (bb->widget, 1);
2777 widget_want_cursor (bb->widget, 0);
2778 bb->btn_width = buttonbat_get_button_width ();
2780 return bb;
2783 static void
2784 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2786 g_free (bb->labels[lc_index - 1].text);
2787 bb->labels[lc_index - 1].text = g_strdup (text);
2790 /* Find ButtonBar widget in the dialog */
2791 WButtonBar *
2792 find_buttonbar (const Dlg_head *h)
2794 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2797 void
2798 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2799 const struct global_keymap_t *keymap, const Widget *receiver)
2801 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2802 unsigned long command = CK_Ignore_Key;
2804 if (keymap != NULL)
2805 command = lookup_keymap_command (keymap, KEY_F (idx));
2807 if ((text == NULL) || (text[0] == '\0'))
2808 set_label_text (bb, idx, "");
2809 else
2810 set_label_text (bb, idx, text);
2812 bb->labels[idx - 1].command = command;
2813 bb->labels[idx - 1].receiver = (Widget *) receiver;
2817 void
2818 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2820 bb->visible = visible;
2823 void
2824 buttonbar_redraw (WButtonBar *bb)
2826 if (bb != NULL)
2827 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2830 static cb_ret_t
2831 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2833 WGroupbox *g = (WGroupbox *) w;
2835 switch (msg) {
2836 case WIDGET_INIT:
2837 return MSG_HANDLED;
2839 case WIDGET_FOCUS:
2840 return MSG_NOT_HANDLED;
2842 case WIDGET_DRAW:
2843 tty_setcolor (COLOR_NORMAL);
2844 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2845 g->widget.x - g->widget.parent->x, g->widget.lines,
2846 g->widget.cols);
2848 tty_setcolor (COLOR_HOT_NORMAL);
2849 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2850 g->widget.x - g->widget.parent->x + 1);
2851 tty_print_string (g->title);
2852 return MSG_HANDLED;
2854 case WIDGET_DESTROY:
2855 g_free (g->title);
2856 return MSG_HANDLED;
2858 default:
2859 return default_proc (msg, parm);
2863 WGroupbox *
2864 groupbox_new (int y, int x, int height, int width, const char *title)
2866 WGroupbox *g = g_new (WGroupbox, 1);
2868 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2870 g->widget.options &= ~W_WANT_CURSOR;
2871 widget_want_hotkey (g->widget, 0);
2873 /* Strip existing spaces, add one space before and after the title */
2874 if (title) {
2875 char *t;
2876 t = g_strstrip (g_strdup (title));
2877 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2878 g_free (t);
2881 return g;