Merge branch '1708_ftp_permission'
[midnight-commander.git] / src / widget.c
blob16f4ec87582ab802eb7903e16277e43233e86724
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 /** \file widget.c
29 * \brief Source: widgets
32 #include <config.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
43 #include "global.h"
45 #include "../src/tty/tty.h"
46 #include "../src/skin/skin.h"
47 #include "../src/tty/mouse.h"
48 #include "../src/tty/key.h" /* XCTRL and ALT macros */
50 #include "../src/mcconfig/mcconfig.h" /* for history loading and saving */
52 #include "dialog.h"
53 #include "widget.h"
54 #include "wtools.h"
55 #include "strutil.h"
57 #include "cmddef.h" /* CK_ cmd name const */
58 #include "keybind.h" /* global_keymap_t */
59 #include "fileloc.h"
60 #include "panel.h" /* current_panel */
62 const global_keymap_t *input_map;
64 static void
65 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
67 Dlg_head *h = w->parent;
69 tty_setcolor (hotkey
70 ? (focused
71 ? DLG_HOT_FOCUSC (h)
72 : DLG_HOT_NORMALC (h))
73 : (focused
74 ? DLG_FOCUSC (h)
75 : DLG_NORMALC (h)));
78 struct hotkey_t
79 parse_hotkey (const char *text)
81 struct hotkey_t result;
82 const char *cp, *p;
84 /* search for '&', that is not on the of text */
85 cp = strchr (text, '&');
86 if (cp != NULL && cp[1] != '\0') {
87 result.start = g_strndup (text, cp - text);
89 /* skip '&' */
90 cp++;
91 p = str_cget_next_char (cp);
92 result.hotkey = g_strndup (cp, p - cp);
94 cp = p;
95 result.end = g_strdup (cp);
96 } else {
97 result.start = g_strdup (text);
98 result.hotkey = NULL;
99 result.end = NULL;
102 return result;
104 void
105 release_hotkey (const struct hotkey_t hotkey)
107 g_free (hotkey.start);
108 g_free (hotkey.hotkey);
109 g_free (hotkey.end);
113 hotkey_width (const struct hotkey_t hotkey)
115 int result;
117 result = str_term_width1 (hotkey.start);
118 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
119 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
120 return result;
123 static void
124 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
126 widget_selectcolor (w, focused, FALSE);
127 tty_print_string (hotkey.start);
129 if (hotkey.hotkey != NULL) {
130 widget_selectcolor (w, focused, TRUE);
131 tty_print_string (hotkey.hotkey);
132 widget_selectcolor (w, focused, FALSE);
135 if (hotkey.end != NULL)
136 tty_print_string (hotkey.end);
140 /* Default callback for widgets */
141 cb_ret_t
142 default_proc (widget_msg_t msg, int parm)
144 (void) parm;
146 switch (msg) {
147 case WIDGET_INIT:
148 case WIDGET_FOCUS:
149 case WIDGET_UNFOCUS:
150 case WIDGET_DRAW:
151 case WIDGET_DESTROY:
152 case WIDGET_CURSOR:
153 case WIDGET_IDLE:
154 return MSG_HANDLED;
156 default:
157 return MSG_NOT_HANDLED;
161 static int button_event (Gpm_Event *event, void *);
163 int quote = 0;
165 static cb_ret_t
166 button_callback (Widget *w, widget_msg_t msg, int parm)
168 WButton *b = (WButton *) w;
169 int stop = 0;
170 int off = 0;
171 Dlg_head *h = b->widget.parent;
173 switch (msg) {
174 case WIDGET_HOTKEY:
176 * Don't let the default button steal Enter from the current
177 * button. This is a workaround for the flawed event model
178 * when hotkeys are sent to all widgets before the key is
179 * handled by the current widget.
181 if (parm == '\n' && h->current == &b->widget) {
182 button_callback (w, WIDGET_KEY, ' ');
183 return MSG_HANDLED;
186 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
187 button_callback (w, WIDGET_KEY, ' ');
188 return MSG_HANDLED;
191 if (b->text.hotkey != NULL) {
192 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
193 g_ascii_tolower ((gchar)parm)) {
194 button_callback (w, WIDGET_KEY, ' ');
195 return MSG_HANDLED;
198 return MSG_NOT_HANDLED;
200 case WIDGET_KEY:
201 if (parm != ' ' && parm != '\n')
202 return MSG_NOT_HANDLED;
204 if (b->callback)
205 stop = (*b->callback) (b->action);
206 if (!b->callback || stop) {
207 h->ret_value = b->action;
208 dlg_stop (h);
210 return MSG_HANDLED;
212 case WIDGET_CURSOR:
213 switch (b->flags) {
214 case DEFPUSH_BUTTON:
215 off = 3;
216 break;
217 case NORMAL_BUTTON:
218 off = 2;
219 break;
220 case NARROW_BUTTON:
221 off = 1;
222 break;
223 case HIDDEN_BUTTON:
224 default:
225 off = 0;
226 break;
228 widget_move (&b->widget, 0, b->hotpos + off);
229 return MSG_HANDLED;
231 case WIDGET_UNFOCUS:
232 case WIDGET_FOCUS:
233 case WIDGET_DRAW:
234 if (msg == WIDGET_UNFOCUS)
235 b->selected = 0;
236 else if (msg == WIDGET_FOCUS)
237 b->selected = 1;
239 widget_selectcolor (w, b->selected, FALSE);
240 widget_move (w, 0, 0);
242 switch (b->flags) {
243 case DEFPUSH_BUTTON:
244 tty_print_string ("[< ");
245 break;
246 case NORMAL_BUTTON:
247 tty_print_string ("[ ");
248 break;
249 case NARROW_BUTTON:
250 tty_print_string ("[");
251 break;
252 case HIDDEN_BUTTON:
253 default:
254 return MSG_HANDLED;
257 draw_hotkey (w, b->text, b->selected);
259 switch (b->flags) {
260 case DEFPUSH_BUTTON:
261 tty_print_string (" >]");
262 break;
263 case NORMAL_BUTTON:
264 tty_print_string (" ]");
265 break;
266 case NARROW_BUTTON:
267 tty_print_string ("]");
268 break;
270 return MSG_HANDLED;
272 case WIDGET_DESTROY:
273 release_hotkey (b->text);
274 return MSG_HANDLED;
276 default:
277 return default_proc (msg, parm);
281 static int
282 button_event (Gpm_Event *event, void *data)
284 WButton *b = data;
286 if (event->type & (GPM_DOWN|GPM_UP)){
287 Dlg_head *h=b->widget.parent;
288 dlg_select_widget (b);
289 if (event->type & GPM_UP){
290 button_callback ((Widget *) data, WIDGET_KEY, ' ');
291 (*h->callback) (h, DLG_POST_KEY, ' ');
292 return MOU_NORMAL;
295 return MOU_NORMAL;
299 button_get_len (const WButton *b)
301 int ret = hotkey_width (b->text);
302 switch (b->flags) {
303 case DEFPUSH_BUTTON:
304 ret += 6;
305 break;
306 case NORMAL_BUTTON:
307 ret += 4;
308 break;
309 case NARROW_BUTTON:
310 ret += 2;
311 break;
312 case HIDDEN_BUTTON:
313 default:
314 return 0;
316 return ret;
319 WButton *
320 button_new (int y, int x, int action, int flags, const char *text,
321 bcback callback)
323 WButton *b = g_new (WButton, 1);
325 b->action = action;
326 b->flags = flags;
327 b->text = parse_hotkey (text);
329 init_widget (&b->widget, y, x, 1, button_get_len (b),
330 button_callback, button_event);
332 b->selected = 0;
333 b->callback = callback;
334 widget_want_hotkey (b->widget, 1);
335 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
337 return b;
340 const char *
341 button_get_text (const WButton *b)
343 if (b->text.hotkey != NULL)
344 return g_strconcat (b->text.start, "&", b->text.hotkey,
345 b->text.end, NULL);
346 else
347 return g_strdup (b->text.start);
350 void
351 button_set_text (WButton *b, const char *text)
353 release_hotkey (b->text);
354 b->text = parse_hotkey (text);
355 b->widget.cols = button_get_len (b);
356 dlg_redraw (b->widget.parent);
360 /* Radio button widget */
361 static int radio_event (Gpm_Event *event, void *);
363 static cb_ret_t
364 radio_callback (Widget *w, widget_msg_t msg, int parm)
366 WRadio *r = (WRadio *) w;
367 int i;
368 Dlg_head *h = r->widget.parent;
370 switch (msg) {
371 case WIDGET_HOTKEY:
373 int lp = g_ascii_tolower ((gchar)parm);
375 for (i = 0; i < r->count; i++) {
376 if (r->texts[i].hotkey != NULL) {
377 int c = g_ascii_tolower ((gchar)r->texts[i].hotkey[0]);
379 if (c != lp)
380 continue;
381 r->pos = i;
383 /* Take action */
384 radio_callback (w, WIDGET_KEY, ' ');
385 return MSG_HANDLED;
389 return MSG_NOT_HANDLED;
391 case WIDGET_KEY:
392 switch (parm) {
393 case ' ':
394 r->sel = r->pos;
395 (*h->callback) (h, DLG_ACTION, 0);
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, DLG_ACTION, 0);
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, DLG_POST_KEY, ' ');
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, DLG_ACTION, 0);
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, DLG_POST_KEY, ' ');
562 return MOU_NORMAL;
565 return MOU_NORMAL;
568 WCheck *
569 check_new (int y, int x, int state, const char *text)
571 WCheck *c = g_new (WCheck, 1);
573 c->text = parse_hotkey (text);
575 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
576 check_callback, check_event);
577 c->state = state ? C_BOOL : 0;
578 widget_want_hotkey (c->widget, 1);
580 return c;
583 static gboolean
584 save_text_to_clip_file (const char *text)
586 int file;
587 char *fname = NULL;
589 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
590 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
591 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
592 g_free (fname);
594 if (file == -1)
595 return FALSE;
597 mc_write (file, (char *) text, strlen (text));
598 mc_close (file);
599 return TRUE;
602 static gboolean
603 load_text_from_clip_file (char **text)
605 char buf[BUF_LARGE];
606 FILE *f;
607 char *fname = NULL;
608 gboolean first = TRUE;
610 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
611 f = fopen (fname, "r");
612 g_free (fname);
614 if (f == NULL)
615 return FALSE;
617 *text = NULL;
619 while (fgets (buf, sizeof (buf), f)) {
620 size_t len;
622 len = strlen (buf);
623 if ( len > 0 ) {
624 if (buf[len - 1] == '\n')
625 buf[len - 1] = '\0';
627 if (first) {
628 first = FALSE;
629 *text = g_strdup (buf);
630 } else {
631 /* remove \n on EOL */
632 char *tmp;
634 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
635 g_free (*text);
636 *text = tmp;
641 fclose (f);
643 return (*text != NULL);
646 static gboolean
647 panel_save_curent_file_to_clip_file (void)
649 gboolean res;
651 if (current_panel->marked == 0)
652 res = save_text_to_clip_file (selection (current_panel)->fname);
653 else {
654 int i;
655 gboolean first = TRUE;
656 char *flist = NULL;
658 for (i = 0; i < current_panel->count; i++)
659 if (current_panel->dir.list[i].f.marked != 0) { /* Skip the unmarked ones */
660 if (first) {
661 flist = g_strdup (current_panel->dir.list[i].fname);
662 first = FALSE;
663 } else {
664 /* Add empty lines after the file */
665 char *tmp;
667 tmp = g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
668 g_free (flist);
669 flist = tmp;
673 if (flist != NULL) {
674 res = save_text_to_clip_file (flist);
675 g_free (flist);
678 return res;
682 /* Label widget */
684 static cb_ret_t
685 label_callback (Widget *w, widget_msg_t msg, int parm)
687 WLabel *l = (WLabel *) w;
688 Dlg_head *h = l->widget.parent;
690 switch (msg) {
691 case WIDGET_INIT:
692 return MSG_HANDLED;
694 /* We don't want to get the focus */
695 case WIDGET_FOCUS:
696 return MSG_NOT_HANDLED;
698 case WIDGET_DRAW:
700 char *p = l->text, *q, c = 0;
701 int y = 0;
703 if (!l->text)
704 return MSG_HANDLED;
706 if (l->transparent)
707 tty_setcolor (DEFAULT_COLOR);
708 else
709 tty_setcolor (DLG_NORMALC (h));
711 for (;;) {
712 q = strchr (p, '\n');
713 if (q != NULL) {
714 c = q[0];
715 q[0] = '\0';
718 widget_move (&l->widget, y, 0);
719 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
721 if (q == NULL)
722 break;
723 q[0] = c;
724 p = q + 1;
725 y++;
727 return MSG_HANDLED;
730 case WIDGET_DESTROY:
731 g_free (l->text);
732 return MSG_HANDLED;
734 default:
735 return default_proc (msg, parm);
739 void
740 label_set_text (WLabel *label, const char *text)
742 int newcols = label->widget.cols;
743 int newlines;
745 if (label->text && text && !strcmp (label->text, text))
746 return; /* Flickering is not nice */
748 g_free (label->text);
750 if (text != NULL) {
751 label->text = g_strdup (text);
752 if (label->auto_adjust_cols) {
753 str_msg_term_size (text, &newlines, &newcols);
754 if (newcols > label->widget.cols)
755 label->widget.cols = newcols;
756 if (newlines > label->widget.lines)
757 label->widget.lines = newlines;
759 } else label->text = NULL;
761 if (label->widget.parent)
762 label_callback ((Widget *) label, WIDGET_DRAW, 0);
764 if (newcols < label->widget.cols)
765 label->widget.cols = newcols;
768 WLabel *
769 label_new (int y, int x, const char *text)
771 WLabel *l;
772 int cols = 1;
773 int lines = 1;
775 if (text != NULL)
776 str_msg_term_size (text, &lines, &cols);
778 l = g_new (WLabel, 1);
779 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
780 l->text = (text != NULL) ? g_strdup (text) : NULL;
781 l->auto_adjust_cols = 1;
782 l->transparent = 0;
783 widget_want_cursor (l->widget, 0);
784 return l;
788 /* Gauge widget (progress indicator) */
789 /* Currently width is hardcoded here for text mode */
790 #define gauge_len 47
792 static cb_ret_t
793 gauge_callback (Widget *w, widget_msg_t msg, int parm)
795 WGauge *g = (WGauge *) w;
796 Dlg_head *h = g->widget.parent;
798 if (msg == WIDGET_INIT)
799 return MSG_HANDLED;
801 /* We don't want to get the focus */
802 if (msg == WIDGET_FOCUS)
803 return MSG_NOT_HANDLED;
805 if (msg == WIDGET_DRAW){
806 widget_move (&g->widget, 0, 0);
807 tty_setcolor (DLG_NORMALC (h));
808 if (!g->shown)
809 tty_printf ("%*s", gauge_len, "");
810 else {
811 int percentage, columns;
812 long total = g->max, done = g->current;
814 if (total <= 0 || done < 0) {
815 done = 0;
816 total = 100;
818 if (done > total)
819 done = total;
820 while (total > 65535) {
821 total /= 256;
822 done /= 256;
824 percentage = (200 * done / total + 1) / 2;
825 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
826 tty_print_char ('[');
827 tty_setcolor (GAUGE_COLOR);
828 tty_printf ("%*s", (int) columns, "");
829 tty_setcolor (DLG_NORMALC (h));
830 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
832 return MSG_HANDLED;
835 return default_proc (msg, parm);
838 void
839 gauge_set_value (WGauge *g, int max, int current)
841 if (g->current == current && g->max == max)
842 return; /* Do not flicker */
843 if (max == 0)
844 max = 1; /* I do not like division by zero :) */
846 g->current = current;
847 g->max = max;
848 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
851 void
852 gauge_show (WGauge *g, int shown)
854 if (g->shown == shown)
855 return;
856 g->shown = shown;
857 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
860 WGauge *
861 gauge_new (int y, int x, int shown, int max, int current)
863 WGauge *g = g_new (WGauge, 1);
865 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
866 g->shown = shown;
867 if (max == 0)
868 max = 1; /* I do not like division by zero :) */
869 g->max = max;
870 g->current = current;
871 widget_want_cursor (g->widget, 0);
872 return g;
876 /* Input widget */
878 /* {{{ history button */
880 #define LARGE_HISTORY_BUTTON 1
882 #ifdef LARGE_HISTORY_BUTTON
883 # define HISTORY_BUTTON_WIDTH 3
884 #else
885 # define HISTORY_BUTTON_WIDTH 1
886 #endif
888 #define should_show_history_button(in) \
889 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
891 static void draw_history_button (WInput * in)
893 char c;
894 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
895 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
896 #ifdef LARGE_HISTORY_BUTTON
898 Dlg_head *h;
899 h = in->widget.parent;
900 tty_setcolor (NORMAL_COLOR);
901 tty_print_string ("[ ]");
902 /* Too distracting: tty_setcolor (MARKED_COLOR); */
903 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
904 tty_print_char (c);
906 #else
907 tty_setcolor (MARKED_COLOR);
908 tty_print_char (c);
909 #endif
912 /* }}} history button */
915 /* Input widgets now have a global kill ring */
916 /* Pointer to killed data */
917 static char *kill_buffer = 0;
919 void
920 update_input (WInput *in, int clear_first)
922 int has_history = 0;
923 int i;
924 int buf_len = str_length (in->buffer);
925 const char *cp;
926 int pw;
928 if (should_show_history_button (in))
929 has_history = HISTORY_BUTTON_WIDTH;
931 if (in->disable_update)
932 return;
934 pw = str_term_width2 (in->buffer, in->point);
936 /* Make the point visible */
937 if ((pw < in->term_first_shown) ||
938 (pw >= in->term_first_shown + in->field_width - has_history)) {
940 in->term_first_shown = pw - (in->field_width / 3);
941 if (in->term_first_shown < 0)
942 in->term_first_shown = 0;
945 /* Adjust the mark */
946 if (in->mark > buf_len)
947 in->mark = buf_len;
949 if (has_history)
950 draw_history_button (in);
952 tty_setcolor (in->color);
954 widget_move (&in->widget, 0, 0);
956 if (!in->is_password) {
957 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
958 in->field_width - has_history));
959 } else {
960 cp = in->buffer;
961 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
962 if (i >= 0) {
963 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
965 if (cp[0] != '\0') str_cnext_char (&cp);
969 if (clear_first)
970 in->first = 0;
973 void
974 winput_set_origin (WInput *in, int x, int field_width)
976 in->widget.x = x;
977 in->field_width = in->widget.cols = field_width;
978 update_input (in, 0);
981 /* {{{ history saving and loading */
983 int num_history_items_recorded = 60;
986 This loads and saves the history of an input line to and from the
987 widget. It is called with the widgets history name on creation of the
988 widget, and returns the GList list. It stores histories in the file
989 ~/.mc/history in using the profile code.
991 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
992 function) then input_new assigns the default text to be the last text
993 entered, or "" if not found.
996 GList *
997 history_get (const char *input_name)
999 size_t i;
1000 GList *hist = NULL;
1001 char *profile;
1002 mc_config_t *cfg;
1003 char **keys;
1004 size_t keys_num = 0;
1005 char *this_entry;
1007 if (!num_history_items_recorded) /* this is how to disable */
1008 return NULL;
1009 if (!input_name || !*input_name)
1010 return NULL;
1012 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1013 cfg = mc_config_init (profile);
1015 /* get number of keys */
1016 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1017 g_strfreev (keys);
1019 for (i = 0; i < keys_num; i++) {
1020 char key_name[BUF_TINY];
1021 g_snprintf (key_name, sizeof (key_name), "%lu", (unsigned long)i);
1022 this_entry = mc_config_get_string (cfg, input_name, key_name, "");
1024 if (this_entry && *this_entry)
1025 hist = list_append_unique (hist, this_entry);
1028 mc_config_deinit (cfg);
1029 g_free (profile);
1031 /* return pointer to the last entry in the list */
1032 return g_list_last (hist);
1035 void
1036 history_put (const char *input_name, GList *h)
1038 int i;
1039 char *profile;
1040 mc_config_t *cfg;
1042 if (!input_name)
1043 return;
1045 if (!*input_name)
1046 return;
1048 if (!h)
1049 return;
1051 if (!num_history_items_recorded) /* this is how to disable */
1052 return;
1054 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1056 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
1057 close (i);
1059 /* Make sure the history is only readable by the user */
1060 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1061 g_free (profile);
1062 return;
1065 /* go to end of list */
1066 h = g_list_last (h);
1068 /* go back 60 places */
1069 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
1070 h = g_list_previous (h);
1072 cfg = mc_config_init(profile);
1074 if (input_name)
1075 mc_config_del_group(cfg,input_name);
1077 /* dump histories into profile */
1078 for (i = 0; h; h = g_list_next (h)) {
1079 char *text;
1081 text = (char *) h->data;
1083 /* We shouldn't have null entries, but let's be sure */
1084 if (text && *text) {
1085 char key_name[BUF_TINY];
1086 g_snprintf (key_name, sizeof (key_name), "%d", i++);
1087 mc_config_set_string(cfg,input_name, key_name, text);
1091 mc_config_save_file (cfg, NULL);
1092 mc_config_deinit(cfg);
1093 g_free (profile);
1096 /* }}} history saving and loading */
1099 /* {{{ history display */
1101 static const char *
1102 i18n_htitle (void)
1104 return _(" History ");
1107 static void
1108 listbox_fwd (WListbox *l)
1110 if (l->current != l->list->prev)
1111 listbox_select_entry (l, l->current->next);
1112 else
1113 listbox_select_first (l);
1116 typedef struct {
1117 Widget *widget;
1118 int count;
1119 size_t maxlen;
1120 } dlg_hist_data;
1122 static cb_ret_t
1123 dlg_hist_reposition (Dlg_head *dlg_head)
1125 dlg_hist_data *data;
1126 int x = 0, y, he, wi;
1128 /* guard checks */
1129 if ((dlg_head == NULL)
1130 || (dlg_head->data == NULL))
1131 return MSG_NOT_HANDLED;
1133 data = (dlg_hist_data *) dlg_head->data;
1135 y = data->widget->y;
1136 he = data->count + 2;
1138 if (he <= y || y > (LINES - 6)) {
1139 he = min (he, y - 1);
1140 y -= he;
1141 } else {
1142 y++;
1143 he = min (he, LINES - y);
1146 if (data->widget->x > 2)
1147 x = data->widget->x - 2;
1149 wi = data->maxlen + 4;
1151 if ((wi + x) > COLS) {
1152 wi = min (wi, COLS);
1153 x = COLS - wi;
1156 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1158 return MSG_HANDLED;
1161 static cb_ret_t
1162 dlg_hist_callback (Dlg_head *h, dlg_msg_t msg, int parm)
1164 switch (msg) {
1165 case DLG_RESIZE:
1166 return dlg_hist_reposition (h);
1168 default:
1169 return default_dlg_callback (h, msg, parm);
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, DLG_RESIZE, 0);
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);
1298 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1301 push_history (WInput *in, const char *text)
1303 static int i18n;
1304 /* input widget where urls with passwords are entered without any
1305 vfs prefix */
1306 static const char *password_input_fields[] = {
1307 N_(" Link to a remote machine "),
1308 N_(" FTP to machine "),
1309 N_(" SMB link to machine ")
1311 char *t;
1312 const char *p;
1313 size_t i;
1315 if (!i18n) {
1316 i18n = 1;
1317 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1318 password_input_fields[i] = _(password_input_fields[i]);
1321 for (p = text; *p == ' ' || *p == '\t'; p++);
1322 if (!*p)
1323 return 0;
1325 if (in->history) {
1326 /* Avoid duplicated entries */
1327 in->history = g_list_last (in->history);
1328 if (!strcmp ((char *) in->history->data, text))
1329 return 1;
1332 t = g_strdup (text);
1334 if (in->history_name) {
1335 p = in->history_name + 3;
1336 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1337 if (strcmp (p, password_input_fields[i]) == 0)
1338 break;
1339 if (i < ELEMENTS (password_input_fields))
1340 strip_password (t, 0);
1341 else
1342 strip_password (t, 1);
1345 in->history = list_append_unique (in->history, t);
1346 in->need_push = 0;
1348 return 2;
1351 #undef ELEMENTS
1353 /* Cleans the input line and adds the current text to the history */
1354 void
1355 new_input (WInput *in)
1357 if (in->buffer)
1358 push_history (in, in->buffer);
1359 in->need_push = 1;
1360 in->buffer[0] = '\0';
1361 in->point = 0;
1362 in->charpoint = 0;
1363 in->mark = 0;
1364 free_completions (in);
1365 update_input (in, 0);
1368 static void
1369 move_buffer_backward (WInput *in, int start, int end)
1371 int i, pos, len;
1372 int str_len = str_length (in->buffer);
1373 if (start >= str_len || end > str_len + 1) return;
1375 pos = str_offset_to_pos (in->buffer, start);
1376 len = str_offset_to_pos (in->buffer, end) - pos;
1378 for (i = pos; in->buffer[i + len - 1]; i++)
1379 in->buffer[i] = in->buffer[i + len];
1382 static cb_ret_t
1383 insert_char (WInput *in, int c_code)
1385 size_t i;
1386 int res;
1388 if (c_code == -1)
1389 return MSG_NOT_HANDLED;
1391 if (in->charpoint >= MB_LEN_MAX)
1392 return MSG_HANDLED;
1394 in->charbuf[in->charpoint] = c_code;
1395 in->charpoint++;
1397 res = str_is_valid_char (in->charbuf, in->charpoint);
1398 if (res < 0) {
1399 if (res != -2)
1400 in->charpoint = 0; /* broken multibyte char, skip */
1401 return MSG_HANDLED;
1404 in->need_push = 1;
1405 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1406 /* Expand the buffer */
1407 size_t new_length = in->current_max_size +
1408 in->field_width + in->charpoint;
1409 char *narea = g_try_renew (char, in->buffer, new_length);
1410 if (narea){
1411 in->buffer = narea;
1412 in->current_max_size = new_length;
1416 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1417 /* bytes from begin */
1418 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1419 /* move chars */
1420 size_t rest_bytes = strlen (in->buffer + ins_point);
1422 for (i = rest_bytes + 1; i > 0; i--)
1423 in->buffer[ins_point + i + in->charpoint - 1] =
1424 in->buffer[ins_point + i - 1];
1426 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1427 in->point++;
1430 in->charpoint = 0;
1431 return MSG_HANDLED;
1434 static void
1435 beginning_of_line (WInput *in)
1437 in->point = 0;
1438 in->charpoint = 0;
1441 static void
1442 end_of_line (WInput *in)
1444 in->point = str_length (in->buffer);
1445 in->charpoint = 0;
1448 static void
1449 backward_char (WInput *in)
1451 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1453 if (in->point > 0) {
1454 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1456 in->charpoint = 0;
1459 static void
1460 forward_char (WInput *in)
1462 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1463 if (act[0] != '\0') {
1464 in->point+= str_cnext_noncomb_char (&act);
1466 in->charpoint = 0;
1469 static void
1470 forward_word (WInput * in)
1472 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1474 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1475 str_cnext_char (&p);
1476 in->point++;
1478 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1479 str_cnext_char (&p);
1480 in->point++;
1484 static void
1485 backward_word (WInput *in)
1487 const char *p;
1488 const char *p_tmp;
1490 for (
1491 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1492 (p != in->buffer) && (p[0] == '\0');
1493 p-- , in->point--
1496 while (p != in->buffer) {
1497 p_tmp = p;
1498 str_cprev_char (&p);
1499 if (!str_isspace (p) && !str_ispunct (p)) {
1500 p = p_tmp;
1501 break;
1503 in->point--;
1505 while (p != in->buffer) {
1506 str_cprev_char (&p);
1507 if (str_isspace (p) || str_ispunct (p))
1508 break;
1510 in->point--;
1514 static void
1515 key_left (WInput *in)
1517 backward_char (in);
1520 static void
1521 key_ctrl_left (WInput *in)
1523 backward_word (in);
1526 static void
1527 key_right (WInput *in)
1529 forward_char (in);
1532 static void
1533 key_ctrl_right (WInput *in)
1535 forward_word (in);
1537 static void
1538 backward_delete (WInput *in)
1540 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1541 int start;
1543 if (in->point == 0)
1544 return;
1546 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1547 move_buffer_backward(in, start, in->point);
1548 in->charpoint = 0;
1549 in->need_push = 1;
1550 in->point = start;
1553 static void
1554 delete_char (WInput *in)
1556 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1557 int end = in->point;
1559 end+= str_cnext_noncomb_char (&act);
1561 move_buffer_backward(in, in->point, end);
1562 in->charpoint = 0;
1563 in->need_push = 1;
1566 static void
1567 copy_region (WInput *in, int x_first, int x_last)
1569 int first = min (x_first, x_last);
1570 int last = max (x_first, x_last);
1572 if (last == first) {
1573 /* Copy selected files to clipboard */
1574 panel_save_curent_file_to_clip_file ();
1575 return;
1578 g_free (kill_buffer);
1580 first = str_offset_to_pos (in->buffer, first);
1581 last = str_offset_to_pos (in->buffer, last);
1583 kill_buffer = g_strndup(in->buffer + first, last - first);
1584 save_text_to_clip_file (kill_buffer);
1587 static void
1588 delete_region (WInput *in, int x_first, int x_last)
1590 int first = min (x_first, x_last);
1591 int last = max (x_first, x_last);
1592 size_t len;
1594 in->point = first;
1595 in->mark = first;
1596 last = str_offset_to_pos (in->buffer, last);
1597 first = str_offset_to_pos (in->buffer, first);
1598 len = strlen (&in->buffer[last]) + 1;
1599 memmove (&in->buffer[first], &in->buffer[last], len);
1600 in->charpoint = 0;
1601 in->need_push = 1;
1604 static void
1605 kill_word (WInput *in)
1607 int old_point = in->point;
1608 int new_point;
1610 forward_word (in);
1611 new_point = in->point;
1612 in->point = old_point;
1614 copy_region (in, old_point, new_point);
1615 delete_region (in, old_point, new_point);
1616 in->need_push = 1;
1617 in->charpoint = 0;
1618 in->charpoint = 0;
1621 static void
1622 back_kill_word (WInput *in)
1624 int old_point = in->point;
1625 int new_point;
1627 backward_word (in);
1628 new_point = in->point;
1629 in->point = old_point;
1631 copy_region (in, old_point, new_point);
1632 delete_region (in, old_point, new_point);
1633 in->need_push = 1;
1636 static void
1637 set_mark (WInput *in)
1639 in->mark = in->point;
1642 static void
1643 kill_save (WInput *in)
1645 copy_region (in, in->mark, in->point);
1648 static void
1649 kill_region (WInput *in)
1651 kill_save (in);
1652 delete_region (in, in->point, in->mark);
1655 static void
1656 clear_region (WInput *in)
1658 delete_region (in, in->point, in->mark);
1661 static void
1662 yank (WInput *in)
1664 char *p;
1666 if (!kill_buffer)
1667 return;
1668 in->charpoint = 0;
1669 for (p = kill_buffer; *p; p++)
1670 insert_char (in, *p);
1671 in->charpoint = 0;
1674 static void
1675 kill_line (WInput *in)
1677 int chp = str_offset_to_pos (in->buffer, in->point);
1678 g_free (kill_buffer);
1679 kill_buffer = g_strdup (&in->buffer[chp]);
1680 in->buffer[chp] = '\0';
1681 in->charpoint = 0;
1684 static void
1685 ins_from_clip (WInput *in)
1687 char *p = NULL;
1689 if (load_text_from_clip_file (&p)) {
1690 char *pp;
1692 for (pp = p; *pp != '\0'; pp++)
1693 insert_char (in, *pp);
1695 g_free (p);
1699 void
1700 assign_text (WInput *in, const char *text)
1702 free_completions (in);
1703 g_free (in->buffer);
1704 in->buffer = g_strdup (text); /* was in->buffer->text */
1705 in->current_max_size = strlen (in->buffer) + 1;
1706 in->point = str_length (in->buffer);
1707 in->mark = 0;
1708 in->need_push = 1;
1709 in->charpoint = 0;
1712 static void
1713 hist_prev (WInput *in)
1715 if (!in->history)
1716 return;
1718 if (in->need_push) {
1719 switch (push_history (in, in->buffer)) {
1720 case 2:
1721 in->history = g_list_previous (in->history);
1722 break;
1723 case 1:
1724 if (in->history->prev)
1725 in->history = g_list_previous (in->history);
1726 break;
1727 case 0:
1728 break;
1730 } else if (in->history->prev)
1731 in->history = g_list_previous (in->history);
1732 else
1733 return;
1734 assign_text (in, (char *) in->history->data);
1735 in->need_push = 0;
1738 static void
1739 hist_next (WInput *in)
1741 if (in->need_push) {
1742 switch (push_history (in, in->buffer)) {
1743 case 2:
1744 assign_text (in, "");
1745 return;
1746 case 0:
1747 return;
1751 if (!in->history)
1752 return;
1754 if (!in->history->next) {
1755 assign_text (in, "");
1756 return;
1759 in->history = g_list_next (in->history);
1760 assign_text (in, (char *) in->history->data);
1761 in->need_push = 0;
1764 static void
1765 port_region_marked_for_delete (WInput *in)
1767 in->buffer[0] = '\0';
1768 in->point = 0;
1769 in->first = 0;
1770 in->charpoint = 0;
1773 static cb_ret_t
1774 input_execute_cmd (WInput *in, int command)
1776 cb_ret_t res = MSG_HANDLED;
1778 switch (command) {
1779 case CK_InputBol:
1780 beginning_of_line (in);
1781 break;
1782 case CK_InputEol:
1783 end_of_line (in);
1784 break;
1785 case CK_InputMoveLeft:
1786 key_left (in);
1787 break;
1788 case CK_InputWordLeft:
1789 key_ctrl_left (in);
1790 break;
1791 case CK_InputMoveRight:
1792 key_right (in);
1793 break;
1794 case CK_InputWordRight:
1795 key_ctrl_right (in);
1796 break;
1797 case CK_InputBackwardChar:
1798 backward_char (in);
1799 break;
1800 case CK_InputBackwardWord:
1801 backward_word (in);
1802 break;
1803 case CK_InputForwardChar:
1804 forward_char (in);
1805 break;
1806 case CK_InputForwardWord:
1807 forward_word (in);
1808 break;
1809 case CK_InputBackwardDelete:
1810 backward_delete (in);
1811 break;
1812 case CK_InputDeleteChar:
1813 delete_char (in);
1814 break;
1815 case CK_InputKillWord:
1816 kill_word (in);
1817 break;
1818 case CK_InputBackwardKillWord:
1819 back_kill_word (in);
1820 break;
1821 case CK_InputSetMark:
1822 set_mark (in);
1823 break;
1824 case CK_InputKillRegion:
1825 kill_region (in);
1826 break;
1827 case CK_InputClearLine:
1828 clear_region (in);
1829 break;
1830 case CK_InputKillSave:
1831 kill_save (in);
1832 break;
1833 case CK_InputYank:
1834 yank (in);
1835 break;
1836 case CK_InputPaste:
1837 ins_from_clip (in);
1838 break;
1839 case CK_InputKillLine:
1840 kill_line (in);
1841 break;
1842 case CK_InputHistoryPrev:
1843 hist_prev (in);
1844 break;
1845 case CK_InputHistoryNext:
1846 hist_next (in);
1847 break;
1848 case CK_InputHistoryShow:
1849 do_show_hist (in);
1850 break;
1851 case CK_InputComplete:
1852 complete (in);
1853 break;
1854 default:
1855 res = MSG_NOT_HANDLED;
1858 return res;
1861 /* This function is a test for a special input key used in complete.c */
1862 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1863 and 2 if it is a complete key */
1865 is_in_input_map (WInput *in, int key)
1867 int i;
1868 for (i = 0; input_map[i].key; i++) {
1869 if (key == input_map[i].key) {
1870 input_execute_cmd (in, input_map[i].command);
1871 if (input_map[i].command == CK_InputComplete)
1872 return 2;
1873 else
1874 return 1;
1877 return 0;
1880 cb_ret_t
1881 handle_char (WInput *in, int key)
1883 cb_ret_t v;
1884 int i;
1886 v = MSG_NOT_HANDLED;
1888 if (quote) {
1889 free_completions (in);
1890 v = insert_char (in, key);
1891 update_input (in, 1);
1892 quote = 0;
1893 return v;
1895 for (i = 0; input_map[i].key; i++) {
1896 if (key == input_map[i].key) {
1897 if (input_map[i].command != CK_InputComplete)
1898 free_completions (in);
1899 input_execute_cmd (in, input_map[i].command);
1900 update_input (in, 1);
1901 v = MSG_HANDLED;
1902 break;
1905 if (input_map[i].command == 0) {
1906 if (key > 255)
1907 return MSG_NOT_HANDLED;
1908 if (in->first)
1909 port_region_marked_for_delete (in);
1910 free_completions (in);
1911 v = insert_char (in, key);
1913 update_input (in, 1);
1914 return v;
1917 /* Inserts text in input line */
1918 void
1919 stuff (WInput *in, const char *text, int insert_extra_space)
1921 input_disable_update (in);
1922 while (*text)
1923 handle_char (in, *text++);
1924 if (insert_extra_space)
1925 handle_char (in, ' ');
1926 input_enable_update (in);
1927 update_input (in, 1);
1930 void
1931 input_set_point (WInput *in, int pos)
1933 int max_pos = str_length (in->buffer);
1935 if (pos > max_pos)
1936 pos = max_pos;
1937 if (pos != in->point)
1938 free_completions (in);
1939 in->point = pos;
1940 in->charpoint = 0;
1941 update_input (in, 1);
1944 cb_ret_t
1945 input_callback (Widget *w, widget_msg_t msg, int parm)
1947 WInput *in = (WInput *) w;
1948 cb_ret_t v;
1950 switch (msg) {
1951 case WIDGET_KEY:
1952 if (parm == XCTRL ('q')) {
1953 quote = 1;
1954 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1955 quote = 0;
1956 return v;
1959 /* Keys we want others to handle */
1960 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1961 || parm == KEY_F (10) || parm == '\n')
1962 return MSG_NOT_HANDLED;
1964 /* When pasting multiline text, insert literal Enter */
1965 if ((parm & ~KEY_M_MASK) == '\n') {
1966 quote = 1;
1967 v = handle_char (in, '\n');
1968 quote = 0;
1969 return v;
1972 return handle_char (in, parm);
1974 case WIDGET_COMMAND:
1975 return input_execute_cmd (in, parm);
1977 case WIDGET_FOCUS:
1978 case WIDGET_UNFOCUS:
1979 case WIDGET_DRAW:
1980 update_input (in, 0);
1981 return MSG_HANDLED;
1983 case WIDGET_CURSOR:
1984 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1985 - in->term_first_shown);
1986 return MSG_HANDLED;
1988 case WIDGET_DESTROY:
1989 input_destroy (in);
1990 return MSG_HANDLED;
1992 default:
1993 return default_proc (msg, parm);
1997 static int
1998 input_event (Gpm_Event * event, void *data)
2000 WInput *in = data;
2002 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2003 dlg_select_widget (in);
2005 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2006 && should_show_history_button (in)) {
2007 do_show_hist (in);
2008 } else {
2009 in->point = str_length (in->buffer);
2010 if (event->x + in->term_first_shown - 1 <
2011 str_term_width1 (in->buffer))
2013 in->point = str_column_to_pos (in->buffer, event->x
2014 + in->term_first_shown - 1);
2017 update_input (in, 1);
2019 return MOU_NORMAL;
2022 WInput *
2023 input_new (int y, int x, int color, int width, const char *def_text,
2024 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2026 WInput *in = g_new (WInput, 1);
2027 int initial_buffer_len;
2029 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2031 /* history setup */
2032 in->history = NULL;
2033 in->history_name = 0;
2034 if (histname) {
2035 if (*histname) {
2036 in->history_name = g_strdup (histname);
2037 in->history = history_get (histname);
2041 if (def_text == NULL)
2042 def_text = "";
2044 if (def_text == INPUT_LAST_TEXT) {
2045 def_text = "";
2046 if (in->history)
2047 if (in->history->data)
2048 def_text = (char *) in->history->data;
2050 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2051 in->widget.options |= W_IS_INPUT;
2052 in->completions = NULL;
2053 in->completion_flags = completion_flags;
2054 in->current_max_size = initial_buffer_len;
2055 in->buffer = g_new (char, initial_buffer_len);
2056 in->color = color;
2057 in->field_width = width;
2058 in->first = 1;
2059 in->term_first_shown = 0;
2060 in->disable_update = 0;
2061 in->mark = 0;
2062 in->need_push = 1;
2063 in->is_password = 0;
2065 strcpy (in->buffer, def_text);
2066 in->point = str_length (in->buffer);
2067 in->charpoint = 0;
2068 return in;
2071 /* Listbox widget */
2073 /* Should draw the scrollbar, but currently draws only
2074 * indications that there is more information
2076 static int listbox_cdiff (WLEntry *s, WLEntry *e);
2078 static void
2079 listbox_drawscroll (WListbox *l)
2081 int line = 0;
2082 int i, top;
2083 int max_line = l->widget.lines - 1;
2085 /* Are we at the top? */
2086 widget_move (&l->widget, 0, l->widget.cols);
2087 if (l->list == l->top)
2088 tty_print_one_vline ();
2089 else
2090 tty_print_char ('^');
2092 /* Are we at the bottom? */
2093 widget_move (&l->widget, max_line, l->widget.cols);
2094 top = listbox_cdiff (l->list, l->top);
2095 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
2096 tty_print_one_vline ();
2097 else
2098 tty_print_char ('v');
2100 /* Now draw the nice relative pointer */
2101 if (l->count != 0)
2102 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
2104 for (i = 1; i < max_line; i++){
2105 widget_move (&l->widget, i, l->widget.cols);
2106 if (i != line)
2107 tty_print_one_vline ();
2108 else
2109 tty_print_char ('*');
2113 static void
2114 listbox_draw (WListbox *l, gboolean focused)
2116 const Dlg_head *h = l->widget.parent;
2117 const int normalc = DLG_NORMALC (h);
2118 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2120 WLEntry *e;
2121 int i;
2122 int sel_line = -1;
2123 const char *text;
2125 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2126 /* Display the entry */
2127 if (e == l->current && sel_line == -1) {
2128 sel_line = i;
2129 tty_setcolor (selc);
2130 } else
2131 tty_setcolor (normalc);
2133 widget_move (&l->widget, i, 1);
2135 if ((i > 0 && e == l->list) || !l->list)
2136 text = "";
2137 else {
2138 text = e->text;
2139 e = e->next;
2141 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2143 l->cursor_y = sel_line;
2145 if (l->scrollbar && l->count > l->widget.lines) {
2146 tty_setcolor (normalc);
2147 listbox_drawscroll (l);
2151 /* Returns the number of items between s and e,
2152 must be on the same linked list */
2153 static int
2154 listbox_cdiff (WLEntry *s, WLEntry *e)
2156 int count;
2158 for (count = 0; s != e; count++)
2159 s = s->next;
2160 return count;
2163 static WLEntry *
2164 listbox_check_hotkey (WListbox *l, int key)
2166 int i;
2167 WLEntry *e;
2169 i = 0;
2170 e = l->list;
2171 if (!e)
2172 return NULL;
2174 while (1) {
2176 /* If we didn't find anything, return */
2177 if (i && e == l->list)
2178 return NULL;
2180 if (e->hotkey == key)
2181 return e;
2183 i++;
2184 e = e->next;
2188 /* Selects the last entry and scrolls the list to the bottom */
2189 void
2190 listbox_select_last (WListbox *l)
2192 unsigned int i;
2193 l->current = l->top = l->list->prev;
2194 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2195 l->top = l->top->prev;
2196 l->pos = l->count - 1;
2199 /* Selects the first entry and scrolls the list to the top */
2200 void
2201 listbox_select_first (WListbox *l)
2203 l->current = l->top = l->list;
2204 l->pos = 0;
2207 void
2208 listbox_remove_list (WListbox *l)
2210 WLEntry *p, *q;
2212 if (!l->count)
2213 return;
2215 p = l->list;
2217 while (l->count--) {
2218 q = p->next;
2219 g_free (p->text);
2220 g_free (p);
2221 p = q;
2223 l->pos = l->count = 0;
2224 l->list = l->top = l->current = 0;
2228 * bor 30.10.96: added force flag to remove *last* entry as well
2229 * bor 30.10.96: corrected selection bug if last entry was removed
2232 void
2233 listbox_remove_current (WListbox *l, int force)
2235 WLEntry *p;
2237 /* Ok, note: this won't allow for emtpy lists */
2238 if (!force && (!l->count || l->count == 1))
2239 return;
2241 l->count--;
2242 p = l->current;
2244 if (l->count) {
2245 l->current->next->prev = l->current->prev;
2246 l->current->prev->next = l->current->next;
2247 if (p->next == l->list) {
2248 l->current = p->prev;
2249 l->pos--;
2251 else
2252 l->current = p->next;
2254 if (p == l->list)
2255 l->list = l->top = p->next;
2256 } else {
2257 l->pos = 0;
2258 l->list = l->top = l->current = 0;
2261 g_free (p->text);
2262 g_free (p);
2265 /* Makes *e the selected entry (sets current and pos) */
2266 void
2267 listbox_select_entry (WListbox *l, WLEntry *dest)
2269 WLEntry *e;
2270 int pos;
2271 int top_seen;
2273 top_seen = 0;
2275 /* Special case */
2276 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2278 if (e == l->top)
2279 top_seen = 1;
2281 if (e == dest){
2282 l->current = e;
2283 if (top_seen){
2284 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2285 l->top = l->top->next;
2286 } else {
2287 l->top = l->current;
2289 l->pos = pos;
2290 return;
2293 /* If we are unable to find it, set decent values */
2294 l->current = l->top = l->list;
2295 l->pos = 0;
2298 /* Selects from base the pos element */
2299 static WLEntry *
2300 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2302 WLEntry *last = l->list->prev;
2304 if (base == last)
2305 return last;
2306 while (pos--){
2307 base = base->next;
2308 if (base == last)
2309 break;
2311 return base;
2314 static void
2315 listbox_back (WListbox *l)
2317 if (l->pos != 0)
2318 listbox_select_entry (l, l->current->prev);
2319 else
2320 listbox_select_last (l);
2323 /* Return MSG_HANDLED if we want a redraw */
2324 static cb_ret_t
2325 listbox_key (WListbox *l, int key)
2327 int i;
2329 cb_ret_t j = MSG_NOT_HANDLED;
2331 /* focus on listbox item N by '0'..'9' keys */
2332 if (key >= '0' && key <= '9') {
2333 int oldpos = l->pos;
2334 listbox_select_by_number(l, key - '0');
2336 /* need scroll to item? */
2337 if (abs(oldpos - l->pos) > l->widget.lines)
2338 l->top = l->current;
2340 return MSG_HANDLED;
2343 if (!l->list)
2344 return MSG_NOT_HANDLED;
2346 switch (key){
2347 case KEY_HOME:
2348 case KEY_A1:
2349 case ALT ('<'):
2350 listbox_select_first (l);
2351 return MSG_HANDLED;
2353 case KEY_END:
2354 case KEY_C1:
2355 case ALT ('>'):
2356 listbox_select_last (l);
2357 return MSG_HANDLED;
2359 case XCTRL('p'):
2360 case KEY_UP:
2361 listbox_back (l);
2362 return MSG_HANDLED;
2364 case XCTRL('n'):
2365 case KEY_DOWN:
2366 listbox_fwd (l);
2367 return MSG_HANDLED;
2369 case KEY_NPAGE:
2370 case XCTRL('v'):
2371 for (i = 0; ((i < l->widget.lines - 1)
2372 && (l->current != l->list->prev)); i++) {
2373 listbox_fwd (l);
2374 j = MSG_HANDLED;
2376 return j;
2378 case KEY_PPAGE:
2379 case ALT('v'):
2380 for (i = 0; ((i < l->widget.lines - 1)
2381 && (l->current != l->list)); i++) {
2382 listbox_back (l);
2383 j = MSG_HANDLED;
2385 return j;
2387 return MSG_NOT_HANDLED;
2390 static void
2391 listbox_destroy (WListbox *l)
2393 WLEntry *n, *p = l->list;
2394 int i;
2396 for (i = 0; i < l->count; i++){
2397 n = p->next;
2398 g_free (p->text);
2399 g_free (p);
2400 p = n;
2404 static cb_ret_t
2405 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2407 WListbox *l = (WListbox *) w;
2408 Dlg_head *h = l->widget.parent;
2409 WLEntry *e;
2410 cb_ret_t ret_code;
2412 switch (msg) {
2413 case WIDGET_INIT:
2414 return MSG_HANDLED;
2416 case WIDGET_HOTKEY:
2417 e = listbox_check_hotkey (l, parm);
2418 if (e != NULL) {
2419 int action;
2421 listbox_select_entry (l, e);
2423 (*h->callback) (h, DLG_ACTION, l->pos);
2425 if (l->cback)
2426 action = (*l->cback) (l);
2427 else
2428 action = LISTBOX_DONE;
2430 if (action == LISTBOX_DONE) {
2431 h->ret_value = B_ENTER;
2432 dlg_stop (h);
2434 return MSG_HANDLED;
2436 return MSG_NOT_HANDLED;
2438 case WIDGET_KEY:
2439 ret_code = listbox_key (l, parm);
2440 if (ret_code != MSG_NOT_HANDLED) {
2441 listbox_draw (l, TRUE);
2442 (*h->callback) (h, DLG_ACTION, l->pos);
2444 return ret_code;
2446 case WIDGET_CURSOR:
2447 widget_move (&l->widget, l->cursor_y, 0);
2448 (*h->callback) (h, DLG_ACTION, l->pos);
2449 return MSG_HANDLED;
2451 case WIDGET_FOCUS:
2452 case WIDGET_UNFOCUS:
2453 case WIDGET_DRAW:
2454 listbox_draw (l, msg != WIDGET_UNFOCUS);
2455 return MSG_HANDLED;
2457 case WIDGET_DESTROY:
2458 listbox_destroy (l);
2459 return MSG_HANDLED;
2461 case WIDGET_RESIZED:
2462 return MSG_HANDLED;
2464 default:
2465 return default_proc (msg, parm);
2469 static int
2470 listbox_event (Gpm_Event *event, void *data)
2472 WListbox *l = data;
2473 int i;
2475 Dlg_head *h = l->widget.parent;
2477 /* Single click */
2478 if (event->type & GPM_DOWN)
2479 dlg_select_widget (l);
2481 if (l->list == NULL)
2482 return MOU_NORMAL;
2484 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2485 int ret = MOU_REPEAT;
2487 if (event->x < 0 || event->x > l->widget.cols)
2488 return ret;
2490 if (event->y < 1)
2491 for (i = -event->y; i >= 0; i--)
2492 listbox_back (l);
2493 else if (event->y > l->widget.lines)
2494 for (i = event->y - l->widget.lines; i > 0; i--)
2495 listbox_fwd (l);
2496 else if (event->buttons & GPM_B_UP) {
2497 listbox_back (l);
2498 ret = MOU_NORMAL;
2499 } else if (event->buttons & GPM_B_DOWN) {
2500 listbox_fwd (l);
2501 ret = MOU_NORMAL;
2502 } else
2503 listbox_select_entry (l,
2504 listbox_select_pos (l, l->top,
2505 event->y - 1));
2507 /* We need to refresh ourselves since the dialog manager doesn't */
2508 /* know about this event */
2509 listbox_draw (l, TRUE);
2510 return ret;
2513 /* Double click */
2514 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2515 int action;
2517 if (event->x < 0 || event->x >= l->widget.cols
2518 || event->y < 1 || event->y > l->widget.lines)
2519 return MOU_NORMAL;
2521 dlg_select_widget (l);
2522 listbox_select_entry (l,
2523 listbox_select_pos (l, l->top,
2524 event->y - 1));
2526 if (l->cback)
2527 action = (*l->cback) (l);
2528 else
2529 action = LISTBOX_DONE;
2531 if (action == LISTBOX_DONE) {
2532 h->ret_value = B_ENTER;
2533 dlg_stop (h);
2534 return MOU_NORMAL;
2537 return MOU_NORMAL;
2540 WListbox *
2541 listbox_new (int y, int x, int height, int width, lcback callback)
2543 WListbox *l = g_new (WListbox, 1);
2545 if (height <= 0)
2546 height = 1;
2548 init_widget (&l->widget, y, x, height, width,
2549 listbox_callback, listbox_event);
2551 l->list = l->top = l->current = 0;
2552 l->pos = 0;
2553 l->count = 0;
2554 l->cback = callback;
2555 l->allow_duplicates = 1;
2556 l->scrollbar = !tty_is_slow ();
2557 widget_want_hotkey (l->widget, 1);
2559 return l;
2562 /* Listbox item adding function. They still lack a lot of functionality */
2563 /* any takers? */
2564 /* 1.11.96 bor: added pos argument to control placement of new entry */
2565 static void
2566 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2568 if (!l->list){
2569 l->list = e;
2570 l->top = e;
2571 l->current = e;
2572 e->next = l->list;
2573 e->prev = l->list;
2574 } else if (pos == LISTBOX_APPEND_AT_END) {
2575 e->next = l->list;
2576 e->prev = l->list->prev;
2577 l->list->prev->next = e;
2578 l->list->prev = e;
2579 } else if (pos == LISTBOX_APPEND_BEFORE){
2580 e->next = l->current;
2581 e->prev = l->current->prev;
2582 l->current->prev->next = e;
2583 l->current->prev = e;
2584 if (l->list == l->current) { /* move list one position down */
2585 l->list = e;
2586 l->top = e;
2588 } else if (pos == LISTBOX_APPEND_AFTER) {
2589 e->prev = l->current;
2590 e->next = l->current->next;
2591 l->current->next->prev = e;
2592 l->current->next = e;
2593 } else if (pos == LISTBOX_APPEND_SORTED) {
2594 WLEntry *w = l->list;
2596 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2597 w = w->next;
2598 if (w->next == l->list) {
2599 e->prev = w;
2600 e->next = l->list;
2601 w->next = e;
2602 l->list->prev = e;
2603 } else {
2604 e->next = w;
2605 e->prev = w->prev;
2606 w->prev->next = e;
2607 w->prev = e;
2610 l->count++;
2613 char *
2614 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2615 const char *text, void *data)
2617 WLEntry *entry;
2619 if (!l)
2620 return NULL;
2622 if (!l->allow_duplicates)
2623 if (listbox_search_text (l, text))
2624 return NULL;
2626 entry = g_new (WLEntry, 1);
2627 entry->text = g_strdup (text);
2628 entry->data = data;
2629 entry->hotkey = hotkey;
2631 listbox_append_item (l, entry, pos);
2633 return entry->text;
2636 /* Selects the nth entry in the listbox */
2637 void
2638 listbox_select_by_number (WListbox *l, int n)
2640 if (l->list != NULL)
2641 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2644 WLEntry *
2645 listbox_search_text (WListbox *l, const char *text)
2647 WLEntry *e;
2649 e = l->list;
2650 if (!e)
2651 return NULL;
2653 do {
2654 if(!strcmp (e->text, text))
2655 return e;
2656 e = e->next;
2657 } while (e!=l->list);
2659 return NULL;
2662 /* Returns the current string text as well as the associated extra data */
2663 void
2664 listbox_get_current (WListbox *l, char **string, char **extra)
2666 if (!l->current){
2667 *string = 0;
2668 *extra = 0;
2670 if (string && l->current)
2671 *string = l->current->text;
2672 if (extra && l->current)
2673 *extra = l->current->data;
2676 /* returns TRUE if a function has been called, FALSE otherwise. */
2677 static gboolean
2678 buttonbar_call (WButtonBar *bb, int i)
2680 switch (bb->labels[i].tag) {
2681 case BBFUNC_NONE:
2682 break;
2683 case BBFUNC_VOID:
2684 bb->labels[i].u.fn_void ();
2685 return TRUE;
2686 case BBFUNC_PTR:
2687 bb->labels[i].u.fn_ptr (bb->labels[i].data);
2688 return TRUE;
2690 return FALSE;
2693 /* calculate width of one button, width is never lesser than 7 */
2694 static int
2695 buttonbat_get_button_width ()
2697 int result = COLS / BUTTONBAR_LABELS_NUM;
2698 return (result >= 7) ? result : 7;
2702 static cb_ret_t
2703 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2705 WButtonBar *bb = (WButtonBar *) w;
2706 int i;
2707 const char *text;
2709 switch (msg) {
2710 case WIDGET_FOCUS:
2711 return MSG_NOT_HANDLED;
2713 case WIDGET_HOTKEY:
2714 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2715 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2716 return MSG_HANDLED;
2717 return MSG_NOT_HANDLED;
2719 case WIDGET_DRAW:
2720 if (bb->visible) {
2721 int offset = 0;
2722 int count_free_positions;
2724 widget_move (&bb->widget, 0, 0);
2725 tty_setcolor (DEFAULT_COLOR);
2726 bb->btn_width = buttonbat_get_button_width ();
2727 tty_printf ("%-*s", bb->widget.cols, "");
2728 count_free_positions = COLS - bb->btn_width*BUTTONBAR_LABELS_NUM;
2730 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2731 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2732 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2733 tty_printf ("%2d", i + 1);
2734 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2735 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2736 tty_print_string (str_fit_to_term (
2737 text,
2738 bb->btn_width - 2 + (int)(offset < count_free_positions),
2739 J_LEFT_FIT));
2741 if (count_free_positions != 0 && offset < count_free_positions)
2742 offset++;
2745 return MSG_HANDLED;
2747 case WIDGET_DESTROY:
2748 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2749 g_free (bb->labels[i].text);
2750 return MSG_HANDLED;
2752 default:
2753 return default_proc (msg, parm);
2757 static int
2758 buttonbar_event (Gpm_Event *event, void *data)
2760 WButtonBar *bb = data;
2761 int button;
2763 if (!(event->type & GPM_UP))
2764 return MOU_NORMAL;
2765 if (event->y == 2)
2766 return MOU_NORMAL;
2767 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / (COLS);
2768 if (button < BUTTONBAR_LABELS_NUM)
2769 buttonbar_call (bb, button);
2770 return MOU_NORMAL;
2773 WButtonBar *
2774 buttonbar_new (int visible)
2776 WButtonBar *bb;
2777 int i;
2779 bb = g_new0 (WButtonBar, 1);
2781 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2782 buttonbar_callback, buttonbar_event);
2783 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2784 bb->visible = visible;
2785 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++){
2786 bb->labels[i].text = NULL;
2787 bb->labels[i].tag = BBFUNC_NONE;
2789 widget_want_hotkey (bb->widget, 1);
2790 widget_want_cursor (bb->widget, 0);
2791 bb->btn_width = buttonbat_get_button_width ();
2793 return bb;
2796 static void
2797 set_label_text (WButtonBar * bb, int lc_index, const char *text)
2799 g_free (bb->labels[lc_index - 1].text);
2801 bb->labels[lc_index - 1].text = g_strdup (text);
2804 /* Find ButtonBar widget in the dialog */
2805 WButtonBar *
2806 find_buttonbar (Dlg_head *h)
2808 WButtonBar *bb;
2810 bb = (WButtonBar *) find_widget_type (h, buttonbar_callback);
2811 return bb;
2814 void
2815 buttonbar_clear_label (Dlg_head *h, int idx)
2817 WButtonBar *bb = find_buttonbar (h);
2819 if (!bb)
2820 return;
2822 set_label_text (bb, idx, "");
2823 bb->labels[idx - 1].tag = BBFUNC_NONE;
2826 void
2827 buttonbar_set_label_data (Dlg_head *h, int idx, const char *text,
2828 buttonbarfn cback, void *data)
2830 WButtonBar *bb = find_buttonbar (h);
2832 if (!bb)
2833 return;
2835 assert (cback != (buttonbarfn) 0);
2836 set_label_text (bb, idx, text);
2837 bb->labels[idx - 1].tag = BBFUNC_PTR;
2838 bb->labels[idx - 1].u.fn_ptr = cback;
2839 bb->labels[idx - 1].data = data;
2842 void
2843 buttonbar_set_label (Dlg_head *h, int idx, const char *text, voidfn cback)
2845 WButtonBar *bb = find_buttonbar (h);
2847 if (!bb)
2848 return;
2850 assert (cback != (voidfn) 0);
2851 set_label_text (bb, idx, text);
2852 bb->labels[idx - 1].tag = BBFUNC_VOID;
2853 bb->labels[idx - 1].u.fn_void = cback;
2856 void
2857 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2859 bb->visible = visible;
2862 void
2863 buttonbar_redraw (Dlg_head *h)
2865 WButtonBar *bb = find_buttonbar (h);
2867 if (!bb)
2868 return;
2870 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2873 static cb_ret_t
2874 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2876 WGroupbox *g = (WGroupbox *) w;
2878 switch (msg) {
2879 case WIDGET_INIT:
2880 return MSG_HANDLED;
2882 case WIDGET_FOCUS:
2883 return MSG_NOT_HANDLED;
2885 case WIDGET_DRAW:
2886 tty_setcolor (COLOR_NORMAL);
2887 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2888 g->widget.x - g->widget.parent->x, g->widget.lines,
2889 g->widget.cols);
2891 tty_setcolor (COLOR_HOT_NORMAL);
2892 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2893 g->widget.x - g->widget.parent->x + 1);
2894 tty_print_string (g->title);
2895 return MSG_HANDLED;
2897 case WIDGET_DESTROY:
2898 g_free (g->title);
2899 return MSG_HANDLED;
2901 default:
2902 return default_proc (msg, parm);
2906 WGroupbox *
2907 groupbox_new (int y, int x, int height, int width, const char *title)
2909 WGroupbox *g = g_new (WGroupbox, 1);
2911 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2913 g->widget.options &= ~W_WANT_CURSOR;
2914 widget_want_hotkey (g->widget, 0);
2916 /* Strip existing spaces, add one space before and after the title */
2917 if (title) {
2918 char *t;
2919 t = g_strstrip (g_strdup (title));
2920 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2921 g_free (t);
2924 return g;