Ticket #1920: Issues with deba and debd VFS
[midnight-commander.git] / src / widget.c
blob9257fadc230509901f1f9acbf008a32d06674ea2
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 */
49 #include "../src/mcconfig/mcconfig.h" /* for history loading and saving */
51 #include "../vfs/vfs.h"
53 #include "dialog.h"
54 #include "widget.h"
55 #include "wtools.h"
56 #include "strutil.h"
58 #include "cmddef.h" /* CK_ cmd name const */
59 #include "keybind.h" /* global_keymap_t */
60 #include "fileloc.h"
61 #include "panel.h" /* current_panel */
63 const global_keymap_t *input_map;
65 static void
66 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
68 Dlg_head *h = w->parent;
70 tty_setcolor (hotkey
71 ? (focused
72 ? DLG_HOT_FOCUSC (h)
73 : DLG_HOT_NORMALC (h))
74 : (focused
75 ? DLG_FOCUSC (h)
76 : DLG_NORMALC (h)));
79 struct hotkey_t
80 parse_hotkey (const char *text)
82 struct hotkey_t result;
83 const char *cp, *p;
85 /* search for '&', that is not on the of text */
86 cp = strchr (text, '&');
87 if (cp != NULL && cp[1] != '\0') {
88 result.start = g_strndup (text, cp - text);
90 /* skip '&' */
91 cp++;
92 p = str_cget_next_char (cp);
93 result.hotkey = g_strndup (cp, p - cp);
95 cp = p;
96 result.end = g_strdup (cp);
97 } else {
98 result.start = g_strdup (text);
99 result.hotkey = NULL;
100 result.end = NULL;
103 return result;
105 void
106 release_hotkey (const struct hotkey_t hotkey)
108 g_free (hotkey.start);
109 g_free (hotkey.hotkey);
110 g_free (hotkey.end);
114 hotkey_width (const struct hotkey_t hotkey)
116 int result;
118 result = str_term_width1 (hotkey.start);
119 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
120 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
121 return result;
124 static void
125 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
127 widget_selectcolor (w, focused, FALSE);
128 tty_print_string (hotkey.start);
130 if (hotkey.hotkey != NULL) {
131 widget_selectcolor (w, focused, TRUE);
132 tty_print_string (hotkey.hotkey);
133 widget_selectcolor (w, focused, FALSE);
136 if (hotkey.end != NULL)
137 tty_print_string (hotkey.end);
141 /* Default callback for widgets */
142 cb_ret_t
143 default_proc (widget_msg_t msg, int parm)
145 (void) parm;
147 switch (msg) {
148 case WIDGET_INIT:
149 case WIDGET_FOCUS:
150 case WIDGET_UNFOCUS:
151 case WIDGET_DRAW:
152 case WIDGET_DESTROY:
153 case WIDGET_CURSOR:
154 case WIDGET_IDLE:
155 return MSG_HANDLED;
157 default:
158 return MSG_NOT_HANDLED;
162 static int button_event (Gpm_Event *event, void *);
164 int quote = 0;
166 static cb_ret_t
167 button_callback (Widget *w, widget_msg_t msg, int parm)
169 WButton *b = (WButton *) w;
170 int stop = 0;
171 int off = 0;
172 Dlg_head *h = b->widget.parent;
174 switch (msg) {
175 case WIDGET_HOTKEY:
177 * Don't let the default button steal Enter from the current
178 * button. This is a workaround for the flawed event model
179 * when hotkeys are sent to all widgets before the key is
180 * handled by the current widget.
182 if (parm == '\n' && h->current == &b->widget) {
183 button_callback (w, WIDGET_KEY, ' ');
184 return MSG_HANDLED;
187 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
188 button_callback (w, WIDGET_KEY, ' ');
189 return MSG_HANDLED;
192 if (b->text.hotkey != NULL) {
193 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
194 g_ascii_tolower ((gchar)parm)) {
195 button_callback (w, WIDGET_KEY, ' ');
196 return MSG_HANDLED;
199 return MSG_NOT_HANDLED;
201 case WIDGET_KEY:
202 if (parm != ' ' && parm != '\n')
203 return MSG_NOT_HANDLED;
205 if (b->callback)
206 stop = (*b->callback) (b->action);
207 if (!b->callback || stop) {
208 h->ret_value = b->action;
209 dlg_stop (h);
211 return MSG_HANDLED;
213 case WIDGET_CURSOR:
214 switch (b->flags) {
215 case DEFPUSH_BUTTON:
216 off = 3;
217 break;
218 case NORMAL_BUTTON:
219 off = 2;
220 break;
221 case NARROW_BUTTON:
222 off = 1;
223 break;
224 case HIDDEN_BUTTON:
225 default:
226 off = 0;
227 break;
229 widget_move (&b->widget, 0, b->hotpos + off);
230 return MSG_HANDLED;
232 case WIDGET_UNFOCUS:
233 case WIDGET_FOCUS:
234 case WIDGET_DRAW:
235 if (msg == WIDGET_UNFOCUS)
236 b->selected = 0;
237 else if (msg == WIDGET_FOCUS)
238 b->selected = 1;
240 widget_selectcolor (w, b->selected, FALSE);
241 widget_move (w, 0, 0);
243 switch (b->flags) {
244 case DEFPUSH_BUTTON:
245 tty_print_string ("[< ");
246 break;
247 case NORMAL_BUTTON:
248 tty_print_string ("[ ");
249 break;
250 case NARROW_BUTTON:
251 tty_print_string ("[");
252 break;
253 case HIDDEN_BUTTON:
254 default:
255 return MSG_HANDLED;
258 draw_hotkey (w, b->text, b->selected);
260 switch (b->flags) {
261 case DEFPUSH_BUTTON:
262 tty_print_string (" >]");
263 break;
264 case NORMAL_BUTTON:
265 tty_print_string (" ]");
266 break;
267 case NARROW_BUTTON:
268 tty_print_string ("]");
269 break;
271 return MSG_HANDLED;
273 case WIDGET_DESTROY:
274 release_hotkey (b->text);
275 return MSG_HANDLED;
277 default:
278 return default_proc (msg, parm);
282 static int
283 button_event (Gpm_Event *event, void *data)
285 WButton *b = data;
287 if (event->type & (GPM_DOWN|GPM_UP)){
288 Dlg_head *h = b->widget.parent;
289 dlg_select_widget (b);
290 if (event->type & GPM_UP){
291 button_callback ((Widget *) data, WIDGET_KEY, ' ');
292 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
293 return MOU_NORMAL;
296 return MOU_NORMAL;
300 button_get_len (const WButton *b)
302 int ret = hotkey_width (b->text);
303 switch (b->flags) {
304 case DEFPUSH_BUTTON:
305 ret += 6;
306 break;
307 case NORMAL_BUTTON:
308 ret += 4;
309 break;
310 case NARROW_BUTTON:
311 ret += 2;
312 break;
313 case HIDDEN_BUTTON:
314 default:
315 return 0;
317 return ret;
320 WButton *
321 button_new (int y, int x, int action, int flags, const char *text,
322 bcback callback)
324 WButton *b = g_new (WButton, 1);
326 b->action = action;
327 b->flags = flags;
328 b->text = parse_hotkey (text);
330 init_widget (&b->widget, y, x, 1, button_get_len (b),
331 button_callback, button_event);
333 b->selected = 0;
334 b->callback = callback;
335 widget_want_hotkey (b->widget, 1);
336 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
338 return b;
341 const char *
342 button_get_text (const WButton *b)
344 if (b->text.hotkey != NULL)
345 return g_strconcat (b->text.start, "&", b->text.hotkey,
346 b->text.end, (char *) NULL);
347 else
348 return g_strdup (b->text.start);
351 void
352 button_set_text (WButton *b, const char *text)
354 release_hotkey (b->text);
355 b->text = parse_hotkey (text);
356 b->widget.cols = button_get_len (b);
357 dlg_redraw (b->widget.parent);
361 /* Radio button widget */
362 static int radio_event (Gpm_Event *event, void *);
364 static cb_ret_t
365 radio_callback (Widget *w, widget_msg_t msg, int parm)
367 WRadio *r = (WRadio *) w;
368 int i;
369 Dlg_head *h = r->widget.parent;
371 switch (msg) {
372 case WIDGET_HOTKEY:
374 int lp = g_ascii_tolower ((gchar)parm);
376 for (i = 0; i < r->count; i++) {
377 if (r->texts[i].hotkey != NULL) {
378 int c = g_ascii_tolower ((gchar)r->texts[i].hotkey[0]);
380 if (c != lp)
381 continue;
382 r->pos = i;
384 /* Take action */
385 radio_callback (w, WIDGET_KEY, ' ');
386 return MSG_HANDLED;
390 return MSG_NOT_HANDLED;
392 case WIDGET_KEY:
393 switch (parm) {
394 case ' ':
395 r->sel = r->pos;
396 h->callback (h, w, DLG_ACTION, 0, NULL);
397 radio_callback (w, WIDGET_FOCUS, ' ');
398 return MSG_HANDLED;
400 case KEY_UP:
401 case KEY_LEFT:
402 if (r->pos > 0) {
403 r->pos--;
404 return MSG_HANDLED;
406 return MSG_NOT_HANDLED;
408 case KEY_DOWN:
409 case KEY_RIGHT:
410 if (r->count - 1 > r->pos) {
411 r->pos++;
412 return MSG_HANDLED;
415 return MSG_NOT_HANDLED;
417 case WIDGET_CURSOR:
418 h->callback (h, w, DLG_ACTION, 0, NULL);
419 radio_callback (w, WIDGET_FOCUS, ' ');
420 widget_move (&r->widget, r->pos, 1);
421 return MSG_HANDLED;
423 case WIDGET_UNFOCUS:
424 case WIDGET_FOCUS:
425 case WIDGET_DRAW:
426 for (i = 0; i < r->count; i++) {
427 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
428 widget_selectcolor (w, focused, FALSE);
429 widget_move (&r->widget, i, 0);
430 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
431 draw_hotkey (w, r->texts[i], focused);
433 return MSG_HANDLED;
435 case WIDGET_DESTROY:
436 for (i = 0; i < r->count; i++) {
437 release_hotkey (r->texts[i]);
439 g_free (r->texts);
440 return MSG_HANDLED;
442 default:
443 return default_proc (msg, parm);
447 static int
448 radio_event (Gpm_Event *event, void *data)
450 WRadio *r = data;
451 Widget *w = data;
453 if (event->type & (GPM_DOWN|GPM_UP)){
454 Dlg_head *h = r->widget.parent;
456 r->pos = event->y - 1;
457 dlg_select_widget (r);
458 if (event->type & GPM_UP){
459 radio_callback (w, WIDGET_KEY, ' ');
460 radio_callback (w, WIDGET_FOCUS, 0);
461 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
462 return MOU_NORMAL;
465 return MOU_NORMAL;
468 WRadio *
469 radio_new (int y, int x, int count, const char **texts)
471 WRadio *result = g_new (WRadio, 1);
472 int i, max, m;
474 /* Compute the longest string */
475 result->texts = g_new (struct hotkey_t, count);
477 max = 0;
478 for (i = 0; i < count; i++){
479 result->texts[i] = parse_hotkey (texts[i]);
480 m = hotkey_width (result->texts[i]);
481 if (m > max)
482 max = m;
485 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
486 result->state = 1;
487 result->pos = 0;
488 result->sel = 0;
489 result->count = count;
490 widget_want_hotkey (result->widget, 1);
492 return result;
496 /* Checkbutton widget */
498 static int check_event (Gpm_Event *event, void *);
500 static cb_ret_t
501 check_callback (Widget *w, widget_msg_t msg, int parm)
503 WCheck *c = (WCheck *) w;
504 Dlg_head *h = c->widget.parent;
506 switch (msg) {
507 case WIDGET_HOTKEY:
508 if (c->text.hotkey != NULL) {
509 if (g_ascii_tolower ((gchar)c->text.hotkey[0]) ==
510 g_ascii_tolower ((gchar)parm)) {
512 check_callback (w, WIDGET_KEY, ' '); /* make action */
513 return MSG_HANDLED;
516 return MSG_NOT_HANDLED;
518 case WIDGET_KEY:
519 if (parm != ' ')
520 return MSG_NOT_HANDLED;
521 c->state ^= C_BOOL;
522 c->state ^= C_CHANGE;
523 h->callback (h, w, DLG_ACTION, 0, NULL);
524 check_callback (w, WIDGET_FOCUS, ' ');
525 return MSG_HANDLED;
527 case WIDGET_CURSOR:
528 widget_move (&c->widget, 0, 1);
529 return MSG_HANDLED;
531 case WIDGET_FOCUS:
532 case WIDGET_UNFOCUS:
533 case WIDGET_DRAW:
534 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
535 widget_move (&c->widget, 0, 0);
536 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
537 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
538 return MSG_HANDLED;
540 case WIDGET_DESTROY:
541 release_hotkey (c->text);
542 return MSG_HANDLED;
544 default:
545 return default_proc (msg, parm);
549 static int
550 check_event (Gpm_Event *event, void *data)
552 WCheck *c = data;
553 Widget *w = data;
555 if (event->type & (GPM_DOWN|GPM_UP)){
556 Dlg_head *h = c->widget.parent;
558 dlg_select_widget (c);
559 if (event->type & GPM_UP){
560 check_callback (w, WIDGET_KEY, ' ');
561 check_callback (w, WIDGET_FOCUS, 0);
562 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
563 return MOU_NORMAL;
566 return MOU_NORMAL;
569 WCheck *
570 check_new (int y, int x, int state, const char *text)
572 WCheck *c = g_new (WCheck, 1);
574 c->text = parse_hotkey (text);
576 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
577 check_callback, check_event);
578 c->state = state ? C_BOOL : 0;
579 widget_want_hotkey (c->widget, 1);
581 return c;
584 static gboolean
585 save_text_to_clip_file (const char *text)
587 int file;
588 char *fname = NULL;
590 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
591 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
592 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
593 g_free (fname);
595 if (file == -1)
596 return FALSE;
598 mc_write (file, (char *) text, strlen (text));
599 mc_close (file);
600 return TRUE;
603 static gboolean
604 load_text_from_clip_file (char **text)
606 char buf[BUF_LARGE];
607 FILE *f;
608 char *fname = NULL;
609 gboolean first = TRUE;
611 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
612 f = fopen (fname, "r");
613 g_free (fname);
615 if (f == NULL)
616 return FALSE;
618 *text = NULL;
620 while (fgets (buf, sizeof (buf), f)) {
621 size_t len;
623 len = strlen (buf);
624 if ( len > 0 ) {
625 if (buf[len - 1] == '\n')
626 buf[len - 1] = '\0';
628 if (first) {
629 first = FALSE;
630 *text = g_strdup (buf);
631 } else {
632 /* remove \n on EOL */
633 char *tmp;
635 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
636 g_free (*text);
637 *text = tmp;
642 fclose (f);
644 return (*text != NULL);
647 static gboolean
648 panel_save_curent_file_to_clip_file (void)
650 gboolean res;
652 if (current_panel->marked == 0)
653 res = save_text_to_clip_file (selection (current_panel)->fname);
654 else {
655 int i;
656 gboolean first = TRUE;
657 char *flist = NULL;
659 for (i = 0; i < current_panel->count; i++)
660 if (current_panel->dir.list[i].f.marked != 0) { /* Skip the unmarked ones */
661 if (first) {
662 flist = g_strdup (current_panel->dir.list[i].fname);
663 first = FALSE;
664 } else {
665 /* Add empty lines after the file */
666 char *tmp;
668 tmp = g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
669 g_free (flist);
670 flist = tmp;
674 if (flist != NULL) {
675 res = save_text_to_clip_file (flist);
676 g_free (flist);
679 return res;
683 /* Label widget */
685 static cb_ret_t
686 label_callback (Widget *w, widget_msg_t msg, int parm)
688 WLabel *l = (WLabel *) w;
689 Dlg_head *h = l->widget.parent;
691 switch (msg) {
692 case WIDGET_INIT:
693 return MSG_HANDLED;
695 /* We don't want to get the focus */
696 case WIDGET_FOCUS:
697 return MSG_NOT_HANDLED;
699 case WIDGET_DRAW:
701 char *p = l->text, *q, c = 0;
702 int y = 0;
704 if (!l->text)
705 return MSG_HANDLED;
707 if (l->transparent)
708 tty_setcolor (DEFAULT_COLOR);
709 else
710 tty_setcolor (DLG_NORMALC (h));
712 for (;;) {
713 q = strchr (p, '\n');
714 if (q != NULL) {
715 c = q[0];
716 q[0] = '\0';
719 widget_move (&l->widget, y, 0);
720 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
722 if (q == NULL)
723 break;
724 q[0] = c;
725 p = q + 1;
726 y++;
728 return MSG_HANDLED;
731 case WIDGET_DESTROY:
732 g_free (l->text);
733 return MSG_HANDLED;
735 default:
736 return default_proc (msg, parm);
740 void
741 label_set_text (WLabel *label, const char *text)
743 int newcols = label->widget.cols;
744 int newlines;
746 if (label->text && text && !strcmp (label->text, text))
747 return; /* Flickering is not nice */
749 g_free (label->text);
751 if (text != NULL) {
752 label->text = g_strdup (text);
753 if (label->auto_adjust_cols) {
754 str_msg_term_size (text, &newlines, &newcols);
755 if (newcols > label->widget.cols)
756 label->widget.cols = newcols;
757 if (newlines > label->widget.lines)
758 label->widget.lines = newlines;
760 } else label->text = NULL;
762 if (label->widget.parent)
763 label_callback ((Widget *) label, WIDGET_DRAW, 0);
765 if (newcols < label->widget.cols)
766 label->widget.cols = newcols;
769 WLabel *
770 label_new (int y, int x, const char *text)
772 WLabel *l;
773 int cols = 1;
774 int lines = 1;
776 if (text != NULL)
777 str_msg_term_size (text, &lines, &cols);
779 l = g_new (WLabel, 1);
780 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
781 l->text = (text != NULL) ? g_strdup (text) : NULL;
782 l->auto_adjust_cols = 1;
783 l->transparent = 0;
784 widget_want_cursor (l->widget, 0);
785 return l;
789 /* Gauge widget (progress indicator) */
790 /* Currently width is hardcoded here for text mode */
791 #define gauge_len 47
793 static cb_ret_t
794 gauge_callback (Widget *w, widget_msg_t msg, int parm)
796 WGauge *g = (WGauge *) w;
797 Dlg_head *h = g->widget.parent;
799 if (msg == WIDGET_INIT)
800 return MSG_HANDLED;
802 /* We don't want to get the focus */
803 if (msg == WIDGET_FOCUS)
804 return MSG_NOT_HANDLED;
806 if (msg == WIDGET_DRAW){
807 widget_move (&g->widget, 0, 0);
808 tty_setcolor (DLG_NORMALC (h));
809 if (!g->shown)
810 tty_printf ("%*s", gauge_len, "");
811 else {
812 int percentage, columns;
813 long total = g->max, done = g->current;
815 if (total <= 0 || done < 0) {
816 done = 0;
817 total = 100;
819 if (done > total)
820 done = total;
821 while (total > 65535) {
822 total /= 256;
823 done /= 256;
825 percentage = (200 * done / total + 1) / 2;
826 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
827 tty_print_char ('[');
828 tty_setcolor (GAUGE_COLOR);
829 tty_printf ("%*s", (int) columns, "");
830 tty_setcolor (DLG_NORMALC (h));
831 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
833 return MSG_HANDLED;
836 return default_proc (msg, parm);
839 void
840 gauge_set_value (WGauge *g, int max, int current)
842 if (g->current == current && g->max == max)
843 return; /* Do not flicker */
844 if (max == 0)
845 max = 1; /* I do not like division by zero :) */
847 g->current = current;
848 g->max = max;
849 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
852 void
853 gauge_show (WGauge *g, int shown)
855 if (g->shown == shown)
856 return;
857 g->shown = shown;
858 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
861 WGauge *
862 gauge_new (int y, int x, int shown, int max, int current)
864 WGauge *g = g_new (WGauge, 1);
866 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
867 g->shown = shown;
868 if (max == 0)
869 max = 1; /* I do not like division by zero :) */
870 g->max = max;
871 g->current = current;
872 widget_want_cursor (g->widget, 0);
873 return g;
877 /* Input widget */
879 /* {{{ history button */
881 #define LARGE_HISTORY_BUTTON 1
883 #ifdef LARGE_HISTORY_BUTTON
884 # define HISTORY_BUTTON_WIDTH 3
885 #else
886 # define HISTORY_BUTTON_WIDTH 1
887 #endif
889 #define should_show_history_button(in) \
890 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
892 static void draw_history_button (WInput * in)
894 char c;
895 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
896 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
897 #ifdef LARGE_HISTORY_BUTTON
899 Dlg_head *h;
900 h = in->widget.parent;
901 tty_setcolor (NORMAL_COLOR);
902 tty_print_string ("[ ]");
903 /* Too distracting: tty_setcolor (MARKED_COLOR); */
904 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
905 tty_print_char (c);
907 #else
908 tty_setcolor (MARKED_COLOR);
909 tty_print_char (c);
910 #endif
913 /* }}} history button */
916 /* Input widgets now have a global kill ring */
917 /* Pointer to killed data */
918 static char *kill_buffer = 0;
920 void
921 update_input (WInput *in, int clear_first)
923 int has_history = 0;
924 int i;
925 int buf_len = str_length (in->buffer);
926 const char *cp;
927 int pw;
929 if (should_show_history_button (in))
930 has_history = HISTORY_BUTTON_WIDTH;
932 if (in->disable_update)
933 return;
935 pw = str_term_width2 (in->buffer, in->point);
937 /* Make the point visible */
938 if ((pw < in->term_first_shown) ||
939 (pw >= in->term_first_shown + in->field_width - has_history)) {
941 in->term_first_shown = pw - (in->field_width / 3);
942 if (in->term_first_shown < 0)
943 in->term_first_shown = 0;
946 /* Adjust the mark */
947 if (in->mark > buf_len)
948 in->mark = buf_len;
950 if (has_history)
951 draw_history_button (in);
953 tty_setcolor (in->color);
955 widget_move (&in->widget, 0, 0);
957 if (!in->is_password) {
958 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
959 in->field_width - has_history));
960 } else {
961 cp = in->buffer;
962 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
963 if (i >= 0) {
964 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
966 if (cp[0] != '\0') str_cnext_char (&cp);
970 if (clear_first)
971 in->first = 0;
974 void
975 winput_set_origin (WInput *in, int x, int field_width)
977 in->widget.x = x;
978 in->field_width = in->widget.cols = field_width;
979 update_input (in, 0);
982 /* {{{ history saving and loading */
984 int num_history_items_recorded = 60;
987 This loads and saves the history of an input line to and from the
988 widget. It is called with the widgets history name on creation of the
989 widget, and returns the GList list. It stores histories in the file
990 ~/.mc/history in using the profile code.
992 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
993 function) then input_new assigns the default text to be the last text
994 entered, or "" if not found.
997 GList *
998 history_get (const char *input_name)
1000 size_t i;
1001 GList *hist = NULL;
1002 char *profile;
1003 mc_config_t *cfg;
1004 char **keys;
1005 size_t keys_num = 0;
1006 char *this_entry;
1008 if (!num_history_items_recorded) /* this is how to disable */
1009 return NULL;
1010 if (!input_name || !*input_name)
1011 return NULL;
1013 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1014 cfg = mc_config_init (profile);
1016 /* get number of keys */
1017 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1018 g_strfreev (keys);
1020 for (i = 0; i < keys_num; i++) {
1021 char key_name[BUF_TINY];
1022 g_snprintf (key_name, sizeof (key_name), "%lu", (unsigned long)i);
1023 this_entry = mc_config_get_string (cfg, input_name, key_name, "");
1025 if (this_entry != NULL)
1026 hist = list_append_unique (hist, this_entry);
1029 mc_config_deinit (cfg);
1030 g_free (profile);
1032 /* return pointer to the last entry in the list */
1033 return g_list_last (hist);
1036 void
1037 history_put (const char *input_name, GList *h)
1039 int i;
1040 char *profile;
1041 mc_config_t *cfg;
1043 if (!input_name)
1044 return;
1046 if (!*input_name)
1047 return;
1049 if (!h)
1050 return;
1052 if (!num_history_items_recorded) /* this is how to disable */
1053 return;
1055 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1057 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
1058 close (i);
1060 /* Make sure the history is only readable by the user */
1061 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1062 g_free (profile);
1063 return;
1066 /* go to end of list */
1067 h = g_list_last (h);
1069 /* go back 60 places */
1070 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
1071 h = g_list_previous (h);
1073 cfg = mc_config_init(profile);
1075 if (input_name)
1076 mc_config_del_group(cfg,input_name);
1078 /* dump histories into profile */
1079 for (i = 0; h; h = g_list_next (h)) {
1080 char *text = (char *) h->data;
1082 /* We shouldn't have null entries, but let's be sure */
1083 if (text != NULL) {
1084 char key_name[BUF_TINY];
1085 g_snprintf (key_name, sizeof (key_name), "%d", i++);
1086 mc_config_set_string(cfg,input_name, key_name, text);
1090 mc_config_save_file (cfg, NULL);
1091 mc_config_deinit(cfg);
1092 g_free (profile);
1095 /* }}} history saving and loading */
1098 /* {{{ history display */
1100 static const char *
1101 i18n_htitle (void)
1103 return _(" History ");
1106 static void
1107 listbox_fwd (WListbox *l)
1109 if (l->current != l->list->prev)
1110 listbox_select_entry (l, l->current->next);
1111 else
1112 listbox_select_first (l);
1115 typedef struct {
1116 Widget *widget;
1117 int count;
1118 size_t maxlen;
1119 } dlg_hist_data;
1121 static cb_ret_t
1122 dlg_hist_reposition (Dlg_head *dlg_head)
1124 dlg_hist_data *data;
1125 int x = 0, y, he, wi;
1127 /* guard checks */
1128 if ((dlg_head == NULL)
1129 || (dlg_head->data == NULL))
1130 return MSG_NOT_HANDLED;
1132 data = (dlg_hist_data *) dlg_head->data;
1134 y = data->widget->y;
1135 he = data->count + 2;
1137 if (he <= y || y > (LINES - 6)) {
1138 he = min (he, y - 1);
1139 y -= he;
1140 } else {
1141 y++;
1142 he = min (he, LINES - y);
1145 if (data->widget->x > 2)
1146 x = data->widget->x - 2;
1148 wi = data->maxlen + 4;
1150 if ((wi + x) > COLS) {
1151 wi = min (wi, COLS);
1152 x = COLS - wi;
1155 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1157 return MSG_HANDLED;
1160 static cb_ret_t
1161 dlg_hist_callback (Dlg_head *h, Widget *sender,
1162 dlg_msg_t msg, int parm, void *data)
1164 switch (msg) {
1165 case DLG_RESIZE:
1166 return dlg_hist_reposition (h);
1168 default:
1169 return default_dlg_callback (h, sender, msg, parm, data);
1173 char *
1174 show_hist (GList *history, Widget *widget)
1176 GList *hi, *z;
1177 size_t maxlen, i, count = 0;
1178 char *q, *r = NULL;
1179 Dlg_head *query_dlg;
1180 WListbox *query_list;
1181 dlg_hist_data hist_data;
1183 if (history == NULL)
1184 return NULL;
1186 maxlen = str_term_width1 (i18n_htitle ());
1188 z = g_list_first (history);
1189 hi = z;
1190 while (hi) {
1191 i = str_term_width1 ((char *) hi->data);
1192 maxlen = max (maxlen, i);
1193 count++;
1194 hi = g_list_next (hi);
1197 hist_data.maxlen = maxlen;
1198 hist_data.widget = widget;
1199 hist_data.count = count;
1201 query_dlg =
1202 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1203 "[History-query]", i18n_htitle (), DLG_COMPACT);
1204 query_dlg->data = &hist_data;
1206 query_list = listbox_new (1, 1, 2, 2, NULL);
1208 /* this call makes list stick to all sides of dialog, effectively make
1209 it be resized with dialog */
1210 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1212 /* to avoid diplicating of (calculating sizes in two places)
1213 code, call dlg_hist_callback function here, to set dialog and
1214 controls positions.
1215 The main idea - create 4x4 dialog and add 2x2 list in
1216 center of it, and let dialog function resize it to needed
1217 size. */
1218 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1220 if (query_dlg->y < widget->y) {
1221 /* traverse */
1222 hi = z;
1223 while (hi) {
1224 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1225 0, (char *) hi->data, NULL);
1226 hi = g_list_next (hi);
1228 listbox_select_last (query_list);
1229 } else {
1230 /* traverse backwards */
1231 hi = g_list_last (history);
1232 while (hi) {
1233 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1234 0, (char *) hi->data, NULL);
1235 hi = g_list_previous (hi);
1239 if (run_dlg (query_dlg) != B_CANCEL) {
1240 listbox_get_current (query_list, &q, NULL);
1241 if (q != NULL)
1242 r = g_strdup (q);
1244 destroy_dlg (query_dlg);
1245 return r;
1248 static void
1249 do_show_hist (WInput *in)
1251 char *r;
1252 r = show_hist (in->history, &in->widget);
1253 if (r) {
1254 assign_text (in, r);
1255 g_free (r);
1259 /* }}} history display */
1261 static void
1262 input_destroy (WInput *in)
1264 if (!in){
1265 fprintf (stderr, "Internal error: null Input *\n");
1266 exit (1);
1269 new_input (in);
1271 if (in->history){
1272 if (!in->is_password) /* don't save passwords ;-) */
1273 history_put (in->history_name, in->history);
1275 in->history = g_list_first (in->history);
1276 g_list_foreach (in->history, (GFunc) g_free, NULL);
1277 g_list_free (in->history);
1280 g_free (in->buffer);
1281 free_completions (in);
1282 g_free (in->history_name);
1285 void
1286 input_disable_update (WInput *in)
1288 in->disable_update++;
1291 void
1292 input_enable_update (WInput *in)
1294 in->disable_update--;
1295 update_input (in, 0);
1299 static void
1300 push_history (WInput *in, const char *text)
1302 /* input widget where urls with passwords are entered without any
1303 vfs prefix */
1304 const char *password_input_fields[] = {
1305 N_(" Link to a remote machine "),
1306 N_(" FTP to machine "),
1307 N_(" SMB link to machine ")
1309 const size_t ELEMENTS = (sizeof (password_input_fields) /
1310 sizeof (password_input_fields[0]));
1312 char *t;
1313 size_t i;
1314 gboolean empty;
1316 if (text == NULL)
1317 return;
1319 #ifdef ENABLE_NLS
1320 for (i = 0; i < ELEMENTS; i++)
1321 password_input_fields[i] = _(password_input_fields[i]);
1322 #endif
1324 t = g_strstrip (g_strdup (text));
1325 empty = *t == '\0';
1326 g_free (t);
1327 t = g_strdup (empty ? "" : text);
1329 if (in->history_name != NULL) {
1330 const char *p = in->history_name + 3;
1332 for (i = 0; i < ELEMENTS; i++)
1333 if (strcmp (p, password_input_fields[i]) == 0)
1334 break;
1336 strip_password (t, i >= ELEMENTS);
1339 in->history = list_append_unique (in->history, t);
1340 in->need_push = 0;
1343 /* Cleans the input line and adds the current text to the history */
1344 void
1345 new_input (WInput *in)
1347 push_history (in, in->buffer);
1348 in->need_push = 1;
1349 in->buffer[0] = '\0';
1350 in->point = 0;
1351 in->charpoint = 0;
1352 in->mark = 0;
1353 free_completions (in);
1354 update_input (in, 0);
1357 static void
1358 move_buffer_backward (WInput *in, int start, int end)
1360 int i, pos, len;
1361 int str_len = str_length (in->buffer);
1362 if (start >= str_len || end > str_len + 1) return;
1364 pos = str_offset_to_pos (in->buffer, start);
1365 len = str_offset_to_pos (in->buffer, end) - pos;
1367 for (i = pos; in->buffer[i + len - 1]; i++)
1368 in->buffer[i] = in->buffer[i + len];
1371 static cb_ret_t
1372 insert_char (WInput *in, int c_code)
1374 size_t i;
1375 int res;
1377 if (c_code == -1)
1378 return MSG_NOT_HANDLED;
1380 if (in->charpoint >= MB_LEN_MAX)
1381 return MSG_HANDLED;
1383 in->charbuf[in->charpoint] = c_code;
1384 in->charpoint++;
1386 res = str_is_valid_char (in->charbuf, in->charpoint);
1387 if (res < 0) {
1388 if (res != -2)
1389 in->charpoint = 0; /* broken multibyte char, skip */
1390 return MSG_HANDLED;
1393 in->need_push = 1;
1394 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1395 /* Expand the buffer */
1396 size_t new_length = in->current_max_size +
1397 in->field_width + in->charpoint;
1398 char *narea = g_try_renew (char, in->buffer, new_length);
1399 if (narea){
1400 in->buffer = narea;
1401 in->current_max_size = new_length;
1405 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1406 /* bytes from begin */
1407 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1408 /* move chars */
1409 size_t rest_bytes = strlen (in->buffer + ins_point);
1411 for (i = rest_bytes + 1; i > 0; i--)
1412 in->buffer[ins_point + i + in->charpoint - 1] =
1413 in->buffer[ins_point + i - 1];
1415 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1416 in->point++;
1419 in->charpoint = 0;
1420 return MSG_HANDLED;
1423 static void
1424 beginning_of_line (WInput *in)
1426 in->point = 0;
1427 in->charpoint = 0;
1430 static void
1431 end_of_line (WInput *in)
1433 in->point = str_length (in->buffer);
1434 in->charpoint = 0;
1437 static void
1438 backward_char (WInput *in)
1440 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1442 if (in->point > 0) {
1443 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1445 in->charpoint = 0;
1448 static void
1449 forward_char (WInput *in)
1451 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1452 if (act[0] != '\0') {
1453 in->point+= str_cnext_noncomb_char (&act);
1455 in->charpoint = 0;
1458 static void
1459 forward_word (WInput * in)
1461 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1463 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1464 str_cnext_char (&p);
1465 in->point++;
1467 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1468 str_cnext_char (&p);
1469 in->point++;
1473 static void
1474 backward_word (WInput *in)
1476 const char *p;
1477 const char *p_tmp;
1479 for (
1480 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1481 (p != in->buffer) && (p[0] == '\0');
1482 str_cprev_char (&p), in->point--
1485 while (p != in->buffer) {
1486 p_tmp = p;
1487 str_cprev_char (&p);
1488 if (!str_isspace (p) && !str_ispunct (p)) {
1489 p = p_tmp;
1490 break;
1492 in->point--;
1494 while (p != in->buffer) {
1495 str_cprev_char (&p);
1496 if (str_isspace (p) || str_ispunct (p))
1497 break;
1499 in->point--;
1503 static void
1504 key_left (WInput *in)
1506 backward_char (in);
1509 static void
1510 key_ctrl_left (WInput *in)
1512 backward_word (in);
1515 static void
1516 key_right (WInput *in)
1518 forward_char (in);
1521 static void
1522 key_ctrl_right (WInput *in)
1524 forward_word (in);
1526 static void
1527 backward_delete (WInput *in)
1529 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1530 int start;
1532 if (in->point == 0)
1533 return;
1535 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1536 move_buffer_backward(in, start, in->point);
1537 in->charpoint = 0;
1538 in->need_push = 1;
1539 in->point = start;
1542 static void
1543 delete_char (WInput *in)
1545 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1546 int end = in->point;
1548 end+= str_cnext_noncomb_char (&act);
1550 move_buffer_backward(in, in->point, end);
1551 in->charpoint = 0;
1552 in->need_push = 1;
1555 static void
1556 copy_region (WInput *in, int x_first, int x_last)
1558 int first = min (x_first, x_last);
1559 int last = max (x_first, x_last);
1561 if (last == first) {
1562 /* Copy selected files to clipboard */
1563 panel_save_curent_file_to_clip_file ();
1564 return;
1567 g_free (kill_buffer);
1569 first = str_offset_to_pos (in->buffer, first);
1570 last = str_offset_to_pos (in->buffer, last);
1572 kill_buffer = g_strndup(in->buffer + first, last - first);
1573 save_text_to_clip_file (kill_buffer);
1576 static void
1577 delete_region (WInput *in, int x_first, int x_last)
1579 int first = min (x_first, x_last);
1580 int last = max (x_first, x_last);
1581 size_t len;
1583 in->point = first;
1584 if (in->mark > first)
1585 in->mark = first;
1586 last = str_offset_to_pos (in->buffer, last);
1587 first = str_offset_to_pos (in->buffer, first);
1588 len = strlen (&in->buffer[last]) + 1;
1589 memmove (&in->buffer[first], &in->buffer[last], len);
1590 in->charpoint = 0;
1591 in->need_push = 1;
1594 static void
1595 kill_word (WInput *in)
1597 int old_point = in->point;
1598 int new_point;
1600 forward_word (in);
1601 new_point = in->point;
1602 in->point = old_point;
1604 copy_region (in, old_point, new_point);
1605 delete_region (in, old_point, new_point);
1606 in->need_push = 1;
1607 in->charpoint = 0;
1608 in->charpoint = 0;
1611 static void
1612 back_kill_word (WInput *in)
1614 int old_point = in->point;
1615 int new_point;
1617 backward_word (in);
1618 new_point = in->point;
1619 in->point = old_point;
1621 copy_region (in, old_point, new_point);
1622 delete_region (in, old_point, new_point);
1623 in->need_push = 1;
1626 static void
1627 set_mark (WInput *in)
1629 in->mark = in->point;
1632 static void
1633 kill_save (WInput *in)
1635 copy_region (in, in->mark, in->point);
1638 static void
1639 kill_region (WInput *in)
1641 kill_save (in);
1642 delete_region (in, in->point, in->mark);
1645 static void
1646 clear_region (WInput *in)
1648 delete_region (in, in->point, in->mark);
1651 static void
1652 yank (WInput *in)
1654 char *p;
1656 if (!kill_buffer)
1657 return;
1658 in->charpoint = 0;
1659 for (p = kill_buffer; *p; p++)
1660 insert_char (in, *p);
1661 in->charpoint = 0;
1664 static void
1665 kill_line (WInput *in)
1667 int chp = str_offset_to_pos (in->buffer, in->point);
1668 g_free (kill_buffer);
1669 kill_buffer = g_strdup (&in->buffer[chp]);
1670 in->buffer[chp] = '\0';
1671 in->charpoint = 0;
1674 static void
1675 ins_from_clip (WInput *in)
1677 char *p = NULL;
1679 if (load_text_from_clip_file (&p)) {
1680 char *pp;
1682 for (pp = p; *pp != '\0'; pp++)
1683 insert_char (in, *pp);
1685 g_free (p);
1689 void
1690 assign_text (WInput *in, const char *text)
1692 free_completions (in);
1693 g_free (in->buffer);
1694 in->buffer = g_strdup (text); /* was in->buffer->text */
1695 in->current_max_size = strlen (in->buffer) + 1;
1696 in->point = str_length (in->buffer);
1697 in->mark = 0;
1698 in->need_push = 1;
1699 in->charpoint = 0;
1702 static void
1703 hist_prev (WInput *in)
1705 if (!in->history)
1706 return;
1708 if (in->need_push) {
1709 push_history (in, in->buffer);
1710 in->history = g_list_previous (in->history);
1711 } else if (in->history->prev)
1712 in->history = g_list_previous (in->history);
1713 else
1714 return;
1715 assign_text (in, (char *) in->history->data);
1716 in->need_push = 0;
1719 static void
1720 hist_next (WInput *in)
1722 if (in->need_push) {
1723 push_history (in, in->buffer);
1724 assign_text (in, "");
1725 return;
1728 if (!in->history)
1729 return;
1731 if (!in->history->next) {
1732 assign_text (in, "");
1733 return;
1736 in->history = g_list_next (in->history);
1737 assign_text (in, (char *) in->history->data);
1738 in->need_push = 0;
1741 static void
1742 port_region_marked_for_delete (WInput *in)
1744 in->buffer[0] = '\0';
1745 in->point = 0;
1746 in->first = 0;
1747 in->charpoint = 0;
1750 static cb_ret_t
1751 input_execute_cmd (WInput *in, unsigned long command)
1753 cb_ret_t res = MSG_HANDLED;
1755 switch (command) {
1756 case CK_InputBol:
1757 beginning_of_line (in);
1758 break;
1759 case CK_InputEol:
1760 end_of_line (in);
1761 break;
1762 case CK_InputMoveLeft:
1763 key_left (in);
1764 break;
1765 case CK_InputWordLeft:
1766 key_ctrl_left (in);
1767 break;
1768 case CK_InputMoveRight:
1769 key_right (in);
1770 break;
1771 case CK_InputWordRight:
1772 key_ctrl_right (in);
1773 break;
1774 case CK_InputBackwardChar:
1775 backward_char (in);
1776 break;
1777 case CK_InputBackwardWord:
1778 backward_word (in);
1779 break;
1780 case CK_InputForwardChar:
1781 forward_char (in);
1782 break;
1783 case CK_InputForwardWord:
1784 forward_word (in);
1785 break;
1786 case CK_InputBackwardDelete:
1787 backward_delete (in);
1788 break;
1789 case CK_InputDeleteChar:
1790 delete_char (in);
1791 break;
1792 case CK_InputKillWord:
1793 kill_word (in);
1794 break;
1795 case CK_InputBackwardKillWord:
1796 back_kill_word (in);
1797 break;
1798 case CK_InputSetMark:
1799 set_mark (in);
1800 break;
1801 case CK_InputKillRegion:
1802 kill_region (in);
1803 break;
1804 case CK_InputClearLine:
1805 clear_region (in);
1806 break;
1807 case CK_InputKillSave:
1808 kill_save (in);
1809 break;
1810 case CK_InputYank:
1811 yank (in);
1812 break;
1813 case CK_InputPaste:
1814 ins_from_clip (in);
1815 break;
1816 case CK_InputKillLine:
1817 kill_line (in);
1818 break;
1819 case CK_InputHistoryPrev:
1820 hist_prev (in);
1821 break;
1822 case CK_InputHistoryNext:
1823 hist_next (in);
1824 break;
1825 case CK_InputHistoryShow:
1826 do_show_hist (in);
1827 break;
1828 case CK_InputComplete:
1829 complete (in);
1830 break;
1831 default:
1832 res = MSG_NOT_HANDLED;
1835 return res;
1838 /* This function is a test for a special input key used in complete.c */
1839 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1840 and 2 if it is a complete key */
1842 is_in_input_map (WInput *in, int key)
1844 size_t i;
1845 for (i = 0; input_map[i].key != 0; i++)
1846 if (key == input_map[i].key) {
1847 input_execute_cmd (in, input_map[i].command);
1848 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1850 return 0;
1853 cb_ret_t
1854 handle_char (WInput *in, int key)
1856 cb_ret_t v;
1857 int i;
1859 v = MSG_NOT_HANDLED;
1861 if (quote) {
1862 free_completions (in);
1863 v = insert_char (in, key);
1864 update_input (in, 1);
1865 quote = 0;
1866 return v;
1868 for (i = 0; input_map[i].key; i++) {
1869 if (key == input_map[i].key) {
1870 if (input_map[i].command != CK_InputComplete)
1871 free_completions (in);
1872 input_execute_cmd (in, input_map[i].command);
1873 update_input (in, 1);
1874 v = MSG_HANDLED;
1875 break;
1878 if (input_map[i].command == 0) {
1879 if (key > 255)
1880 return MSG_NOT_HANDLED;
1881 if (in->first)
1882 port_region_marked_for_delete (in);
1883 free_completions (in);
1884 v = insert_char (in, key);
1886 update_input (in, 1);
1887 return v;
1890 /* Inserts text in input line */
1891 void
1892 stuff (WInput *in, const char *text, int insert_extra_space)
1894 input_disable_update (in);
1895 while (*text != '\0')
1896 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1897 if (insert_extra_space)
1898 handle_char (in, ' ');
1899 input_enable_update (in);
1900 update_input (in, 1);
1903 void
1904 input_set_point (WInput *in, int pos)
1906 int max_pos = str_length (in->buffer);
1908 if (pos > max_pos)
1909 pos = max_pos;
1910 if (pos != in->point)
1911 free_completions (in);
1912 in->point = pos;
1913 in->charpoint = 0;
1914 update_input (in, 1);
1917 cb_ret_t
1918 input_callback (Widget *w, widget_msg_t msg, int parm)
1920 WInput *in = (WInput *) w;
1921 cb_ret_t v;
1923 switch (msg) {
1924 case WIDGET_KEY:
1925 if (parm == XCTRL ('q')) {
1926 quote = 1;
1927 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1928 quote = 0;
1929 return v;
1932 /* Keys we want others to handle */
1933 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1934 || parm == KEY_F (10) || parm == '\n')
1935 return MSG_NOT_HANDLED;
1937 /* When pasting multiline text, insert literal Enter */
1938 if ((parm & ~KEY_M_MASK) == '\n') {
1939 quote = 1;
1940 v = handle_char (in, '\n');
1941 quote = 0;
1942 return v;
1945 return handle_char (in, parm);
1947 case WIDGET_COMMAND:
1948 return input_execute_cmd (in, parm);
1950 case WIDGET_FOCUS:
1951 case WIDGET_UNFOCUS:
1952 case WIDGET_DRAW:
1953 update_input (in, 0);
1954 return MSG_HANDLED;
1956 case WIDGET_CURSOR:
1957 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1958 - in->term_first_shown);
1959 return MSG_HANDLED;
1961 case WIDGET_DESTROY:
1962 input_destroy (in);
1963 return MSG_HANDLED;
1965 default:
1966 return default_proc (msg, parm);
1970 static int
1971 input_event (Gpm_Event * event, void *data)
1973 WInput *in = data;
1975 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1976 dlg_select_widget (in);
1978 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1979 && should_show_history_button (in)) {
1980 do_show_hist (in);
1981 } else {
1982 in->point = str_length (in->buffer);
1983 if (event->x + in->term_first_shown - 1 <
1984 str_term_width1 (in->buffer))
1986 in->point = str_column_to_pos (in->buffer, event->x
1987 + in->term_first_shown - 1);
1990 update_input (in, 1);
1992 return MOU_NORMAL;
1995 WInput *
1996 input_new (int y, int x, int color, int width, const char *def_text,
1997 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
1999 WInput *in = g_new (WInput, 1);
2000 int initial_buffer_len;
2002 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2004 /* history setup */
2005 in->history = NULL;
2006 in->history_name = 0;
2007 if (histname) {
2008 if (*histname) {
2009 in->history_name = g_strdup (histname);
2010 in->history = history_get (histname);
2014 if (def_text == NULL)
2015 def_text = "";
2017 if (def_text == INPUT_LAST_TEXT) {
2018 def_text = "";
2019 if (in->history)
2020 if (in->history->data)
2021 def_text = (char *) in->history->data;
2023 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2024 in->widget.options |= W_IS_INPUT;
2025 in->completions = NULL;
2026 in->completion_flags = completion_flags;
2027 in->current_max_size = initial_buffer_len;
2028 in->buffer = g_new (char, initial_buffer_len);
2029 in->color = color;
2030 in->field_width = width;
2031 in->first = 1;
2032 in->term_first_shown = 0;
2033 in->disable_update = 0;
2034 in->mark = 0;
2035 in->need_push = 1;
2036 in->is_password = 0;
2038 strcpy (in->buffer, def_text);
2039 in->point = str_length (in->buffer);
2040 in->charpoint = 0;
2041 return in;
2044 /* Listbox widget */
2046 /* Should draw the scrollbar, but currently draws only
2047 * indications that there is more information
2049 static int listbox_cdiff (WLEntry *s, WLEntry *e);
2051 static void
2052 listbox_drawscroll (WListbox *l)
2054 int line = 0;
2055 int i, top;
2056 int max_line = l->widget.lines - 1;
2058 /* Are we at the top? */
2059 widget_move (&l->widget, 0, l->widget.cols);
2060 if (l->list == l->top)
2061 tty_print_one_vline ();
2062 else
2063 tty_print_char ('^');
2065 /* Are we at the bottom? */
2066 widget_move (&l->widget, max_line, l->widget.cols);
2067 top = listbox_cdiff (l->list, l->top);
2068 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
2069 tty_print_one_vline ();
2070 else
2071 tty_print_char ('v');
2073 /* Now draw the nice relative pointer */
2074 if (l->count != 0)
2075 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
2077 for (i = 1; i < max_line; i++){
2078 widget_move (&l->widget, i, l->widget.cols);
2079 if (i != line)
2080 tty_print_one_vline ();
2081 else
2082 tty_print_char ('*');
2086 static void
2087 listbox_draw (WListbox *l, gboolean focused)
2089 const Dlg_head *h = l->widget.parent;
2090 const int normalc = DLG_NORMALC (h);
2091 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2093 WLEntry *e;
2094 int i;
2095 int sel_line = -1;
2096 const char *text;
2098 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2099 /* Display the entry */
2100 if (e == l->current && sel_line == -1) {
2101 sel_line = i;
2102 tty_setcolor (selc);
2103 } else
2104 tty_setcolor (normalc);
2106 widget_move (&l->widget, i, 1);
2108 if ((i > 0 && e == l->list) || !l->list)
2109 text = "";
2110 else {
2111 text = e->text;
2112 e = e->next;
2114 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2116 l->cursor_y = sel_line;
2118 if (l->scrollbar && l->count > l->widget.lines) {
2119 tty_setcolor (normalc);
2120 listbox_drawscroll (l);
2124 /* Returns the number of items between s and e,
2125 must be on the same linked list */
2126 static int
2127 listbox_cdiff (WLEntry *s, WLEntry *e)
2129 int count;
2131 for (count = 0; s != e; count++)
2132 s = s->next;
2133 return count;
2136 static WLEntry *
2137 listbox_check_hotkey (WListbox *l, int key)
2139 int i;
2140 WLEntry *e;
2142 i = 0;
2143 e = l->list;
2144 if (!e)
2145 return NULL;
2147 while (1) {
2149 /* If we didn't find anything, return */
2150 if (i && e == l->list)
2151 return NULL;
2153 if (e->hotkey == key)
2154 return e;
2156 i++;
2157 e = e->next;
2161 /* Selects the last entry and scrolls the list to the bottom */
2162 void
2163 listbox_select_last (WListbox *l)
2165 unsigned int i;
2166 l->current = l->top = l->list->prev;
2167 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2168 l->top = l->top->prev;
2169 l->pos = l->count - 1;
2172 /* Selects the first entry and scrolls the list to the top */
2173 void
2174 listbox_select_first (WListbox *l)
2176 l->current = l->top = l->list;
2177 l->pos = 0;
2180 void
2181 listbox_remove_list (WListbox *l)
2183 WLEntry *p, *q;
2185 if (!l->count)
2186 return;
2188 p = l->list;
2190 while (l->count--) {
2191 q = p->next;
2192 g_free (p->text);
2193 g_free (p);
2194 p = q;
2196 l->pos = l->count = 0;
2197 l->list = l->top = l->current = 0;
2201 * bor 30.10.96: added force flag to remove *last* entry as well
2202 * bor 30.10.96: corrected selection bug if last entry was removed
2205 void
2206 listbox_remove_current (WListbox *l, int force)
2208 WLEntry *p;
2210 /* Ok, note: this won't allow for emtpy lists */
2211 if (!force && (!l->count || l->count == 1))
2212 return;
2214 l->count--;
2215 p = l->current;
2217 if (l->count) {
2218 l->current->next->prev = l->current->prev;
2219 l->current->prev->next = l->current->next;
2220 if (p->next == l->list) {
2221 l->current = p->prev;
2222 l->pos--;
2224 else
2225 l->current = p->next;
2227 if (p == l->list)
2228 l->list = l->top = p->next;
2229 } else {
2230 l->pos = 0;
2231 l->list = l->top = l->current = 0;
2234 g_free (p->text);
2235 g_free (p);
2238 /* Makes *e the selected entry (sets current and pos) */
2239 void
2240 listbox_select_entry (WListbox *l, WLEntry *dest)
2242 WLEntry *e;
2243 int pos;
2244 int top_seen;
2246 top_seen = 0;
2248 /* Special case */
2249 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2251 if (e == l->top)
2252 top_seen = 1;
2254 if (e == dest){
2255 l->current = e;
2256 if (top_seen){
2257 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2258 l->top = l->top->next;
2259 } else {
2260 l->top = l->current;
2262 l->pos = pos;
2263 return;
2266 /* If we are unable to find it, set decent values */
2267 l->current = l->top = l->list;
2268 l->pos = 0;
2271 /* Selects from base the pos element */
2272 static WLEntry *
2273 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2275 WLEntry *last = l->list->prev;
2277 if (base == last)
2278 return last;
2279 while (pos--){
2280 base = base->next;
2281 if (base == last)
2282 break;
2284 return base;
2287 static void
2288 listbox_back (WListbox *l)
2290 if (l->pos != 0)
2291 listbox_select_entry (l, l->current->prev);
2292 else
2293 listbox_select_last (l);
2296 /* Return MSG_HANDLED if we want a redraw */
2297 static cb_ret_t
2298 listbox_key (WListbox *l, int key)
2300 int i;
2302 cb_ret_t j = MSG_NOT_HANDLED;
2304 /* focus on listbox item N by '0'..'9' keys */
2305 if (key >= '0' && key <= '9') {
2306 int oldpos = l->pos;
2307 listbox_select_by_number(l, key - '0');
2309 /* need scroll to item? */
2310 if (abs(oldpos - l->pos) > l->widget.lines)
2311 l->top = l->current;
2313 return MSG_HANDLED;
2316 if (!l->list)
2317 return MSG_NOT_HANDLED;
2319 switch (key){
2320 case KEY_HOME:
2321 case KEY_A1:
2322 case ALT ('<'):
2323 listbox_select_first (l);
2324 return MSG_HANDLED;
2326 case KEY_END:
2327 case KEY_C1:
2328 case ALT ('>'):
2329 listbox_select_last (l);
2330 return MSG_HANDLED;
2332 case XCTRL('p'):
2333 case KEY_UP:
2334 listbox_back (l);
2335 return MSG_HANDLED;
2337 case XCTRL('n'):
2338 case KEY_DOWN:
2339 listbox_fwd (l);
2340 return MSG_HANDLED;
2342 case KEY_NPAGE:
2343 case XCTRL('v'):
2344 for (i = 0; ((i < l->widget.lines - 1)
2345 && (l->current != l->list->prev)); i++) {
2346 listbox_fwd (l);
2347 j = MSG_HANDLED;
2349 return j;
2351 case KEY_PPAGE:
2352 case ALT('v'):
2353 for (i = 0; ((i < l->widget.lines - 1)
2354 && (l->current != l->list)); i++) {
2355 listbox_back (l);
2356 j = MSG_HANDLED;
2358 return j;
2360 return MSG_NOT_HANDLED;
2363 static void
2364 listbox_destroy (WListbox *l)
2366 WLEntry *n, *p = l->list;
2367 int i;
2369 for (i = 0; i < l->count; i++){
2370 n = p->next;
2371 g_free (p->text);
2372 g_free (p);
2373 p = n;
2377 static cb_ret_t
2378 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2380 WListbox *l = (WListbox *) w;
2381 Dlg_head *h = l->widget.parent;
2382 WLEntry *e;
2383 cb_ret_t ret_code;
2385 switch (msg) {
2386 case WIDGET_INIT:
2387 return MSG_HANDLED;
2389 case WIDGET_HOTKEY:
2390 e = listbox_check_hotkey (l, parm);
2391 if (e != NULL) {
2392 int action;
2394 listbox_select_entry (l, e);
2395 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2397 if (l->cback)
2398 action = (*l->cback) (l);
2399 else
2400 action = LISTBOX_DONE;
2402 if (action == LISTBOX_DONE) {
2403 h->ret_value = B_ENTER;
2404 dlg_stop (h);
2406 return MSG_HANDLED;
2408 return MSG_NOT_HANDLED;
2410 case WIDGET_KEY:
2411 ret_code = listbox_key (l, parm);
2412 if (ret_code != MSG_NOT_HANDLED) {
2413 listbox_draw (l, TRUE);
2414 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2416 return ret_code;
2418 case WIDGET_CURSOR:
2419 widget_move (&l->widget, l->cursor_y, 0);
2420 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2421 return MSG_HANDLED;
2423 case WIDGET_FOCUS:
2424 case WIDGET_UNFOCUS:
2425 case WIDGET_DRAW:
2426 listbox_draw (l, msg != WIDGET_UNFOCUS);
2427 return MSG_HANDLED;
2429 case WIDGET_DESTROY:
2430 listbox_destroy (l);
2431 return MSG_HANDLED;
2433 case WIDGET_RESIZED:
2434 return MSG_HANDLED;
2436 default:
2437 return default_proc (msg, parm);
2441 static int
2442 listbox_event (Gpm_Event *event, void *data)
2444 WListbox *l = data;
2445 int i;
2447 Dlg_head *h = l->widget.parent;
2449 /* Single click */
2450 if (event->type & GPM_DOWN)
2451 dlg_select_widget (l);
2453 if (l->list == NULL)
2454 return MOU_NORMAL;
2456 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2457 int ret = MOU_REPEAT;
2459 if (event->x < 0 || event->x > l->widget.cols)
2460 return ret;
2462 if (event->y < 1)
2463 for (i = -event->y; i >= 0; i--)
2464 listbox_back (l);
2465 else if (event->y > l->widget.lines)
2466 for (i = event->y - l->widget.lines; i > 0; i--)
2467 listbox_fwd (l);
2468 else if (event->buttons & GPM_B_UP) {
2469 listbox_back (l);
2470 ret = MOU_NORMAL;
2471 } else if (event->buttons & GPM_B_DOWN) {
2472 listbox_fwd (l);
2473 ret = MOU_NORMAL;
2474 } else
2475 listbox_select_entry (l,
2476 listbox_select_pos (l, l->top,
2477 event->y - 1));
2479 /* We need to refresh ourselves since the dialog manager doesn't */
2480 /* know about this event */
2481 listbox_draw (l, TRUE);
2482 return ret;
2485 /* Double click */
2486 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2487 int action;
2489 if (event->x < 0 || event->x >= l->widget.cols
2490 || event->y < 1 || event->y > l->widget.lines)
2491 return MOU_NORMAL;
2493 dlg_select_widget (l);
2494 listbox_select_entry (l,
2495 listbox_select_pos (l, l->top,
2496 event->y - 1));
2498 if (l->cback)
2499 action = (*l->cback) (l);
2500 else
2501 action = LISTBOX_DONE;
2503 if (action == LISTBOX_DONE) {
2504 h->ret_value = B_ENTER;
2505 dlg_stop (h);
2506 return MOU_NORMAL;
2509 return MOU_NORMAL;
2512 WListbox *
2513 listbox_new (int y, int x, int height, int width, lcback callback)
2515 WListbox *l = g_new (WListbox, 1);
2517 if (height <= 0)
2518 height = 1;
2520 init_widget (&l->widget, y, x, height, width,
2521 listbox_callback, listbox_event);
2523 l->list = l->top = l->current = 0;
2524 l->pos = 0;
2525 l->count = 0;
2526 l->cback = callback;
2527 l->allow_duplicates = 1;
2528 l->scrollbar = !tty_is_slow ();
2529 widget_want_hotkey (l->widget, 1);
2531 return l;
2534 /* Listbox item adding function. They still lack a lot of functionality */
2535 /* any takers? */
2536 /* 1.11.96 bor: added pos argument to control placement of new entry */
2537 static void
2538 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2540 if (!l->list){
2541 l->list = e;
2542 l->top = e;
2543 l->current = e;
2544 e->next = l->list;
2545 e->prev = l->list;
2546 } else if (pos == LISTBOX_APPEND_AT_END) {
2547 e->next = l->list;
2548 e->prev = l->list->prev;
2549 l->list->prev->next = e;
2550 l->list->prev = e;
2551 } else if (pos == LISTBOX_APPEND_BEFORE){
2552 e->next = l->current;
2553 e->prev = l->current->prev;
2554 l->current->prev->next = e;
2555 l->current->prev = e;
2556 if (l->list == l->current) { /* move list one position down */
2557 l->list = e;
2558 l->top = e;
2560 } else if (pos == LISTBOX_APPEND_AFTER) {
2561 e->prev = l->current;
2562 e->next = l->current->next;
2563 l->current->next->prev = e;
2564 l->current->next = e;
2565 } else if (pos == LISTBOX_APPEND_SORTED) {
2566 WLEntry *w = l->list;
2568 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2569 w = w->next;
2570 if (w->next == l->list) {
2571 e->prev = w;
2572 e->next = l->list;
2573 w->next = e;
2574 l->list->prev = e;
2575 } else {
2576 e->next = w;
2577 e->prev = w->prev;
2578 w->prev->next = e;
2579 w->prev = e;
2582 l->count++;
2585 char *
2586 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2587 const char *text, void *data)
2589 WLEntry *entry;
2591 if (!l)
2592 return NULL;
2594 if (!l->allow_duplicates)
2595 if (listbox_search_text (l, text))
2596 return NULL;
2598 entry = g_new (WLEntry, 1);
2599 entry->text = g_strdup (text);
2600 entry->data = data;
2601 entry->hotkey = hotkey;
2603 listbox_append_item (l, entry, pos);
2605 return entry->text;
2608 /* Selects the nth entry in the listbox */
2609 void
2610 listbox_select_by_number (WListbox *l, int n)
2612 if (l->list != NULL)
2613 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2616 WLEntry *
2617 listbox_search_text (WListbox *l, const char *text)
2619 WLEntry *e;
2621 e = l->list;
2622 if (!e)
2623 return NULL;
2625 do {
2626 if(!strcmp (e->text, text))
2627 return e;
2628 e = e->next;
2629 } while (e!=l->list);
2631 return NULL;
2634 /* Returns the current string text as well as the associated extra data */
2635 void
2636 listbox_get_current (WListbox *l, char **string, char **extra)
2638 if (!l->current){
2639 *string = 0;
2640 *extra = 0;
2642 if (string && l->current)
2643 *string = l->current->text;
2644 if (extra && l->current)
2645 *extra = l->current->data;
2648 /* returns TRUE if a function has been called, FALSE otherwise. */
2649 static gboolean
2650 buttonbar_call (WButtonBar *bb, int i)
2652 cb_ret_t ret = MSG_NOT_HANDLED;
2654 if (bb != NULL)
2655 ret = bb->widget.parent->callback (bb->widget.parent,
2656 (Widget *) bb, DLG_ACTION,
2657 bb->labels[i].command,
2658 bb->labels[i].receiver);
2659 return ret;
2662 /* calculate width of one button, width is never lesser than 7 */
2663 static int
2664 buttonbat_get_button_width (void)
2666 int result = COLS / BUTTONBAR_LABELS_NUM;
2667 return (result >= 7) ? result : 7;
2670 static cb_ret_t
2671 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2673 WButtonBar *bb = (WButtonBar *) w;
2674 int i;
2675 const char *text;
2677 switch (msg) {
2678 case WIDGET_FOCUS:
2679 return MSG_NOT_HANDLED;
2681 case WIDGET_HOTKEY:
2682 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2683 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2684 return MSG_HANDLED;
2685 return MSG_NOT_HANDLED;
2687 case WIDGET_DRAW:
2688 if (bb->visible) {
2689 int offset = 0;
2690 int count_free_positions;
2692 widget_move (&bb->widget, 0, 0);
2693 tty_setcolor (DEFAULT_COLOR);
2694 bb->btn_width = buttonbat_get_button_width ();
2695 tty_printf ("%-*s", bb->widget.cols, "");
2696 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2698 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2699 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2700 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2701 tty_printf ("%2d", i + 1);
2702 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2703 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2704 tty_print_string (str_fit_to_term (
2705 text,
2706 bb->btn_width - 2 + (int)(offset < count_free_positions),
2707 J_LEFT_FIT));
2709 if (count_free_positions != 0 && offset < count_free_positions)
2710 offset++;
2713 return MSG_HANDLED;
2715 case WIDGET_DESTROY:
2716 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2717 g_free (bb->labels[i].text);
2718 return MSG_HANDLED;
2720 default:
2721 return default_proc (msg, parm);
2725 static int
2726 buttonbar_event (Gpm_Event *event, void *data)
2728 WButtonBar *bb = data;
2729 int button;
2731 if (!(event->type & GPM_UP))
2732 return MOU_NORMAL;
2733 if (event->y == 2)
2734 return MOU_NORMAL;
2735 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2736 if (button < BUTTONBAR_LABELS_NUM)
2737 buttonbar_call (bb, button);
2738 return MOU_NORMAL;
2741 WButtonBar *
2742 buttonbar_new (gboolean visible)
2744 WButtonBar *bb;
2746 bb = g_new0 (WButtonBar, 1);
2748 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2749 buttonbar_callback, buttonbar_event);
2750 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2751 bb->visible = visible;
2752 widget_want_hotkey (bb->widget, 1);
2753 widget_want_cursor (bb->widget, 0);
2754 bb->btn_width = buttonbat_get_button_width ();
2756 return bb;
2759 static void
2760 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2762 g_free (bb->labels[lc_index - 1].text);
2763 bb->labels[lc_index - 1].text = g_strdup (text);
2766 /* Find ButtonBar widget in the dialog */
2767 WButtonBar *
2768 find_buttonbar (const Dlg_head *h)
2770 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2773 void
2774 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2775 const struct global_keymap_t *keymap, const Widget *receiver)
2777 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2778 unsigned long command = CK_Ignore_Key;
2780 if (keymap != NULL)
2781 command = lookup_keymap_command (keymap, KEY_F (idx));
2783 if ((text == NULL) || (text[0] == '\0'))
2784 set_label_text (bb, idx, "");
2785 else
2786 set_label_text (bb, idx, text);
2788 bb->labels[idx - 1].command = command;
2789 bb->labels[idx - 1].receiver = (Widget *) receiver;
2793 void
2794 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2796 bb->visible = visible;
2799 void
2800 buttonbar_redraw (WButtonBar *bb)
2802 if (bb != NULL)
2803 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2806 static cb_ret_t
2807 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2809 WGroupbox *g = (WGroupbox *) w;
2811 switch (msg) {
2812 case WIDGET_INIT:
2813 return MSG_HANDLED;
2815 case WIDGET_FOCUS:
2816 return MSG_NOT_HANDLED;
2818 case WIDGET_DRAW:
2819 tty_setcolor (COLOR_NORMAL);
2820 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2821 g->widget.x - g->widget.parent->x, g->widget.lines,
2822 g->widget.cols);
2824 tty_setcolor (COLOR_HOT_NORMAL);
2825 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2826 g->widget.x - g->widget.parent->x + 1);
2827 tty_print_string (g->title);
2828 return MSG_HANDLED;
2830 case WIDGET_DESTROY:
2831 g_free (g->title);
2832 return MSG_HANDLED;
2834 default:
2835 return default_proc (msg, parm);
2839 WGroupbox *
2840 groupbox_new (int y, int x, int height, int width, const char *title)
2842 WGroupbox *g = g_new (WGroupbox, 1);
2844 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2846 g->widget.options &= ~W_WANT_CURSOR;
2847 widget_want_hotkey (g->widget, 0);
2849 /* Strip existing spaces, add one space before and after the title */
2850 if (title) {
2851 char *t;
2852 t = g_strstrip (g_strdup (title));
2853 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2854 g_free (t);
2857 return g;