Merge branch '1981_AM_PROG_CC_C_O'
[midnight-commander.git] / src / widget.c
blob4bd38f6abcadb2e4f69dff30b692fc3ac6d1ee4b
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) /* this is how to disable */
1008 return NULL;
1009 if (!input_name || !*input_name)
1010 return NULL;
1012 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1013 cfg = mc_config_init (profile);
1015 /* get number of keys */
1016 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1017 g_strfreev (keys);
1019 for (i = 0; i < keys_num; i++) {
1020 char key_name[BUF_TINY];
1021 g_snprintf (key_name, sizeof (key_name), "%lu", (unsigned long)i);
1022 this_entry = mc_config_get_string (cfg, input_name, key_name, "");
1024 if (this_entry != 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 (!input_name)
1043 return;
1045 if (!*input_name)
1046 return;
1048 if (!h)
1049 return;
1051 if (!num_history_items_recorded) /* this is how to disable */
1052 return;
1054 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1056 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
1057 close (i);
1059 /* Make sure the history is only readable by the user */
1060 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1061 g_free (profile);
1062 return;
1065 /* go to end of list */
1066 h = g_list_last (h);
1068 /* go back 60 places */
1069 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
1070 h = g_list_previous (h);
1072 cfg = mc_config_init(profile);
1074 if (input_name)
1075 mc_config_del_group(cfg,input_name);
1077 /* dump histories into profile */
1078 for (i = 0; h; h = g_list_next (h)) {
1079 char *text = (char *) h->data;
1081 /* We shouldn't have null entries, but let's be sure */
1082 if (text != NULL) {
1083 char key_name[BUF_TINY];
1084 g_snprintf (key_name, sizeof (key_name), "%d", i++);
1085 mc_config_set_string(cfg,input_name, key_name, text);
1089 mc_config_save_file (cfg, NULL);
1090 mc_config_deinit(cfg);
1091 g_free (profile);
1094 /* }}} history saving and loading */
1097 /* {{{ history display */
1099 static const char *
1100 i18n_htitle (void)
1102 return _(" History ");
1105 static void
1106 listbox_fwd (WListbox *l)
1108 if (l->current != l->list->prev)
1109 listbox_select_entry (l, l->current->next);
1110 else
1111 listbox_select_first (l);
1114 typedef struct {
1115 Widget *widget;
1116 int count;
1117 size_t maxlen;
1118 } dlg_hist_data;
1120 static cb_ret_t
1121 dlg_hist_reposition (Dlg_head *dlg_head)
1123 dlg_hist_data *data;
1124 int x = 0, y, he, wi;
1126 /* guard checks */
1127 if ((dlg_head == NULL)
1128 || (dlg_head->data == NULL))
1129 return MSG_NOT_HANDLED;
1131 data = (dlg_hist_data *) dlg_head->data;
1133 y = data->widget->y;
1134 he = data->count + 2;
1136 if (he <= y || y > (LINES - 6)) {
1137 he = min (he, y - 1);
1138 y -= he;
1139 } else {
1140 y++;
1141 he = min (he, LINES - y);
1144 if (data->widget->x > 2)
1145 x = data->widget->x - 2;
1147 wi = data->maxlen + 4;
1149 if ((wi + x) > COLS) {
1150 wi = min (wi, COLS);
1151 x = COLS - wi;
1154 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1156 return MSG_HANDLED;
1159 static cb_ret_t
1160 dlg_hist_callback (Dlg_head *h, Widget *sender,
1161 dlg_msg_t msg, int parm, void *data)
1163 switch (msg) {
1164 case DLG_RESIZE:
1165 return dlg_hist_reposition (h);
1167 default:
1168 return default_dlg_callback (h, sender, msg, parm, data);
1172 char *
1173 show_hist (GList *history, Widget *widget)
1175 GList *hi, *z;
1176 size_t maxlen, i, count = 0;
1177 char *q, *r = NULL;
1178 Dlg_head *query_dlg;
1179 WListbox *query_list;
1180 dlg_hist_data hist_data;
1182 if (history == NULL)
1183 return NULL;
1185 maxlen = str_term_width1 (i18n_htitle ());
1187 z = g_list_first (history);
1188 hi = z;
1189 while (hi) {
1190 i = str_term_width1 ((char *) hi->data);
1191 maxlen = max (maxlen, i);
1192 count++;
1193 hi = g_list_next (hi);
1196 hist_data.maxlen = maxlen;
1197 hist_data.widget = widget;
1198 hist_data.count = count;
1200 query_dlg =
1201 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1202 "[History-query]", i18n_htitle (), DLG_COMPACT);
1203 query_dlg->data = &hist_data;
1205 query_list = listbox_new (1, 1, 2, 2, NULL);
1207 /* this call makes list stick to all sides of dialog, effectively make
1208 it be resized with dialog */
1209 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1211 /* to avoid diplicating of (calculating sizes in two places)
1212 code, call dlg_hist_callback function here, to set dialog and
1213 controls positions.
1214 The main idea - create 4x4 dialog and add 2x2 list in
1215 center of it, and let dialog function resize it to needed
1216 size. */
1217 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1219 if (query_dlg->y < widget->y) {
1220 /* traverse */
1221 hi = z;
1222 while (hi) {
1223 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1224 0, (char *) hi->data, NULL);
1225 hi = g_list_next (hi);
1227 listbox_select_last (query_list);
1228 } else {
1229 /* traverse backwards */
1230 hi = g_list_last (history);
1231 while (hi) {
1232 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1233 0, (char *) hi->data, NULL);
1234 hi = g_list_previous (hi);
1238 if (run_dlg (query_dlg) != B_CANCEL) {
1239 listbox_get_current (query_list, &q, NULL);
1240 if (q != NULL)
1241 r = g_strdup (q);
1243 destroy_dlg (query_dlg);
1244 return r;
1247 static void
1248 do_show_hist (WInput *in)
1250 char *r;
1251 r = show_hist (in->history, &in->widget);
1252 if (r) {
1253 assign_text (in, r);
1254 g_free (r);
1258 /* }}} history display */
1260 static void
1261 input_destroy (WInput *in)
1263 if (!in){
1264 fprintf (stderr, "Internal error: null Input *\n");
1265 exit (1);
1268 new_input (in);
1270 if (in->history){
1271 if (!in->is_password) /* don't save passwords ;-) */
1272 history_put (in->history_name, in->history);
1274 in->history = g_list_first (in->history);
1275 g_list_foreach (in->history, (GFunc) g_free, NULL);
1276 g_list_free (in->history);
1279 g_free (in->buffer);
1280 free_completions (in);
1281 g_free (in->history_name);
1284 void
1285 input_disable_update (WInput *in)
1287 in->disable_update++;
1290 void
1291 input_enable_update (WInput *in)
1293 in->disable_update--;
1294 update_input (in, 0);
1298 static void
1299 push_history (WInput *in, const char *text)
1301 /* input widget where urls with passwords are entered without any
1302 vfs prefix */
1303 const char *password_input_fields[] = {
1304 N_(" Link to a remote machine "),
1305 N_(" FTP to machine "),
1306 N_(" SMB link to machine ")
1308 const size_t ELEMENTS = (sizeof (password_input_fields) /
1309 sizeof (password_input_fields[0]));
1311 char *t;
1312 size_t i;
1313 gboolean empty;
1315 if (text == NULL)
1316 return;
1318 #ifdef ENABLE_NLS
1319 for (i = 0; i < ELEMENTS; i++)
1320 password_input_fields[i] = _(password_input_fields[i]);
1321 #endif
1323 t = g_strstrip (g_strdup (text));
1324 empty = *t == '\0';
1325 g_free (t);
1326 t = g_strdup (empty ? "" : text);
1328 if (in->history_name != NULL) {
1329 const char *p = in->history_name + 3;
1331 for (i = 0; i < ELEMENTS; i++)
1332 if (strcmp (p, password_input_fields[i]) == 0)
1333 break;
1335 strip_password (t, i >= ELEMENTS);
1338 in->history = list_append_unique (in->history, t);
1339 in->need_push = 0;
1342 /* Cleans the input line and adds the current text to the history */
1343 void
1344 new_input (WInput *in)
1346 push_history (in, in->buffer);
1347 in->need_push = 1;
1348 in->buffer[0] = '\0';
1349 in->point = 0;
1350 in->charpoint = 0;
1351 in->mark = 0;
1352 free_completions (in);
1353 update_input (in, 0);
1356 static void
1357 move_buffer_backward (WInput *in, int start, int end)
1359 int i, pos, len;
1360 int str_len = str_length (in->buffer);
1361 if (start >= str_len || end > str_len + 1) return;
1363 pos = str_offset_to_pos (in->buffer, start);
1364 len = str_offset_to_pos (in->buffer, end) - pos;
1366 for (i = pos; in->buffer[i + len - 1]; i++)
1367 in->buffer[i] = in->buffer[i + len];
1370 static cb_ret_t
1371 insert_char (WInput *in, int c_code)
1373 size_t i;
1374 int res;
1376 if (c_code == -1)
1377 return MSG_NOT_HANDLED;
1379 if (in->charpoint >= MB_LEN_MAX)
1380 return MSG_HANDLED;
1382 in->charbuf[in->charpoint] = c_code;
1383 in->charpoint++;
1385 res = str_is_valid_char (in->charbuf, in->charpoint);
1386 if (res < 0) {
1387 if (res != -2)
1388 in->charpoint = 0; /* broken multibyte char, skip */
1389 return MSG_HANDLED;
1392 in->need_push = 1;
1393 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1394 /* Expand the buffer */
1395 size_t new_length = in->current_max_size +
1396 in->field_width + in->charpoint;
1397 char *narea = g_try_renew (char, in->buffer, new_length);
1398 if (narea){
1399 in->buffer = narea;
1400 in->current_max_size = new_length;
1404 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1405 /* bytes from begin */
1406 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1407 /* move chars */
1408 size_t rest_bytes = strlen (in->buffer + ins_point);
1410 for (i = rest_bytes + 1; i > 0; i--)
1411 in->buffer[ins_point + i + in->charpoint - 1] =
1412 in->buffer[ins_point + i - 1];
1414 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1415 in->point++;
1418 in->charpoint = 0;
1419 return MSG_HANDLED;
1422 static void
1423 beginning_of_line (WInput *in)
1425 in->point = 0;
1426 in->charpoint = 0;
1429 static void
1430 end_of_line (WInput *in)
1432 in->point = str_length (in->buffer);
1433 in->charpoint = 0;
1436 static void
1437 backward_char (WInput *in)
1439 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1441 if (in->point > 0) {
1442 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1444 in->charpoint = 0;
1447 static void
1448 forward_char (WInput *in)
1450 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1451 if (act[0] != '\0') {
1452 in->point+= str_cnext_noncomb_char (&act);
1454 in->charpoint = 0;
1457 static void
1458 forward_word (WInput * in)
1460 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1462 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1463 str_cnext_char (&p);
1464 in->point++;
1466 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1467 str_cnext_char (&p);
1468 in->point++;
1472 static void
1473 backward_word (WInput *in)
1475 const char *p;
1476 const char *p_tmp;
1478 for (
1479 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1480 (p != in->buffer) && (p[0] == '\0');
1481 str_cprev_char (&p), in->point--
1484 while (p != in->buffer) {
1485 p_tmp = p;
1486 str_cprev_char (&p);
1487 if (!str_isspace (p) && !str_ispunct (p)) {
1488 p = p_tmp;
1489 break;
1491 in->point--;
1493 while (p != in->buffer) {
1494 str_cprev_char (&p);
1495 if (str_isspace (p) || str_ispunct (p))
1496 break;
1498 in->point--;
1502 static void
1503 key_left (WInput *in)
1505 backward_char (in);
1508 static void
1509 key_ctrl_left (WInput *in)
1511 backward_word (in);
1514 static void
1515 key_right (WInput *in)
1517 forward_char (in);
1520 static void
1521 key_ctrl_right (WInput *in)
1523 forward_word (in);
1525 static void
1526 backward_delete (WInput *in)
1528 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1529 int start;
1531 if (in->point == 0)
1532 return;
1534 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1535 move_buffer_backward(in, start, in->point);
1536 in->charpoint = 0;
1537 in->need_push = 1;
1538 in->point = start;
1541 static void
1542 delete_char (WInput *in)
1544 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1545 int end = in->point;
1547 end+= str_cnext_noncomb_char (&act);
1549 move_buffer_backward(in, in->point, end);
1550 in->charpoint = 0;
1551 in->need_push = 1;
1554 static void
1555 copy_region (WInput *in, int x_first, int x_last)
1557 int first = min (x_first, x_last);
1558 int last = max (x_first, x_last);
1560 if (last == first) {
1561 /* Copy selected files to clipboard */
1562 panel_save_curent_file_to_clip_file ();
1563 return;
1566 g_free (kill_buffer);
1568 first = str_offset_to_pos (in->buffer, first);
1569 last = str_offset_to_pos (in->buffer, last);
1571 kill_buffer = g_strndup(in->buffer + first, last - first);
1572 save_text_to_clip_file (kill_buffer);
1575 static void
1576 delete_region (WInput *in, int x_first, int x_last)
1578 int first = min (x_first, x_last);
1579 int last = max (x_first, x_last);
1580 size_t len;
1582 in->point = first;
1583 if (in->mark > first)
1584 in->mark = first;
1585 last = str_offset_to_pos (in->buffer, last);
1586 first = str_offset_to_pos (in->buffer, first);
1587 len = strlen (&in->buffer[last]) + 1;
1588 memmove (&in->buffer[first], &in->buffer[last], len);
1589 in->charpoint = 0;
1590 in->need_push = 1;
1593 static void
1594 kill_word (WInput *in)
1596 int old_point = in->point;
1597 int new_point;
1599 forward_word (in);
1600 new_point = in->point;
1601 in->point = old_point;
1603 copy_region (in, old_point, new_point);
1604 delete_region (in, old_point, new_point);
1605 in->need_push = 1;
1606 in->charpoint = 0;
1607 in->charpoint = 0;
1610 static void
1611 back_kill_word (WInput *in)
1613 int old_point = in->point;
1614 int new_point;
1616 backward_word (in);
1617 new_point = in->point;
1618 in->point = old_point;
1620 copy_region (in, old_point, new_point);
1621 delete_region (in, old_point, new_point);
1622 in->need_push = 1;
1625 static void
1626 set_mark (WInput *in)
1628 in->mark = in->point;
1631 static void
1632 kill_save (WInput *in)
1634 copy_region (in, in->mark, in->point);
1637 static void
1638 kill_region (WInput *in)
1640 kill_save (in);
1641 delete_region (in, in->point, in->mark);
1644 static void
1645 clear_region (WInput *in)
1647 delete_region (in, in->point, in->mark);
1650 static void
1651 yank (WInput *in)
1653 char *p;
1655 if (!kill_buffer)
1656 return;
1657 in->charpoint = 0;
1658 for (p = kill_buffer; *p; p++)
1659 insert_char (in, *p);
1660 in->charpoint = 0;
1663 static void
1664 kill_line (WInput *in)
1666 int chp = str_offset_to_pos (in->buffer, in->point);
1667 g_free (kill_buffer);
1668 kill_buffer = g_strdup (&in->buffer[chp]);
1669 in->buffer[chp] = '\0';
1670 in->charpoint = 0;
1673 static void
1674 ins_from_clip (WInput *in)
1676 char *p = NULL;
1678 if (load_text_from_clip_file (&p)) {
1679 char *pp;
1681 for (pp = p; *pp != '\0'; pp++)
1682 insert_char (in, *pp);
1684 g_free (p);
1688 void
1689 assign_text (WInput *in, const char *text)
1691 free_completions (in);
1692 g_free (in->buffer);
1693 in->buffer = g_strdup (text); /* was in->buffer->text */
1694 in->current_max_size = strlen (in->buffer) + 1;
1695 in->point = str_length (in->buffer);
1696 in->mark = 0;
1697 in->need_push = 1;
1698 in->charpoint = 0;
1701 static void
1702 hist_prev (WInput *in)
1704 GList *prev;
1706 if (!in->history)
1707 return;
1709 if (in->need_push)
1710 push_history (in, in->buffer);
1712 prev = g_list_previous (in->history);
1713 if (prev != NULL) {
1714 in->history = prev;
1715 assign_text (in, (char *) prev->data);
1716 in->need_push = 0;
1720 static void
1721 hist_next (WInput *in)
1723 if (in->need_push) {
1724 push_history (in, in->buffer);
1725 assign_text (in, "");
1726 return;
1729 if (!in->history)
1730 return;
1732 if (!in->history->next) {
1733 assign_text (in, "");
1734 return;
1737 in->history = g_list_next (in->history);
1738 assign_text (in, (char *) in->history->data);
1739 in->need_push = 0;
1742 static void
1743 port_region_marked_for_delete (WInput *in)
1745 in->buffer[0] = '\0';
1746 in->point = 0;
1747 in->first = 0;
1748 in->charpoint = 0;
1751 static cb_ret_t
1752 input_execute_cmd (WInput *in, unsigned long command)
1754 cb_ret_t res = MSG_HANDLED;
1756 switch (command) {
1757 case CK_InputBol:
1758 beginning_of_line (in);
1759 break;
1760 case CK_InputEol:
1761 end_of_line (in);
1762 break;
1763 case CK_InputMoveLeft:
1764 key_left (in);
1765 break;
1766 case CK_InputWordLeft:
1767 key_ctrl_left (in);
1768 break;
1769 case CK_InputMoveRight:
1770 key_right (in);
1771 break;
1772 case CK_InputWordRight:
1773 key_ctrl_right (in);
1774 break;
1775 case CK_InputBackwardChar:
1776 backward_char (in);
1777 break;
1778 case CK_InputBackwardWord:
1779 backward_word (in);
1780 break;
1781 case CK_InputForwardChar:
1782 forward_char (in);
1783 break;
1784 case CK_InputForwardWord:
1785 forward_word (in);
1786 break;
1787 case CK_InputBackwardDelete:
1788 backward_delete (in);
1789 break;
1790 case CK_InputDeleteChar:
1791 delete_char (in);
1792 break;
1793 case CK_InputKillWord:
1794 kill_word (in);
1795 break;
1796 case CK_InputBackwardKillWord:
1797 back_kill_word (in);
1798 break;
1799 case CK_InputSetMark:
1800 set_mark (in);
1801 break;
1802 case CK_InputKillRegion:
1803 kill_region (in);
1804 break;
1805 case CK_InputClearLine:
1806 clear_region (in);
1807 break;
1808 case CK_InputKillSave:
1809 kill_save (in);
1810 break;
1811 case CK_InputYank:
1812 yank (in);
1813 break;
1814 case CK_InputPaste:
1815 ins_from_clip (in);
1816 break;
1817 case CK_InputKillLine:
1818 kill_line (in);
1819 break;
1820 case CK_InputHistoryPrev:
1821 hist_prev (in);
1822 break;
1823 case CK_InputHistoryNext:
1824 hist_next (in);
1825 break;
1826 case CK_InputHistoryShow:
1827 do_show_hist (in);
1828 break;
1829 case CK_InputComplete:
1830 complete (in);
1831 break;
1832 default:
1833 res = MSG_NOT_HANDLED;
1836 return res;
1839 /* This function is a test for a special input key used in complete.c */
1840 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1841 and 2 if it is a complete key */
1843 is_in_input_map (WInput *in, int key)
1845 size_t i;
1846 for (i = 0; input_map[i].key != 0; i++)
1847 if (key == input_map[i].key) {
1848 input_execute_cmd (in, input_map[i].command);
1849 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1851 return 0;
1854 cb_ret_t
1855 handle_char (WInput *in, int key)
1857 cb_ret_t v;
1858 int i;
1860 v = MSG_NOT_HANDLED;
1862 if (quote) {
1863 free_completions (in);
1864 v = insert_char (in, key);
1865 update_input (in, 1);
1866 quote = 0;
1867 return v;
1869 for (i = 0; input_map[i].key; i++) {
1870 if (key == input_map[i].key) {
1871 if (input_map[i].command != CK_InputComplete)
1872 free_completions (in);
1873 input_execute_cmd (in, input_map[i].command);
1874 update_input (in, 1);
1875 v = MSG_HANDLED;
1876 break;
1879 if (input_map[i].command == 0) {
1880 if (key > 255)
1881 return MSG_NOT_HANDLED;
1882 if (in->first)
1883 port_region_marked_for_delete (in);
1884 free_completions (in);
1885 v = insert_char (in, key);
1887 update_input (in, 1);
1888 return v;
1891 /* Inserts text in input line */
1892 void
1893 stuff (WInput *in, const char *text, int insert_extra_space)
1895 input_disable_update (in);
1896 while (*text != '\0')
1897 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1898 if (insert_extra_space)
1899 handle_char (in, ' ');
1900 input_enable_update (in);
1901 update_input (in, 1);
1904 void
1905 input_set_point (WInput *in, int pos)
1907 int max_pos = str_length (in->buffer);
1909 if (pos > max_pos)
1910 pos = max_pos;
1911 if (pos != in->point)
1912 free_completions (in);
1913 in->point = pos;
1914 in->charpoint = 0;
1915 update_input (in, 1);
1918 cb_ret_t
1919 input_callback (Widget *w, widget_msg_t msg, int parm)
1921 WInput *in = (WInput *) w;
1922 cb_ret_t v;
1924 switch (msg) {
1925 case WIDGET_KEY:
1926 if (parm == XCTRL ('q')) {
1927 quote = 1;
1928 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1929 quote = 0;
1930 return v;
1933 /* Keys we want others to handle */
1934 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1935 || parm == KEY_F (10) || parm == '\n')
1936 return MSG_NOT_HANDLED;
1938 /* When pasting multiline text, insert literal Enter */
1939 if ((parm & ~KEY_M_MASK) == '\n') {
1940 quote = 1;
1941 v = handle_char (in, '\n');
1942 quote = 0;
1943 return v;
1946 return handle_char (in, parm);
1948 case WIDGET_COMMAND:
1949 return input_execute_cmd (in, parm);
1951 case WIDGET_FOCUS:
1952 case WIDGET_UNFOCUS:
1953 case WIDGET_DRAW:
1954 update_input (in, 0);
1955 return MSG_HANDLED;
1957 case WIDGET_CURSOR:
1958 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1959 - in->term_first_shown);
1960 return MSG_HANDLED;
1962 case WIDGET_DESTROY:
1963 input_destroy (in);
1964 return MSG_HANDLED;
1966 default:
1967 return default_proc (msg, parm);
1971 static int
1972 input_event (Gpm_Event * event, void *data)
1974 WInput *in = data;
1976 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1977 dlg_select_widget (in);
1979 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1980 && should_show_history_button (in)) {
1981 do_show_hist (in);
1982 } else {
1983 in->point = str_length (in->buffer);
1984 if (event->x + in->term_first_shown - 1 <
1985 str_term_width1 (in->buffer))
1987 in->point = str_column_to_pos (in->buffer, event->x
1988 + in->term_first_shown - 1);
1991 update_input (in, 1);
1993 return MOU_NORMAL;
1996 WInput *
1997 input_new (int y, int x, int color, int width, const char *def_text,
1998 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2000 WInput *in = g_new (WInput, 1);
2001 int initial_buffer_len;
2003 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2005 /* history setup */
2006 in->history = NULL;
2007 in->history_name = 0;
2008 if (histname) {
2009 if (*histname) {
2010 in->history_name = g_strdup (histname);
2011 in->history = history_get (histname);
2015 if (def_text == NULL)
2016 def_text = "";
2018 if (def_text == INPUT_LAST_TEXT) {
2019 def_text = "";
2020 if (in->history)
2021 if (in->history->data)
2022 def_text = (char *) in->history->data;
2024 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2025 in->widget.options |= W_IS_INPUT;
2026 in->completions = NULL;
2027 in->completion_flags = completion_flags;
2028 in->current_max_size = initial_buffer_len;
2029 in->buffer = g_new (char, initial_buffer_len);
2030 in->color = color;
2031 in->field_width = width;
2032 in->first = 1;
2033 in->term_first_shown = 0;
2034 in->disable_update = 0;
2035 in->mark = 0;
2036 in->need_push = 1;
2037 in->is_password = 0;
2039 strcpy (in->buffer, def_text);
2040 in->point = str_length (in->buffer);
2041 in->charpoint = 0;
2042 return in;
2045 /* Listbox widget */
2047 /* Should draw the scrollbar, but currently draws only
2048 * indications that there is more information
2050 static int listbox_cdiff (WLEntry *s, WLEntry *e);
2052 static void
2053 listbox_drawscroll (WListbox *l)
2055 int line = 0;
2056 int i, top;
2057 int max_line = l->widget.lines - 1;
2059 /* Are we at the top? */
2060 widget_move (&l->widget, 0, l->widget.cols);
2061 if (l->list == l->top)
2062 tty_print_one_vline ();
2063 else
2064 tty_print_char ('^');
2066 /* Are we at the bottom? */
2067 widget_move (&l->widget, max_line, l->widget.cols);
2068 top = listbox_cdiff (l->list, l->top);
2069 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
2070 tty_print_one_vline ();
2071 else
2072 tty_print_char ('v');
2074 /* Now draw the nice relative pointer */
2075 if (l->count != 0)
2076 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
2078 for (i = 1; i < max_line; i++){
2079 widget_move (&l->widget, i, l->widget.cols);
2080 if (i != line)
2081 tty_print_one_vline ();
2082 else
2083 tty_print_char ('*');
2087 static void
2088 listbox_draw (WListbox *l, gboolean focused)
2090 const Dlg_head *h = l->widget.parent;
2091 const int normalc = DLG_NORMALC (h);
2092 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2094 WLEntry *e;
2095 int i;
2096 int sel_line = -1;
2097 const char *text;
2099 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2100 /* Display the entry */
2101 if (e == l->current && sel_line == -1) {
2102 sel_line = i;
2103 tty_setcolor (selc);
2104 } else
2105 tty_setcolor (normalc);
2107 widget_move (&l->widget, i, 1);
2109 if ((i > 0 && e == l->list) || !l->list)
2110 text = "";
2111 else {
2112 text = e->text;
2113 e = e->next;
2115 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2117 l->cursor_y = sel_line;
2119 if (l->scrollbar && l->count > l->widget.lines) {
2120 tty_setcolor (normalc);
2121 listbox_drawscroll (l);
2125 /* Returns the number of items between s and e,
2126 must be on the same linked list */
2127 static int
2128 listbox_cdiff (WLEntry *s, WLEntry *e)
2130 int count;
2132 for (count = 0; s != e; count++)
2133 s = s->next;
2134 return count;
2137 static WLEntry *
2138 listbox_check_hotkey (WListbox *l, int key)
2140 int i;
2141 WLEntry *e;
2143 i = 0;
2144 e = l->list;
2145 if (!e)
2146 return NULL;
2148 while (1) {
2150 /* If we didn't find anything, return */
2151 if (i && e == l->list)
2152 return NULL;
2154 if (e->hotkey == key)
2155 return e;
2157 i++;
2158 e = e->next;
2162 /* Selects the last entry and scrolls the list to the bottom */
2163 void
2164 listbox_select_last (WListbox *l)
2166 unsigned int i;
2167 l->current = l->top = l->list->prev;
2168 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2169 l->top = l->top->prev;
2170 l->pos = l->count - 1;
2173 /* Selects the first entry and scrolls the list to the top */
2174 void
2175 listbox_select_first (WListbox *l)
2177 l->current = l->top = l->list;
2178 l->pos = 0;
2181 void
2182 listbox_remove_list (WListbox *l)
2184 WLEntry *p, *q;
2186 if (!l->count)
2187 return;
2189 p = l->list;
2191 while (l->count--) {
2192 q = p->next;
2193 g_free (p->text);
2194 g_free (p);
2195 p = q;
2197 l->pos = l->count = 0;
2198 l->list = l->top = l->current = 0;
2202 * bor 30.10.96: added force flag to remove *last* entry as well
2203 * bor 30.10.96: corrected selection bug if last entry was removed
2206 void
2207 listbox_remove_current (WListbox *l, int force)
2209 WLEntry *p;
2211 /* Ok, note: this won't allow for emtpy lists */
2212 if (!force && (!l->count || l->count == 1))
2213 return;
2215 l->count--;
2216 p = l->current;
2218 if (l->count) {
2219 l->current->next->prev = l->current->prev;
2220 l->current->prev->next = l->current->next;
2221 if (p->next == l->list) {
2222 l->current = p->prev;
2223 l->pos--;
2225 else
2226 l->current = p->next;
2228 if (p == l->list)
2229 l->list = l->top = p->next;
2230 } else {
2231 l->pos = 0;
2232 l->list = l->top = l->current = 0;
2235 g_free (p->text);
2236 g_free (p);
2239 /* Makes *e the selected entry (sets current and pos) */
2240 void
2241 listbox_select_entry (WListbox *l, WLEntry *dest)
2243 WLEntry *e;
2244 int pos;
2245 int top_seen;
2247 top_seen = 0;
2249 /* Special case */
2250 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2252 if (e == l->top)
2253 top_seen = 1;
2255 if (e == dest){
2256 l->current = e;
2257 if (top_seen){
2258 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2259 l->top = l->top->next;
2260 } else {
2261 l->top = l->current;
2263 l->pos = pos;
2264 return;
2267 /* If we are unable to find it, set decent values */
2268 l->current = l->top = l->list;
2269 l->pos = 0;
2272 /* Selects from base the pos element */
2273 static WLEntry *
2274 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2276 WLEntry *last = l->list->prev;
2278 if (base == last)
2279 return last;
2280 while (pos--){
2281 base = base->next;
2282 if (base == last)
2283 break;
2285 return base;
2288 static void
2289 listbox_back (WListbox *l)
2291 if (l->pos != 0)
2292 listbox_select_entry (l, l->current->prev);
2293 else
2294 listbox_select_last (l);
2297 /* Return MSG_HANDLED if we want a redraw */
2298 static cb_ret_t
2299 listbox_key (WListbox *l, int key)
2301 int i;
2303 cb_ret_t j = MSG_NOT_HANDLED;
2305 /* focus on listbox item N by '0'..'9' keys */
2306 if (key >= '0' && key <= '9') {
2307 int oldpos = l->pos;
2308 listbox_select_by_number(l, key - '0');
2310 /* need scroll to item? */
2311 if (abs(oldpos - l->pos) > l->widget.lines)
2312 l->top = l->current;
2314 return MSG_HANDLED;
2317 if (!l->list)
2318 return MSG_NOT_HANDLED;
2320 switch (key){
2321 case KEY_HOME:
2322 case KEY_A1:
2323 case ALT ('<'):
2324 listbox_select_first (l);
2325 return MSG_HANDLED;
2327 case KEY_END:
2328 case KEY_C1:
2329 case ALT ('>'):
2330 listbox_select_last (l);
2331 return MSG_HANDLED;
2333 case XCTRL('p'):
2334 case KEY_UP:
2335 listbox_back (l);
2336 return MSG_HANDLED;
2338 case XCTRL('n'):
2339 case KEY_DOWN:
2340 listbox_fwd (l);
2341 return MSG_HANDLED;
2343 case KEY_NPAGE:
2344 case XCTRL('v'):
2345 for (i = 0; ((i < l->widget.lines - 1)
2346 && (l->current != l->list->prev)); i++) {
2347 listbox_fwd (l);
2348 j = MSG_HANDLED;
2350 return j;
2352 case KEY_PPAGE:
2353 case ALT('v'):
2354 for (i = 0; ((i < l->widget.lines - 1)
2355 && (l->current != l->list)); i++) {
2356 listbox_back (l);
2357 j = MSG_HANDLED;
2359 return j;
2361 return MSG_NOT_HANDLED;
2364 static void
2365 listbox_destroy (WListbox *l)
2367 WLEntry *n, *p = l->list;
2368 int i;
2370 for (i = 0; i < l->count; i++){
2371 n = p->next;
2372 g_free (p->text);
2373 g_free (p);
2374 p = n;
2378 static cb_ret_t
2379 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2381 WListbox *l = (WListbox *) w;
2382 Dlg_head *h = l->widget.parent;
2383 WLEntry *e;
2384 cb_ret_t ret_code;
2386 switch (msg) {
2387 case WIDGET_INIT:
2388 return MSG_HANDLED;
2390 case WIDGET_HOTKEY:
2391 e = listbox_check_hotkey (l, parm);
2392 if (e != NULL) {
2393 int action;
2395 listbox_select_entry (l, e);
2396 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2398 if (l->cback)
2399 action = (*l->cback) (l);
2400 else
2401 action = LISTBOX_DONE;
2403 if (action == LISTBOX_DONE) {
2404 h->ret_value = B_ENTER;
2405 dlg_stop (h);
2407 return MSG_HANDLED;
2409 return MSG_NOT_HANDLED;
2411 case WIDGET_KEY:
2412 ret_code = listbox_key (l, parm);
2413 if (ret_code != MSG_NOT_HANDLED) {
2414 listbox_draw (l, TRUE);
2415 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2417 return ret_code;
2419 case WIDGET_CURSOR:
2420 widget_move (&l->widget, l->cursor_y, 0);
2421 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2422 return MSG_HANDLED;
2424 case WIDGET_FOCUS:
2425 case WIDGET_UNFOCUS:
2426 case WIDGET_DRAW:
2427 listbox_draw (l, msg != WIDGET_UNFOCUS);
2428 return MSG_HANDLED;
2430 case WIDGET_DESTROY:
2431 listbox_destroy (l);
2432 return MSG_HANDLED;
2434 case WIDGET_RESIZED:
2435 return MSG_HANDLED;
2437 default:
2438 return default_proc (msg, parm);
2442 static int
2443 listbox_event (Gpm_Event *event, void *data)
2445 WListbox *l = data;
2446 int i;
2448 Dlg_head *h = l->widget.parent;
2450 /* Single click */
2451 if (event->type & GPM_DOWN)
2452 dlg_select_widget (l);
2454 if (l->list == NULL)
2455 return MOU_NORMAL;
2457 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2458 int ret = MOU_REPEAT;
2460 if (event->x < 0 || event->x > l->widget.cols)
2461 return ret;
2463 if (event->y < 1)
2464 for (i = -event->y; i >= 0; i--)
2465 listbox_back (l);
2466 else if (event->y > l->widget.lines)
2467 for (i = event->y - l->widget.lines; i > 0; i--)
2468 listbox_fwd (l);
2469 else if (event->buttons & GPM_B_UP) {
2470 listbox_back (l);
2471 ret = MOU_NORMAL;
2472 } else if (event->buttons & GPM_B_DOWN) {
2473 listbox_fwd (l);
2474 ret = MOU_NORMAL;
2475 } else
2476 listbox_select_entry (l,
2477 listbox_select_pos (l, l->top,
2478 event->y - 1));
2480 /* We need to refresh ourselves since the dialog manager doesn't */
2481 /* know about this event */
2482 listbox_draw (l, TRUE);
2483 return ret;
2486 /* Double click */
2487 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2488 int action;
2490 if (event->x < 0 || event->x >= l->widget.cols
2491 || event->y < 1 || event->y > l->widget.lines)
2492 return MOU_NORMAL;
2494 dlg_select_widget (l);
2495 listbox_select_entry (l,
2496 listbox_select_pos (l, l->top,
2497 event->y - 1));
2499 if (l->cback)
2500 action = (*l->cback) (l);
2501 else
2502 action = LISTBOX_DONE;
2504 if (action == LISTBOX_DONE) {
2505 h->ret_value = B_ENTER;
2506 dlg_stop (h);
2507 return MOU_NORMAL;
2510 return MOU_NORMAL;
2513 WListbox *
2514 listbox_new (int y, int x, int height, int width, lcback callback)
2516 WListbox *l = g_new (WListbox, 1);
2518 if (height <= 0)
2519 height = 1;
2521 init_widget (&l->widget, y, x, height, width,
2522 listbox_callback, listbox_event);
2524 l->list = l->top = l->current = 0;
2525 l->pos = 0;
2526 l->count = 0;
2527 l->cback = callback;
2528 l->allow_duplicates = 1;
2529 l->scrollbar = !tty_is_slow ();
2530 widget_want_hotkey (l->widget, 1);
2532 return l;
2535 /* Listbox item adding function. They still lack a lot of functionality */
2536 /* any takers? */
2537 /* 1.11.96 bor: added pos argument to control placement of new entry */
2538 static void
2539 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2541 if (!l->list){
2542 l->list = e;
2543 l->top = e;
2544 l->current = e;
2545 e->next = l->list;
2546 e->prev = l->list;
2547 } else if (pos == LISTBOX_APPEND_AT_END) {
2548 e->next = l->list;
2549 e->prev = l->list->prev;
2550 l->list->prev->next = e;
2551 l->list->prev = e;
2552 } else if (pos == LISTBOX_APPEND_BEFORE){
2553 e->next = l->current;
2554 e->prev = l->current->prev;
2555 l->current->prev->next = e;
2556 l->current->prev = e;
2557 if (l->list == l->current) { /* move list one position down */
2558 l->list = e;
2559 l->top = e;
2561 } else if (pos == LISTBOX_APPEND_AFTER) {
2562 e->prev = l->current;
2563 e->next = l->current->next;
2564 l->current->next->prev = e;
2565 l->current->next = e;
2566 } else if (pos == LISTBOX_APPEND_SORTED) {
2567 WLEntry *w = l->list;
2569 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2570 w = w->next;
2571 if (w->next == l->list) {
2572 e->prev = w;
2573 e->next = l->list;
2574 w->next = e;
2575 l->list->prev = e;
2576 } else {
2577 e->next = w;
2578 e->prev = w->prev;
2579 w->prev->next = e;
2580 w->prev = e;
2583 l->count++;
2586 char *
2587 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2588 const char *text, void *data)
2590 WLEntry *entry;
2592 if (!l)
2593 return NULL;
2595 if (!l->allow_duplicates)
2596 if (listbox_search_text (l, text))
2597 return NULL;
2599 entry = g_new (WLEntry, 1);
2600 entry->text = g_strdup (text);
2601 entry->data = data;
2602 entry->hotkey = hotkey;
2604 listbox_append_item (l, entry, pos);
2606 return entry->text;
2609 /* Selects the nth entry in the listbox */
2610 void
2611 listbox_select_by_number (WListbox *l, int n)
2613 if (l->list != NULL)
2614 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2617 WLEntry *
2618 listbox_search_text (WListbox *l, const char *text)
2620 WLEntry *e;
2622 e = l->list;
2623 if (!e)
2624 return NULL;
2626 do {
2627 if(!strcmp (e->text, text))
2628 return e;
2629 e = e->next;
2630 } while (e!=l->list);
2632 return NULL;
2635 /* Returns the current string text as well as the associated extra data */
2636 void
2637 listbox_get_current (WListbox *l, char **string, char **extra)
2639 if (!l->current){
2640 *string = 0;
2641 *extra = 0;
2643 if (string && l->current)
2644 *string = l->current->text;
2645 if (extra && l->current)
2646 *extra = l->current->data;
2649 /* returns TRUE if a function has been called, FALSE otherwise. */
2650 static gboolean
2651 buttonbar_call (WButtonBar *bb, int i)
2653 cb_ret_t ret = MSG_NOT_HANDLED;
2655 if (bb != NULL)
2656 ret = bb->widget.parent->callback (bb->widget.parent,
2657 (Widget *) bb, DLG_ACTION,
2658 bb->labels[i].command,
2659 bb->labels[i].receiver);
2660 return ret;
2663 /* calculate width of one button, width is never lesser than 7 */
2664 static int
2665 buttonbat_get_button_width (void)
2667 int result = COLS / BUTTONBAR_LABELS_NUM;
2668 return (result >= 7) ? result : 7;
2671 static cb_ret_t
2672 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2674 WButtonBar *bb = (WButtonBar *) w;
2675 int i;
2676 const char *text;
2678 switch (msg) {
2679 case WIDGET_FOCUS:
2680 return MSG_NOT_HANDLED;
2682 case WIDGET_HOTKEY:
2683 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2684 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2685 return MSG_HANDLED;
2686 return MSG_NOT_HANDLED;
2688 case WIDGET_DRAW:
2689 if (bb->visible) {
2690 int offset = 0;
2691 int count_free_positions;
2693 widget_move (&bb->widget, 0, 0);
2694 tty_setcolor (DEFAULT_COLOR);
2695 bb->btn_width = buttonbat_get_button_width ();
2696 tty_printf ("%-*s", bb->widget.cols, "");
2697 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2699 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2700 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2701 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2702 tty_printf ("%2d", i + 1);
2703 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2704 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2705 tty_print_string (str_fit_to_term (
2706 text,
2707 bb->btn_width - 2 + (int)(offset < count_free_positions),
2708 J_LEFT_FIT));
2710 if (count_free_positions != 0 && offset < count_free_positions)
2711 offset++;
2714 return MSG_HANDLED;
2716 case WIDGET_DESTROY:
2717 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2718 g_free (bb->labels[i].text);
2719 return MSG_HANDLED;
2721 default:
2722 return default_proc (msg, parm);
2726 static int
2727 buttonbar_event (Gpm_Event *event, void *data)
2729 WButtonBar *bb = data;
2730 int button;
2732 if (!(event->type & GPM_UP))
2733 return MOU_NORMAL;
2734 if (event->y == 2)
2735 return MOU_NORMAL;
2736 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2737 if (button < BUTTONBAR_LABELS_NUM)
2738 buttonbar_call (bb, button);
2739 return MOU_NORMAL;
2742 WButtonBar *
2743 buttonbar_new (gboolean visible)
2745 WButtonBar *bb;
2747 bb = g_new0 (WButtonBar, 1);
2749 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2750 buttonbar_callback, buttonbar_event);
2751 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2752 bb->visible = visible;
2753 widget_want_hotkey (bb->widget, 1);
2754 widget_want_cursor (bb->widget, 0);
2755 bb->btn_width = buttonbat_get_button_width ();
2757 return bb;
2760 static void
2761 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2763 g_free (bb->labels[lc_index - 1].text);
2764 bb->labels[lc_index - 1].text = g_strdup (text);
2767 /* Find ButtonBar widget in the dialog */
2768 WButtonBar *
2769 find_buttonbar (const Dlg_head *h)
2771 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2774 void
2775 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2776 const struct global_keymap_t *keymap, const Widget *receiver)
2778 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2779 unsigned long command = CK_Ignore_Key;
2781 if (keymap != NULL)
2782 command = lookup_keymap_command (keymap, KEY_F (idx));
2784 if ((text == NULL) || (text[0] == '\0'))
2785 set_label_text (bb, idx, "");
2786 else
2787 set_label_text (bb, idx, text);
2789 bb->labels[idx - 1].command = command;
2790 bb->labels[idx - 1].receiver = (Widget *) receiver;
2794 void
2795 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2797 bb->visible = visible;
2800 void
2801 buttonbar_redraw (WButtonBar *bb)
2803 if (bb != NULL)
2804 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2807 static cb_ret_t
2808 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2810 WGroupbox *g = (WGroupbox *) w;
2812 switch (msg) {
2813 case WIDGET_INIT:
2814 return MSG_HANDLED;
2816 case WIDGET_FOCUS:
2817 return MSG_NOT_HANDLED;
2819 case WIDGET_DRAW:
2820 tty_setcolor (COLOR_NORMAL);
2821 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2822 g->widget.x - g->widget.parent->x, g->widget.lines,
2823 g->widget.cols);
2825 tty_setcolor (COLOR_HOT_NORMAL);
2826 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2827 g->widget.x - g->widget.parent->x + 1);
2828 tty_print_string (g->title);
2829 return MSG_HANDLED;
2831 case WIDGET_DESTROY:
2832 g_free (g->title);
2833 return MSG_HANDLED;
2835 default:
2836 return default_proc (msg, parm);
2840 WGroupbox *
2841 groupbox_new (int y, int x, int height, int width, const char *title)
2843 WGroupbox *g = g_new (WGroupbox, 1);
2845 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2847 g->widget.options &= ~W_WANT_CURSOR;
2848 widget_want_hotkey (g->widget, 0);
2850 /* Strip existing spaces, add one space before and after the title */
2851 if (title) {
2852 char *t;
2853 t = g_strstrip (g_strdup (title));
2854 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2855 g_free (t);
2858 return g;