Scroll list if last item have been deleted.
[kaloumi3.git] / src / widget.c
blob4d8bce741b84245e1b1fcf9251bb8903526cf5c0
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/skin.h"
47 #include "lib/tty/mouse.h"
48 #include "lib/tty/key.h" /* XCTRL and ALT macros */
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 */
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, (char *) 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 == 0) /* this is how to disable */
1008 return NULL;
1009 if ((input_name == NULL) || (*input_name == '\0'))
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[BUF_TINY];
1021 g_snprintf (key, sizeof (key), "%lu", (unsigned long)i);
1022 this_entry = mc_config_get_string (cfg, input_name, key, "");
1024 if (this_entry != NULL)
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 (num_history_items_recorded == 0) /* this is how to disable */
1043 return;
1044 if ((input_name == NULL) || (*input_name == '\0'))
1045 return;
1046 if (h == NULL)
1047 return;
1049 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1051 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1052 if (i != -1)
1053 close (i);
1055 /* Make sure the history is only readable by the user */
1056 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1057 g_free (profile);
1058 return;
1061 /* go to end of list */
1062 h = g_list_last (h);
1064 /* go back 60 places */
1065 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1066 h = g_list_previous (h);
1068 cfg = mc_config_init (profile);
1070 if (input_name != NULL)
1071 mc_config_del_group (cfg, input_name);
1073 /* dump history into profile */
1074 for (i = 0; h != NULL; h = g_list_next (h)) {
1075 char *text = (char *) h->data;
1077 /* We shouldn't have null entries, but let's be sure */
1078 if (text != NULL) {
1079 char key[BUF_TINY];
1080 g_snprintf (key, sizeof (key), "%d", i++);
1081 mc_config_set_string (cfg, input_name, key, text);
1085 mc_config_save_file (cfg, NULL);
1086 mc_config_deinit(cfg);
1087 g_free (profile);
1090 /* }}} history saving and loading */
1093 /* {{{ history display */
1095 static const char *
1096 i18n_htitle (void)
1098 return _(" History ");
1101 typedef struct {
1102 Widget *widget;
1103 size_t count;
1104 size_t maxlen;
1105 } dlg_hist_data;
1107 static cb_ret_t
1108 dlg_hist_reposition (Dlg_head *dlg_head)
1110 dlg_hist_data *data;
1111 int x = 0, y, he, wi;
1113 /* guard checks */
1114 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1115 return MSG_NOT_HANDLED;
1117 data = (dlg_hist_data *) dlg_head->data;
1119 y = data->widget->y;
1120 he = data->count + 2;
1122 if (he <= y || y > (LINES - 6)) {
1123 he = min (he, y - 1);
1124 y -= he;
1125 } else {
1126 y++;
1127 he = min (he, LINES - y);
1130 if (data->widget->x > 2)
1131 x = data->widget->x - 2;
1133 wi = data->maxlen + 4;
1135 if ((wi + x) > COLS) {
1136 wi = min (wi, COLS);
1137 x = COLS - wi;
1140 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1142 return MSG_HANDLED;
1145 static cb_ret_t
1146 dlg_hist_callback (Dlg_head *h, Widget *sender,
1147 dlg_msg_t msg, int parm, void *data)
1149 switch (msg) {
1150 case DLG_RESIZE:
1151 return dlg_hist_reposition (h);
1153 default:
1154 return default_dlg_callback (h, sender, msg, parm, data);
1158 char *
1159 show_hist (GList **history, Widget *widget)
1161 GList *z, *hlist = NULL, *hi;
1162 size_t maxlen, i, count = 0;
1163 char *r = NULL;
1164 Dlg_head *query_dlg;
1165 WListbox *query_list;
1166 dlg_hist_data hist_data;
1168 if (*history == NULL)
1169 return NULL;
1171 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1173 for (z = *history; z != NULL; z = g_list_previous (z)) {
1174 WLEntry *entry;
1176 i = str_term_width1 ((char *) z->data);
1177 maxlen = max (maxlen, i);
1178 count++;
1180 entry = g_new0 (WLEntry, 1);
1181 /* history is being reverted here */
1182 entry->text = g_strdup ((char *) z->data);
1183 hlist = g_list_prepend (hlist, entry);
1186 hist_data.widget = widget;
1187 hist_data.count = count;
1188 hist_data.maxlen = maxlen;
1190 query_dlg =
1191 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1192 "[History-query]", i18n_htitle (), DLG_COMPACT);
1193 query_dlg->data = &hist_data;
1195 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1197 /* this call makes list stick to all sides of dialog, effectively make
1198 it be resized with dialog */
1199 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1201 /* to avoid diplicating of (calculating sizes in two places)
1202 code, call dlg_hist_callback function here, to set dialog and
1203 controls positions.
1204 The main idea - create 4x4 dialog and add 2x2 list in
1205 center of it, and let dialog function resize it to needed
1206 size. */
1207 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1209 if (query_dlg->y < widget->y) {
1210 /* draw list entries from bottom upto top */
1211 listbox_set_list (query_list, hlist);
1212 listbox_select_last (query_list);
1213 } else {
1214 /* draw list entries from top downto bottom */
1215 /* revert history direction */
1216 hlist = g_list_reverse (hlist);
1217 listbox_set_list (query_list, hlist);
1220 if (run_dlg (query_dlg) != B_CANCEL) {
1221 char *q;
1223 listbox_get_current (query_list, &q, NULL);
1224 if (q != NULL)
1225 r = g_strdup (q);
1228 /* get modified history from dialog */
1229 z = NULL;
1230 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi)) {
1231 WLEntry *entry;
1233 entry = (WLEntry *) hi->data;
1234 /* history is being reverted here again */
1235 z = g_list_prepend (z, entry->text);
1236 entry->text = NULL;
1239 destroy_dlg (query_dlg);
1241 /* restore history direction */
1242 if (query_dlg->y < widget->y)
1243 z = g_list_reverse (z);
1245 g_list_foreach (*history, (GFunc) g_free, NULL);
1246 g_list_free (*history);
1247 *history = g_list_last (z);
1249 return r;
1252 static void
1253 do_show_hist (WInput *in)
1255 char *r;
1257 r = show_hist (&in->history, &in->widget);
1258 if (r != NULL) {
1259 assign_text (in, r);
1260 g_free (r);
1264 /* }}} history display */
1266 static void
1267 input_destroy (WInput *in)
1269 if (!in){
1270 fprintf (stderr, "Internal error: null Input *\n");
1271 exit (1);
1274 new_input (in);
1276 if (in->history){
1277 if (!in->is_password) /* don't save passwords ;-) */
1278 history_put (in->history_name, in->history);
1280 in->history = g_list_first (in->history);
1281 g_list_foreach (in->history, (GFunc) g_free, NULL);
1282 g_list_free (in->history);
1285 g_free (in->buffer);
1286 free_completions (in);
1287 g_free (in->history_name);
1290 void
1291 input_disable_update (WInput *in)
1293 in->disable_update++;
1296 void
1297 input_enable_update (WInput *in)
1299 in->disable_update--;
1300 update_input (in, 0);
1304 static void
1305 push_history (WInput *in, const char *text)
1307 /* input widget where urls with passwords are entered without any
1308 vfs prefix */
1309 const char *password_input_fields[] = {
1310 N_(" Link to a remote machine "),
1311 N_(" FTP to machine "),
1312 N_(" SMB link to machine ")
1314 const size_t ELEMENTS = (sizeof (password_input_fields) /
1315 sizeof (password_input_fields[0]));
1317 char *t;
1318 size_t i;
1319 gboolean empty;
1321 if (text == NULL)
1322 return;
1324 #ifdef ENABLE_NLS
1325 for (i = 0; i < ELEMENTS; i++)
1326 password_input_fields[i] = _(password_input_fields[i]);
1327 #endif
1329 t = g_strstrip (g_strdup (text));
1330 empty = *t == '\0';
1331 g_free (t);
1332 t = g_strdup (empty ? "" : text);
1334 if (in->history_name != NULL) {
1335 const char *p = in->history_name + 3;
1337 for (i = 0; i < ELEMENTS; i++)
1338 if (strcmp (p, password_input_fields[i]) == 0)
1339 break;
1341 strip_password (t, i >= ELEMENTS);
1344 in->history = list_append_unique (in->history, t);
1345 in->need_push = 0;
1348 /* Cleans the input line and adds the current text to the history */
1349 void
1350 new_input (WInput *in)
1352 push_history (in, in->buffer);
1353 in->need_push = 1;
1354 in->buffer[0] = '\0';
1355 in->point = 0;
1356 in->charpoint = 0;
1357 in->mark = 0;
1358 free_completions (in);
1359 update_input (in, 0);
1362 static void
1363 move_buffer_backward (WInput *in, int start, int end)
1365 int i, pos, len;
1366 int str_len = str_length (in->buffer);
1367 if (start >= str_len || end > str_len + 1) return;
1369 pos = str_offset_to_pos (in->buffer, start);
1370 len = str_offset_to_pos (in->buffer, end) - pos;
1372 for (i = pos; in->buffer[i + len - 1]; i++)
1373 in->buffer[i] = in->buffer[i + len];
1376 static cb_ret_t
1377 insert_char (WInput *in, int c_code)
1379 size_t i;
1380 int res;
1382 if (c_code == -1)
1383 return MSG_NOT_HANDLED;
1385 if (in->charpoint >= MB_LEN_MAX)
1386 return MSG_HANDLED;
1388 in->charbuf[in->charpoint] = c_code;
1389 in->charpoint++;
1391 res = str_is_valid_char (in->charbuf, in->charpoint);
1392 if (res < 0) {
1393 if (res != -2)
1394 in->charpoint = 0; /* broken multibyte char, skip */
1395 return MSG_HANDLED;
1398 in->need_push = 1;
1399 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1400 /* Expand the buffer */
1401 size_t new_length = in->current_max_size +
1402 in->field_width + in->charpoint;
1403 char *narea = g_try_renew (char, in->buffer, new_length);
1404 if (narea){
1405 in->buffer = narea;
1406 in->current_max_size = new_length;
1410 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1411 /* bytes from begin */
1412 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1413 /* move chars */
1414 size_t rest_bytes = strlen (in->buffer + ins_point);
1416 for (i = rest_bytes + 1; i > 0; i--)
1417 in->buffer[ins_point + i + in->charpoint - 1] =
1418 in->buffer[ins_point + i - 1];
1420 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1421 in->point++;
1424 in->charpoint = 0;
1425 return MSG_HANDLED;
1428 static void
1429 beginning_of_line (WInput *in)
1431 in->point = 0;
1432 in->charpoint = 0;
1435 static void
1436 end_of_line (WInput *in)
1438 in->point = str_length (in->buffer);
1439 in->charpoint = 0;
1442 static void
1443 backward_char (WInput *in)
1445 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1447 if (in->point > 0) {
1448 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1450 in->charpoint = 0;
1453 static void
1454 forward_char (WInput *in)
1456 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1457 if (act[0] != '\0') {
1458 in->point+= str_cnext_noncomb_char (&act);
1460 in->charpoint = 0;
1463 static void
1464 forward_word (WInput * in)
1466 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1468 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1469 str_cnext_char (&p);
1470 in->point++;
1472 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1473 str_cnext_char (&p);
1474 in->point++;
1478 static void
1479 backward_word (WInput *in)
1481 const char *p;
1482 const char *p_tmp;
1484 for (
1485 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1486 (p != in->buffer) && (p[0] == '\0');
1487 str_cprev_char (&p), in->point--
1490 while (p != in->buffer) {
1491 p_tmp = p;
1492 str_cprev_char (&p);
1493 if (!str_isspace (p) && !str_ispunct (p)) {
1494 p = p_tmp;
1495 break;
1497 in->point--;
1499 while (p != in->buffer) {
1500 str_cprev_char (&p);
1501 if (str_isspace (p) || str_ispunct (p))
1502 break;
1504 in->point--;
1508 static void
1509 key_left (WInput *in)
1511 backward_char (in);
1514 static void
1515 key_ctrl_left (WInput *in)
1517 backward_word (in);
1520 static void
1521 key_right (WInput *in)
1523 forward_char (in);
1526 static void
1527 key_ctrl_right (WInput *in)
1529 forward_word (in);
1531 static void
1532 backward_delete (WInput *in)
1534 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1535 int start;
1537 if (in->point == 0)
1538 return;
1540 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1541 move_buffer_backward(in, start, in->point);
1542 in->charpoint = 0;
1543 in->need_push = 1;
1544 in->point = start;
1547 static void
1548 delete_char (WInput *in)
1550 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1551 int end = in->point;
1553 end+= str_cnext_noncomb_char (&act);
1555 move_buffer_backward(in, in->point, end);
1556 in->charpoint = 0;
1557 in->need_push = 1;
1560 static void
1561 copy_region (WInput *in, int x_first, int x_last)
1563 int first = min (x_first, x_last);
1564 int last = max (x_first, x_last);
1566 if (last == first) {
1567 /* Copy selected files to clipboard */
1568 panel_save_curent_file_to_clip_file ();
1569 return;
1572 g_free (kill_buffer);
1574 first = str_offset_to_pos (in->buffer, first);
1575 last = str_offset_to_pos (in->buffer, last);
1577 kill_buffer = g_strndup(in->buffer + first, last - first);
1578 save_text_to_clip_file (kill_buffer);
1581 static void
1582 delete_region (WInput *in, int x_first, int x_last)
1584 int first = min (x_first, x_last);
1585 int last = max (x_first, x_last);
1586 size_t len;
1588 in->point = first;
1589 if (in->mark > first)
1590 in->mark = first;
1591 last = str_offset_to_pos (in->buffer, last);
1592 first = str_offset_to_pos (in->buffer, first);
1593 len = strlen (&in->buffer[last]) + 1;
1594 memmove (&in->buffer[first], &in->buffer[last], len);
1595 in->charpoint = 0;
1596 in->need_push = 1;
1599 static void
1600 kill_word (WInput *in)
1602 int old_point = in->point;
1603 int new_point;
1605 forward_word (in);
1606 new_point = in->point;
1607 in->point = old_point;
1609 copy_region (in, old_point, new_point);
1610 delete_region (in, old_point, new_point);
1611 in->need_push = 1;
1612 in->charpoint = 0;
1613 in->charpoint = 0;
1616 static void
1617 back_kill_word (WInput *in)
1619 int old_point = in->point;
1620 int new_point;
1622 backward_word (in);
1623 new_point = in->point;
1624 in->point = old_point;
1626 copy_region (in, old_point, new_point);
1627 delete_region (in, old_point, new_point);
1628 in->need_push = 1;
1631 static void
1632 set_mark (WInput *in)
1634 in->mark = in->point;
1637 static void
1638 kill_save (WInput *in)
1640 copy_region (in, in->mark, in->point);
1643 static void
1644 kill_region (WInput *in)
1646 kill_save (in);
1647 delete_region (in, in->point, in->mark);
1650 static void
1651 clear_region (WInput *in)
1653 delete_region (in, in->point, in->mark);
1656 static void
1657 yank (WInput *in)
1659 char *p;
1661 if (!kill_buffer)
1662 return;
1663 in->charpoint = 0;
1664 for (p = kill_buffer; *p; p++)
1665 insert_char (in, *p);
1666 in->charpoint = 0;
1669 static void
1670 kill_line (WInput *in)
1672 int chp = str_offset_to_pos (in->buffer, in->point);
1673 g_free (kill_buffer);
1674 kill_buffer = g_strdup (&in->buffer[chp]);
1675 in->buffer[chp] = '\0';
1676 in->charpoint = 0;
1679 static void
1680 ins_from_clip (WInput *in)
1682 char *p = NULL;
1684 if (load_text_from_clip_file (&p)) {
1685 char *pp;
1687 for (pp = p; *pp != '\0'; pp++)
1688 insert_char (in, *pp);
1690 g_free (p);
1694 void
1695 assign_text (WInput *in, const char *text)
1697 free_completions (in);
1698 g_free (in->buffer);
1699 in->buffer = g_strdup (text); /* was in->buffer->text */
1700 in->current_max_size = strlen (in->buffer) + 1;
1701 in->point = str_length (in->buffer);
1702 in->mark = 0;
1703 in->need_push = 1;
1704 in->charpoint = 0;
1707 static void
1708 hist_prev (WInput *in)
1710 GList *prev;
1712 if (!in->history)
1713 return;
1715 if (in->need_push)
1716 push_history (in, in->buffer);
1718 prev = g_list_previous (in->history);
1719 if (prev != NULL) {
1720 in->history = prev;
1721 assign_text (in, (char *) prev->data);
1722 in->need_push = 0;
1726 static void
1727 hist_next (WInput *in)
1729 if (in->need_push) {
1730 push_history (in, in->buffer);
1731 assign_text (in, "");
1732 return;
1735 if (!in->history)
1736 return;
1738 if (!in->history->next) {
1739 assign_text (in, "");
1740 return;
1743 in->history = g_list_next (in->history);
1744 assign_text (in, (char *) in->history->data);
1745 in->need_push = 0;
1748 static void
1749 port_region_marked_for_delete (WInput *in)
1751 in->buffer[0] = '\0';
1752 in->point = 0;
1753 in->first = 0;
1754 in->charpoint = 0;
1757 static cb_ret_t
1758 input_execute_cmd (WInput *in, unsigned long command)
1760 cb_ret_t res = MSG_HANDLED;
1762 switch (command) {
1763 case CK_InputBol:
1764 beginning_of_line (in);
1765 break;
1766 case CK_InputEol:
1767 end_of_line (in);
1768 break;
1769 case CK_InputMoveLeft:
1770 key_left (in);
1771 break;
1772 case CK_InputWordLeft:
1773 key_ctrl_left (in);
1774 break;
1775 case CK_InputMoveRight:
1776 key_right (in);
1777 break;
1778 case CK_InputWordRight:
1779 key_ctrl_right (in);
1780 break;
1781 case CK_InputBackwardChar:
1782 backward_char (in);
1783 break;
1784 case CK_InputBackwardWord:
1785 backward_word (in);
1786 break;
1787 case CK_InputForwardChar:
1788 forward_char (in);
1789 break;
1790 case CK_InputForwardWord:
1791 forward_word (in);
1792 break;
1793 case CK_InputBackwardDelete:
1794 backward_delete (in);
1795 break;
1796 case CK_InputDeleteChar:
1797 delete_char (in);
1798 break;
1799 case CK_InputKillWord:
1800 kill_word (in);
1801 break;
1802 case CK_InputBackwardKillWord:
1803 back_kill_word (in);
1804 break;
1805 case CK_InputSetMark:
1806 set_mark (in);
1807 break;
1808 case CK_InputKillRegion:
1809 kill_region (in);
1810 break;
1811 case CK_InputClearLine:
1812 clear_region (in);
1813 break;
1814 case CK_InputKillSave:
1815 kill_save (in);
1816 break;
1817 case CK_InputYank:
1818 yank (in);
1819 break;
1820 case CK_InputPaste:
1821 ins_from_clip (in);
1822 break;
1823 case CK_InputKillLine:
1824 kill_line (in);
1825 break;
1826 case CK_InputHistoryPrev:
1827 hist_prev (in);
1828 break;
1829 case CK_InputHistoryNext:
1830 hist_next (in);
1831 break;
1832 case CK_InputHistoryShow:
1833 do_show_hist (in);
1834 break;
1835 case CK_InputComplete:
1836 complete (in);
1837 break;
1838 default:
1839 res = MSG_NOT_HANDLED;
1842 return res;
1845 /* This function is a test for a special input key used in complete.c */
1846 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1847 and 2 if it is a complete key */
1849 is_in_input_map (WInput *in, int key)
1851 size_t i;
1852 for (i = 0; input_map[i].key != 0; i++)
1853 if (key == input_map[i].key) {
1854 input_execute_cmd (in, input_map[i].command);
1855 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1857 return 0;
1860 cb_ret_t
1861 handle_char (WInput *in, int key)
1863 cb_ret_t v;
1864 int i;
1866 v = MSG_NOT_HANDLED;
1868 if (quote) {
1869 free_completions (in);
1870 v = insert_char (in, key);
1871 update_input (in, 1);
1872 quote = 0;
1873 return v;
1875 for (i = 0; input_map[i].key; i++) {
1876 if (key == input_map[i].key) {
1877 if (input_map[i].command != CK_InputComplete)
1878 free_completions (in);
1879 input_execute_cmd (in, input_map[i].command);
1880 update_input (in, 1);
1881 v = MSG_HANDLED;
1882 break;
1885 if (input_map[i].command == 0) {
1886 if (key > 255)
1887 return MSG_NOT_HANDLED;
1888 if (in->first)
1889 port_region_marked_for_delete (in);
1890 free_completions (in);
1891 v = insert_char (in, key);
1893 update_input (in, 1);
1894 return v;
1897 /* Inserts text in input line */
1898 void
1899 stuff (WInput *in, const char *text, int insert_extra_space)
1901 input_disable_update (in);
1902 while (*text != '\0')
1903 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1904 if (insert_extra_space)
1905 handle_char (in, ' ');
1906 input_enable_update (in);
1907 update_input (in, 1);
1910 void
1911 input_set_point (WInput *in, int pos)
1913 int max_pos = str_length (in->buffer);
1915 if (pos > max_pos)
1916 pos = max_pos;
1917 if (pos != in->point)
1918 free_completions (in);
1919 in->point = pos;
1920 in->charpoint = 0;
1921 update_input (in, 1);
1924 cb_ret_t
1925 input_callback (Widget *w, widget_msg_t msg, int parm)
1927 WInput *in = (WInput *) w;
1928 cb_ret_t v;
1930 switch (msg) {
1931 case WIDGET_KEY:
1932 if (parm == XCTRL ('q')) {
1933 quote = 1;
1934 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1935 quote = 0;
1936 return v;
1939 /* Keys we want others to handle */
1940 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1941 || parm == KEY_F (10) || parm == '\n')
1942 return MSG_NOT_HANDLED;
1944 /* When pasting multiline text, insert literal Enter */
1945 if ((parm & ~KEY_M_MASK) == '\n') {
1946 quote = 1;
1947 v = handle_char (in, '\n');
1948 quote = 0;
1949 return v;
1952 return handle_char (in, parm);
1954 case WIDGET_COMMAND:
1955 return input_execute_cmd (in, parm);
1957 case WIDGET_FOCUS:
1958 case WIDGET_UNFOCUS:
1959 case WIDGET_DRAW:
1960 update_input (in, 0);
1961 return MSG_HANDLED;
1963 case WIDGET_CURSOR:
1964 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1965 - in->term_first_shown);
1966 return MSG_HANDLED;
1968 case WIDGET_DESTROY:
1969 input_destroy (in);
1970 return MSG_HANDLED;
1972 default:
1973 return default_proc (msg, parm);
1977 static int
1978 input_event (Gpm_Event * event, void *data)
1980 WInput *in = data;
1982 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1983 dlg_select_widget (in);
1985 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1986 && should_show_history_button (in)) {
1987 do_show_hist (in);
1988 } else {
1989 in->point = str_length (in->buffer);
1990 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
1991 in->point = str_column_to_pos (in->buffer, event->x
1992 + in->term_first_shown - 1);
1994 update_input (in, 1);
1996 return MOU_NORMAL;
1999 WInput *
2000 input_new (int y, int x, int color, int width, const char *def_text,
2001 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2003 WInput *in = g_new (WInput, 1);
2004 size_t initial_buffer_len;
2006 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2008 /* history setup */
2009 in->history_name = NULL;
2010 in->history = NULL;
2011 if ((histname != NULL) && (*histname != '\0')) {
2012 in->history_name = g_strdup (histname);
2013 in->history = history_get (histname);
2016 if (def_text == NULL)
2017 def_text = "";
2018 else if (def_text == INPUT_LAST_TEXT) {
2019 if ((in->history != NULL) && (in->history->data != NULL))
2020 def_text = (char *) in->history->data;
2021 else
2022 def_text = "";
2025 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2026 in->widget.options |= W_IS_INPUT;
2027 in->completions = NULL;
2028 in->completion_flags = completion_flags;
2029 in->current_max_size = initial_buffer_len;
2030 in->buffer = g_new (char, initial_buffer_len);
2031 in->color = color;
2032 in->field_width = width;
2033 in->first = 1;
2034 in->term_first_shown = 0;
2035 in->disable_update = 0;
2036 in->mark = 0;
2037 in->need_push = 1;
2038 in->is_password = 0;
2040 strcpy (in->buffer, def_text);
2041 in->point = str_length (in->buffer);
2042 in->charpoint = 0;
2044 return in;
2048 /* Listbox widget */
2050 /* Should draw the scrollbar, but currently draws only
2051 * indications that there is more information
2054 static void
2055 listbox_entry_free (void *data)
2057 WLEntry *e = data;
2058 g_free (e->text);
2059 g_free (e);
2062 static void
2063 listbox_drawscroll (WListbox *l)
2065 const int max_line = l->widget.lines - 1;
2066 int line = 0;
2067 int i;
2069 /* Are we at the top? */
2070 widget_move (&l->widget, 0, l->widget.cols);
2071 if (l->top == 0)
2072 tty_print_one_vline ();
2073 else
2074 tty_print_char ('^');
2076 /* Are we at the bottom? */
2077 widget_move (&l->widget, max_line, l->widget.cols);
2078 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2079 tty_print_one_vline ();
2080 else
2081 tty_print_char ('v');
2083 /* Now draw the nice relative pointer */
2084 if (l->count != 0)
2085 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2087 for (i = 1; i < max_line; i++) {
2088 widget_move (&l->widget, i, l->widget.cols);
2089 if (i != line)
2090 tty_print_one_vline ();
2091 else
2092 tty_print_char ('*');
2096 static void
2097 listbox_draw (WListbox *l, gboolean focused)
2099 const Dlg_head *h = l->widget.parent;
2100 const int normalc = DLG_NORMALC (h);
2101 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2103 GList *le;
2104 int pos;
2105 int i;
2106 int sel_line = -1;
2108 le = g_list_nth (l->list, l->top);
2109 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2110 pos = (le == NULL) ? 0 : l->top;
2112 for (i = 0; i < l->widget.lines; i++) {
2113 const char *text;
2115 /* Display the entry */
2116 if (pos == l->pos && sel_line == -1) {
2117 sel_line = i;
2118 tty_setcolor (selc);
2119 } else
2120 tty_setcolor (normalc);
2122 widget_move (&l->widget, i, 1);
2124 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2125 text = "";
2126 else {
2127 WLEntry *e = (WLEntry *) le->data;
2128 text = e->text;
2129 le = g_list_next (le);
2130 pos++;
2133 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2136 l->cursor_y = sel_line;
2138 if (l->scrollbar && (l->count > l->widget.lines)) {
2139 tty_setcolor (normalc);
2140 listbox_drawscroll (l);
2144 static int
2145 listbox_check_hotkey (WListbox *l, int key)
2147 int i;
2148 GList *le;
2150 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2151 WLEntry *e = (WLEntry *) le->data;
2153 if (e->hotkey == key)
2154 return i;
2157 return (-1);
2160 /* Selects the last entry and scrolls the list to the bottom */
2161 void
2162 listbox_select_last (WListbox *l)
2164 l->pos = l->count - 1;
2165 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2168 /* Selects the first entry and scrolls the list to the top */
2169 void
2170 listbox_select_first (WListbox *l)
2172 l->pos = l->top = 0;
2175 void
2176 listbox_set_list (WListbox *l, GList *list)
2178 listbox_remove_list (l);
2180 if (l != NULL) {
2181 l->list = list;
2182 l->top = l->pos = 0;
2183 l->count = g_list_length (list);
2187 void
2188 listbox_remove_list (WListbox *l)
2190 if ((l != NULL) && (l->count != 0)) {
2191 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2192 g_list_free (l->list);
2193 l->list = NULL;
2194 l->count = l->pos = l->top = 0;
2198 void
2199 listbox_remove_current (WListbox *l)
2201 if ((l != NULL) && (l->count != 0)) {
2202 GList *current;
2204 current = g_list_nth (l->list, l->pos);
2205 l->list = g_list_remove_link (l->list, current);
2206 listbox_entry_free ((WLEntry *) current->data);
2207 g_list_free_1 (current);
2208 l->count--;
2210 if (l->count == 0)
2211 l->top = l->pos = 0;
2212 else if (l->pos >= l->count)
2213 l->pos = l->count - 1;
2217 void
2218 listbox_select_entry (WListbox *l, int dest)
2220 GList *le;
2221 int pos;
2222 gboolean top_seen = FALSE;
2224 if (dest < 0)
2225 return;
2227 /* Special case */
2228 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le)) {
2229 if (pos == l->top)
2230 top_seen = TRUE;
2232 if (pos == dest) {
2233 l->pos = dest;
2234 if (!top_seen)
2235 l->top = l->pos;
2236 else
2237 if (l->pos - l->top >= l->widget.lines)
2238 l->top = l->pos - l->widget.lines + 1;
2239 return;
2243 /* If we are unable to find it, set decent values */
2244 l->pos = l->top = 0;
2247 /* Selects from base the pos element */
2248 static int
2249 listbox_select_pos (WListbox *l, int base, int pos)
2251 int last = l->count - 1;
2253 base += pos;
2254 if (base >= last)
2255 base = last;
2257 return base;
2260 static void
2261 listbox_fwd (WListbox *l)
2263 if (l->pos + 1 >= l->count)
2264 listbox_select_first (l);
2265 else
2266 listbox_select_entry (l, l->pos + 1);
2269 static void
2270 listbox_back (WListbox *l)
2272 if (l->pos <= 0)
2273 listbox_select_last (l);
2274 else
2275 listbox_select_entry (l, l->pos - 1);
2278 /* Return MSG_HANDLED if we want a redraw */
2279 static cb_ret_t
2280 listbox_key (WListbox *l, int key)
2282 int i;
2284 cb_ret_t j = MSG_NOT_HANDLED;
2286 /* focus on listbox item N by '0'..'9' keys */
2287 if (key >= '0' && key <= '9') {
2288 int oldpos = l->pos;
2289 listbox_select_entry (l, key - '0');
2291 /* need scroll to item? */
2292 if (abs (oldpos - l->pos) > l->widget.lines)
2293 l->top = l->pos;
2295 return MSG_HANDLED;
2298 if (l->list == NULL)
2299 return MSG_NOT_HANDLED;
2301 switch (key){
2302 case KEY_HOME:
2303 case KEY_A1:
2304 case ALT ('<'):
2305 listbox_select_first (l);
2306 return MSG_HANDLED;
2308 case KEY_END:
2309 case KEY_C1:
2310 case ALT ('>'):
2311 listbox_select_last (l);
2312 return MSG_HANDLED;
2314 case XCTRL('p'):
2315 case KEY_UP:
2316 listbox_back (l);
2317 return MSG_HANDLED;
2319 case XCTRL('n'):
2320 case KEY_DOWN:
2321 listbox_fwd (l);
2322 return MSG_HANDLED;
2324 case KEY_NPAGE:
2325 case XCTRL('v'):
2326 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++) {
2327 listbox_fwd (l);
2328 j = MSG_HANDLED;
2330 return j;
2332 case KEY_PPAGE:
2333 case ALT('v'):
2334 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++) {
2335 listbox_back (l);
2336 j = MSG_HANDLED;
2338 return j;
2340 case KEY_DC:
2341 if (l->deletable) {
2342 gboolean is_last = (l->pos + 1 == l->count);
2344 listbox_remove_current (l);
2345 if (is_last && (l->top > 0))
2346 l->top--;
2348 return MSG_HANDLED;
2350 default:
2351 break;
2354 return MSG_NOT_HANDLED;
2357 static inline void
2358 listbox_destroy (WListbox *l)
2360 /* don't delete list in modifable listbox */
2361 if (!l->deletable)
2362 listbox_remove_list (l);
2365 static cb_ret_t
2366 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2368 WListbox *l = (WListbox *) w;
2369 Dlg_head *h = l->widget.parent;
2370 cb_ret_t ret_code;
2372 switch (msg) {
2373 case WIDGET_INIT:
2374 return MSG_HANDLED;
2376 case WIDGET_HOTKEY:
2378 int pos, action;
2380 pos = listbox_check_hotkey (l, parm);
2381 if (pos < 0)
2382 return MSG_NOT_HANDLED;
2384 listbox_select_entry (l, pos);
2385 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2387 if (l->cback != NULL)
2388 action = l->cback (l);
2389 else
2390 action = LISTBOX_DONE;
2392 if (action == LISTBOX_DONE) {
2393 h->ret_value = B_ENTER;
2394 dlg_stop (h);
2397 return MSG_HANDLED;
2400 case WIDGET_KEY:
2401 ret_code = listbox_key (l, parm);
2402 if (ret_code != MSG_NOT_HANDLED) {
2403 listbox_draw (l, TRUE);
2404 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2406 return ret_code;
2408 case WIDGET_CURSOR:
2409 widget_move (&l->widget, l->cursor_y, 0);
2410 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2411 return MSG_HANDLED;
2413 case WIDGET_FOCUS:
2414 case WIDGET_UNFOCUS:
2415 case WIDGET_DRAW:
2416 listbox_draw (l, msg != WIDGET_UNFOCUS);
2417 return MSG_HANDLED;
2419 case WIDGET_DESTROY:
2420 listbox_destroy (l);
2421 return MSG_HANDLED;
2423 case WIDGET_RESIZED:
2424 return MSG_HANDLED;
2426 default:
2427 return default_proc (msg, parm);
2431 static int
2432 listbox_event (Gpm_Event *event, void *data)
2434 WListbox *l = data;
2435 int i;
2437 Dlg_head *h = l->widget.parent;
2439 /* Single click */
2440 if (event->type & GPM_DOWN)
2441 dlg_select_widget (l);
2443 if (l->list == NULL)
2444 return MOU_NORMAL;
2446 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2447 int ret = MOU_REPEAT;
2449 if (event->x < 0 || event->x > l->widget.cols)
2450 return ret;
2452 if (event->y < 1)
2453 for (i = -event->y; i >= 0; i--)
2454 listbox_back (l);
2455 else if (event->y > l->widget.lines)
2456 for (i = event->y - l->widget.lines; i > 0; i--)
2457 listbox_fwd (l);
2458 else if (event->buttons & GPM_B_UP) {
2459 listbox_back (l);
2460 ret = MOU_NORMAL;
2461 } else if (event->buttons & GPM_B_DOWN) {
2462 listbox_fwd (l);
2463 ret = MOU_NORMAL;
2464 } else
2465 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2467 /* We need to refresh ourselves since the dialog manager doesn't */
2468 /* know about this event */
2469 listbox_draw (l, TRUE);
2470 return ret;
2473 /* Double click */
2474 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2475 int action;
2477 if (event->x < 0 || event->x >= l->widget.cols
2478 || event->y < 1 || event->y > l->widget.lines)
2479 return MOU_NORMAL;
2481 dlg_select_widget (l);
2482 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2484 if (l->cback != NULL)
2485 action = l->cback (l);
2486 else
2487 action = LISTBOX_DONE;
2489 if (action == LISTBOX_DONE) {
2490 h->ret_value = B_ENTER;
2491 dlg_stop (h);
2492 return MOU_NORMAL;
2495 return MOU_NORMAL;
2498 WListbox *
2499 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2501 WListbox *l = g_new (WListbox, 1);
2503 if (height <= 0)
2504 height = 1;
2506 init_widget (&l->widget, y, x, height, width,
2507 listbox_callback, listbox_event);
2509 l->list = NULL;
2510 l->top = l->pos = 0;
2511 l->count = 0;
2512 l->deletable = deletable;
2513 l->cback = callback;
2514 l->allow_duplicates = TRUE;
2515 l->scrollbar = !tty_is_slow ();
2516 widget_want_hotkey (l->widget, 1);
2518 return l;
2521 static int
2522 listbox_entry_cmp (const void *a, const void *b)
2524 const WLEntry *ea = (const WLEntry *) a;
2525 const WLEntry *eb = (const WLEntry *) b;
2527 return strcmp (ea->text, eb->text);
2530 /* Listbox item adding function */
2531 static inline void
2532 listbox_append_item (WListbox *l, WLEntry *e, listbox_append_t pos)
2534 switch (pos) {
2535 case LISTBOX_APPEND_AT_END:
2536 l->list = g_list_append (l->list, e);
2537 break;
2539 case LISTBOX_APPEND_BEFORE:
2540 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2541 if (l->pos > 0)
2542 l->pos--;
2543 break;
2545 case LISTBOX_APPEND_AFTER:
2546 l->list = g_list_insert (l->list, e, l->pos + 1);
2547 break;
2549 case LISTBOX_APPEND_SORTED:
2550 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2551 break;
2553 default:
2554 return;
2557 l->count++;
2560 char *
2561 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey,
2562 const char *text, void *data)
2564 WLEntry *entry;
2566 if (l == NULL)
2567 return NULL;
2569 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2570 return NULL;
2572 entry = g_new (WLEntry, 1);
2573 entry->text = g_strdup (text);
2574 entry->data = data;
2575 entry->hotkey = hotkey;
2577 listbox_append_item (l, entry, pos);
2579 return entry->text;
2583 listbox_search_text (WListbox *l, const char *text)
2585 if (l != NULL) {
2586 int i;
2587 GList *le;
2589 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2590 WLEntry *e = (WLEntry *) le->data;
2592 if (strcmp (e->text, text) == 0)
2593 return i;
2597 return (-1);
2600 /* Returns the current string text as well as the associated extra data */
2601 void
2602 listbox_get_current (WListbox *l, char **string, void **extra)
2604 WLEntry *e = NULL;
2605 gboolean ok;
2607 if (l != NULL)
2608 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2610 ok = (e != NULL);
2612 if (string != NULL)
2613 *string = ok ? e->text : NULL;
2615 if (extra != NULL)
2616 *extra = ok ? e->data : NULL;
2620 /* ButtonBar widget */
2622 /* returns TRUE if a function has been called, FALSE otherwise. */
2623 static gboolean
2624 buttonbar_call (WButtonBar *bb, int i)
2626 cb_ret_t ret = MSG_NOT_HANDLED;
2628 if (bb != NULL)
2629 ret = bb->widget.parent->callback (bb->widget.parent,
2630 (Widget *) bb, DLG_ACTION,
2631 bb->labels[i].command,
2632 bb->labels[i].receiver);
2633 return ret;
2636 /* calculate width of one button, width is never lesser than 7 */
2637 static int
2638 buttonbat_get_button_width (void)
2640 int result = COLS / BUTTONBAR_LABELS_NUM;
2641 return (result >= 7) ? result : 7;
2644 static cb_ret_t
2645 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2647 WButtonBar *bb = (WButtonBar *) w;
2648 int i;
2649 const char *text;
2651 switch (msg) {
2652 case WIDGET_FOCUS:
2653 return MSG_NOT_HANDLED;
2655 case WIDGET_HOTKEY:
2656 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2657 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2658 return MSG_HANDLED;
2659 return MSG_NOT_HANDLED;
2661 case WIDGET_DRAW:
2662 if (bb->visible) {
2663 int offset = 0;
2664 int count_free_positions;
2666 widget_move (&bb->widget, 0, 0);
2667 tty_setcolor (DEFAULT_COLOR);
2668 bb->btn_width = buttonbat_get_button_width ();
2669 tty_printf ("%-*s", bb->widget.cols, "");
2670 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2672 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2673 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2674 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2675 tty_printf ("%2d", i + 1);
2676 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2677 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2678 tty_print_string (str_fit_to_term (
2679 text,
2680 bb->btn_width - 2 + (int)(offset < count_free_positions),
2681 J_LEFT_FIT));
2683 if (count_free_positions != 0 && offset < count_free_positions)
2684 offset++;
2687 return MSG_HANDLED;
2689 case WIDGET_DESTROY:
2690 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2691 g_free (bb->labels[i].text);
2692 return MSG_HANDLED;
2694 default:
2695 return default_proc (msg, parm);
2699 static int
2700 buttonbar_event (Gpm_Event *event, void *data)
2702 WButtonBar *bb = data;
2703 int button;
2705 if (!(event->type & GPM_UP))
2706 return MOU_NORMAL;
2707 if (event->y == 2)
2708 return MOU_NORMAL;
2709 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2710 if (button < BUTTONBAR_LABELS_NUM)
2711 buttonbar_call (bb, button);
2712 return MOU_NORMAL;
2715 WButtonBar *
2716 buttonbar_new (gboolean visible)
2718 WButtonBar *bb;
2720 bb = g_new0 (WButtonBar, 1);
2722 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2723 buttonbar_callback, buttonbar_event);
2724 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2725 bb->visible = visible;
2726 widget_want_hotkey (bb->widget, 1);
2727 widget_want_cursor (bb->widget, 0);
2728 bb->btn_width = buttonbat_get_button_width ();
2730 return bb;
2733 static void
2734 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2736 g_free (bb->labels[lc_index - 1].text);
2737 bb->labels[lc_index - 1].text = g_strdup (text);
2740 /* Find ButtonBar widget in the dialog */
2741 WButtonBar *
2742 find_buttonbar (const Dlg_head *h)
2744 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2747 void
2748 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2749 const struct global_keymap_t *keymap, const Widget *receiver)
2751 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2752 unsigned long command = CK_Ignore_Key;
2754 if (keymap != NULL)
2755 command = lookup_keymap_command (keymap, KEY_F (idx));
2757 if ((text == NULL) || (text[0] == '\0'))
2758 set_label_text (bb, idx, "");
2759 else
2760 set_label_text (bb, idx, text);
2762 bb->labels[idx - 1].command = command;
2763 bb->labels[idx - 1].receiver = (Widget *) receiver;
2767 void
2768 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2770 bb->visible = visible;
2773 void
2774 buttonbar_redraw (WButtonBar *bb)
2776 if (bb != NULL)
2777 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2780 static cb_ret_t
2781 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2783 WGroupbox *g = (WGroupbox *) w;
2785 switch (msg) {
2786 case WIDGET_INIT:
2787 return MSG_HANDLED;
2789 case WIDGET_FOCUS:
2790 return MSG_NOT_HANDLED;
2792 case WIDGET_DRAW:
2793 tty_setcolor (COLOR_NORMAL);
2794 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2795 g->widget.x - g->widget.parent->x, g->widget.lines,
2796 g->widget.cols);
2798 tty_setcolor (COLOR_HOT_NORMAL);
2799 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2800 g->widget.x - g->widget.parent->x + 1);
2801 tty_print_string (g->title);
2802 return MSG_HANDLED;
2804 case WIDGET_DESTROY:
2805 g_free (g->title);
2806 return MSG_HANDLED;
2808 default:
2809 return default_proc (msg, parm);
2813 WGroupbox *
2814 groupbox_new (int y, int x, int height, int width, const char *title)
2816 WGroupbox *g = g_new (WGroupbox, 1);
2818 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2820 g->widget.options &= ~W_WANT_CURSOR;
2821 widget_want_hotkey (g->widget, 0);
2823 /* Strip existing spaces, add one space before and after the title */
2824 if (title) {
2825 char *t;
2826 t = g_strstrip (g_strdup (title));
2827 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2828 g_free (t);
2831 return g;