Ticket #1873: Viewer: hangup after search in archive
[pantumic.git] / src / widget.c
blob1590d2bb63fd68dc195f34fb9531215e011e5257
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 /** \file widget.c
29 * \brief Source: widgets
32 #include <config.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
43 #include "global.h"
45 #include "../src/tty/tty.h"
46 #include "../src/skin/skin.h"
47 #include "../src/tty/mouse.h"
48 #include "../src/tty/key.h" /* XCTRL and ALT macros */
50 #include "../src/mcconfig/mcconfig.h" /* for history loading and saving */
52 #include "dialog.h"
53 #include "widget.h"
54 #include "wtools.h"
55 #include "strutil.h"
57 #include "cmddef.h" /* CK_ cmd name const */
58 #include "keybind.h" /* global_keymap_t */
59 #include "fileloc.h"
60 #include "panel.h" /* current_panel */
62 const global_keymap_t *input_map;
64 static void
65 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
67 Dlg_head *h = w->parent;
69 tty_setcolor (hotkey
70 ? (focused
71 ? DLG_HOT_FOCUSC (h)
72 : DLG_HOT_NORMALC (h))
73 : (focused
74 ? DLG_FOCUSC (h)
75 : DLG_NORMALC (h)));
78 struct hotkey_t
79 parse_hotkey (const char *text)
81 struct hotkey_t result;
82 const char *cp, *p;
84 /* search for '&', that is not on the of text */
85 cp = strchr (text, '&');
86 if (cp != NULL && cp[1] != '\0') {
87 result.start = g_strndup (text, cp - text);
89 /* skip '&' */
90 cp++;
91 p = str_cget_next_char (cp);
92 result.hotkey = g_strndup (cp, p - cp);
94 cp = p;
95 result.end = g_strdup (cp);
96 } else {
97 result.start = g_strdup (text);
98 result.hotkey = NULL;
99 result.end = NULL;
102 return result;
104 void
105 release_hotkey (const struct hotkey_t hotkey)
107 g_free (hotkey.start);
108 g_free (hotkey.hotkey);
109 g_free (hotkey.end);
113 hotkey_width (const struct hotkey_t hotkey)
115 int result;
117 result = str_term_width1 (hotkey.start);
118 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
119 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
120 return result;
123 static void
124 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
126 widget_selectcolor (w, focused, FALSE);
127 tty_print_string (hotkey.start);
129 if (hotkey.hotkey != NULL) {
130 widget_selectcolor (w, focused, TRUE);
131 tty_print_string (hotkey.hotkey);
132 widget_selectcolor (w, focused, FALSE);
135 if (hotkey.end != NULL)
136 tty_print_string (hotkey.end);
140 /* Default callback for widgets */
141 cb_ret_t
142 default_proc (widget_msg_t msg, int parm)
144 (void) parm;
146 switch (msg) {
147 case WIDGET_INIT:
148 case WIDGET_FOCUS:
149 case WIDGET_UNFOCUS:
150 case WIDGET_DRAW:
151 case WIDGET_DESTROY:
152 case WIDGET_CURSOR:
153 case WIDGET_IDLE:
154 return MSG_HANDLED;
156 default:
157 return MSG_NOT_HANDLED;
161 static int button_event (Gpm_Event *event, void *);
163 int quote = 0;
165 static cb_ret_t
166 button_callback (Widget *w, widget_msg_t msg, int parm)
168 WButton *b = (WButton *) w;
169 int stop = 0;
170 int off = 0;
171 Dlg_head *h = b->widget.parent;
173 switch (msg) {
174 case WIDGET_HOTKEY:
176 * Don't let the default button steal Enter from the current
177 * button. This is a workaround for the flawed event model
178 * when hotkeys are sent to all widgets before the key is
179 * handled by the current widget.
181 if (parm == '\n' && h->current == &b->widget) {
182 button_callback (w, WIDGET_KEY, ' ');
183 return MSG_HANDLED;
186 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
187 button_callback (w, WIDGET_KEY, ' ');
188 return MSG_HANDLED;
191 if (b->text.hotkey != NULL) {
192 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
193 g_ascii_tolower ((gchar)parm)) {
194 button_callback (w, WIDGET_KEY, ' ');
195 return MSG_HANDLED;
198 return MSG_NOT_HANDLED;
200 case WIDGET_KEY:
201 if (parm != ' ' && parm != '\n')
202 return MSG_NOT_HANDLED;
204 if (b->callback)
205 stop = (*b->callback) (b->action);
206 if (!b->callback || stop) {
207 h->ret_value = b->action;
208 dlg_stop (h);
210 return MSG_HANDLED;
212 case WIDGET_CURSOR:
213 switch (b->flags) {
214 case DEFPUSH_BUTTON:
215 off = 3;
216 break;
217 case NORMAL_BUTTON:
218 off = 2;
219 break;
220 case NARROW_BUTTON:
221 off = 1;
222 break;
223 case HIDDEN_BUTTON:
224 default:
225 off = 0;
226 break;
228 widget_move (&b->widget, 0, b->hotpos + off);
229 return MSG_HANDLED;
231 case WIDGET_UNFOCUS:
232 case WIDGET_FOCUS:
233 case WIDGET_DRAW:
234 if (msg == WIDGET_UNFOCUS)
235 b->selected = 0;
236 else if (msg == WIDGET_FOCUS)
237 b->selected = 1;
239 widget_selectcolor (w, b->selected, FALSE);
240 widget_move (w, 0, 0);
242 switch (b->flags) {
243 case DEFPUSH_BUTTON:
244 tty_print_string ("[< ");
245 break;
246 case NORMAL_BUTTON:
247 tty_print_string ("[ ");
248 break;
249 case NARROW_BUTTON:
250 tty_print_string ("[");
251 break;
252 case HIDDEN_BUTTON:
253 default:
254 return MSG_HANDLED;
257 draw_hotkey (w, b->text, b->selected);
259 switch (b->flags) {
260 case DEFPUSH_BUTTON:
261 tty_print_string (" >]");
262 break;
263 case NORMAL_BUTTON:
264 tty_print_string (" ]");
265 break;
266 case NARROW_BUTTON:
267 tty_print_string ("]");
268 break;
270 return MSG_HANDLED;
272 case WIDGET_DESTROY:
273 release_hotkey (b->text);
274 return MSG_HANDLED;
276 default:
277 return default_proc (msg, parm);
281 static int
282 button_event (Gpm_Event *event, void *data)
284 WButton *b = data;
286 if (event->type & (GPM_DOWN|GPM_UP)){
287 Dlg_head *h = b->widget.parent;
288 dlg_select_widget (b);
289 if (event->type & GPM_UP){
290 button_callback ((Widget *) data, WIDGET_KEY, ' ');
291 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
292 return MOU_NORMAL;
295 return MOU_NORMAL;
299 button_get_len (const WButton *b)
301 int ret = hotkey_width (b->text);
302 switch (b->flags) {
303 case DEFPUSH_BUTTON:
304 ret += 6;
305 break;
306 case NORMAL_BUTTON:
307 ret += 4;
308 break;
309 case NARROW_BUTTON:
310 ret += 2;
311 break;
312 case HIDDEN_BUTTON:
313 default:
314 return 0;
316 return ret;
319 WButton *
320 button_new (int y, int x, int action, int flags, const char *text,
321 bcback callback)
323 WButton *b = g_new (WButton, 1);
325 b->action = action;
326 b->flags = flags;
327 b->text = parse_hotkey (text);
329 init_widget (&b->widget, y, x, 1, button_get_len (b),
330 button_callback, button_event);
332 b->selected = 0;
333 b->callback = callback;
334 widget_want_hotkey (b->widget, 1);
335 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
337 return b;
340 const char *
341 button_get_text (const WButton *b)
343 if (b->text.hotkey != NULL)
344 return g_strconcat (b->text.start, "&", b->text.hotkey,
345 b->text.end, (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 if (!in->history)
1705 return;
1707 if (in->need_push) {
1708 push_history (in, in->buffer);
1709 in->history = g_list_previous (in->history);
1710 } else if (in->history->prev)
1711 in->history = g_list_previous (in->history);
1712 else
1713 return;
1714 assign_text (in, (char *) in->history->data);
1715 in->need_push = 0;
1718 static void
1719 hist_next (WInput *in)
1721 if (in->need_push) {
1722 push_history (in, in->buffer);
1723 assign_text (in, "");
1724 return;
1727 if (!in->history)
1728 return;
1730 if (!in->history->next) {
1731 assign_text (in, "");
1732 return;
1735 in->history = g_list_next (in->history);
1736 assign_text (in, (char *) in->history->data);
1737 in->need_push = 0;
1740 static void
1741 port_region_marked_for_delete (WInput *in)
1743 in->buffer[0] = '\0';
1744 in->point = 0;
1745 in->first = 0;
1746 in->charpoint = 0;
1749 static cb_ret_t
1750 input_execute_cmd (WInput *in, unsigned long command)
1752 cb_ret_t res = MSG_HANDLED;
1754 switch (command) {
1755 case CK_InputBol:
1756 beginning_of_line (in);
1757 break;
1758 case CK_InputEol:
1759 end_of_line (in);
1760 break;
1761 case CK_InputMoveLeft:
1762 key_left (in);
1763 break;
1764 case CK_InputWordLeft:
1765 key_ctrl_left (in);
1766 break;
1767 case CK_InputMoveRight:
1768 key_right (in);
1769 break;
1770 case CK_InputWordRight:
1771 key_ctrl_right (in);
1772 break;
1773 case CK_InputBackwardChar:
1774 backward_char (in);
1775 break;
1776 case CK_InputBackwardWord:
1777 backward_word (in);
1778 break;
1779 case CK_InputForwardChar:
1780 forward_char (in);
1781 break;
1782 case CK_InputForwardWord:
1783 forward_word (in);
1784 break;
1785 case CK_InputBackwardDelete:
1786 backward_delete (in);
1787 break;
1788 case CK_InputDeleteChar:
1789 delete_char (in);
1790 break;
1791 case CK_InputKillWord:
1792 kill_word (in);
1793 break;
1794 case CK_InputBackwardKillWord:
1795 back_kill_word (in);
1796 break;
1797 case CK_InputSetMark:
1798 set_mark (in);
1799 break;
1800 case CK_InputKillRegion:
1801 kill_region (in);
1802 break;
1803 case CK_InputClearLine:
1804 clear_region (in);
1805 break;
1806 case CK_InputKillSave:
1807 kill_save (in);
1808 break;
1809 case CK_InputYank:
1810 yank (in);
1811 break;
1812 case CK_InputPaste:
1813 ins_from_clip (in);
1814 break;
1815 case CK_InputKillLine:
1816 kill_line (in);
1817 break;
1818 case CK_InputHistoryPrev:
1819 hist_prev (in);
1820 break;
1821 case CK_InputHistoryNext:
1822 hist_next (in);
1823 break;
1824 case CK_InputHistoryShow:
1825 do_show_hist (in);
1826 break;
1827 case CK_InputComplete:
1828 complete (in);
1829 break;
1830 default:
1831 res = MSG_NOT_HANDLED;
1834 return res;
1837 /* This function is a test for a special input key used in complete.c */
1838 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1839 and 2 if it is a complete key */
1841 is_in_input_map (WInput *in, int key)
1843 size_t i;
1844 for (i = 0; input_map[i].key != 0; i++)
1845 if (key == input_map[i].key) {
1846 input_execute_cmd (in, input_map[i].command);
1847 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1849 return 0;
1852 cb_ret_t
1853 handle_char (WInput *in, int key)
1855 cb_ret_t v;
1856 int i;
1858 v = MSG_NOT_HANDLED;
1860 if (quote) {
1861 free_completions (in);
1862 v = insert_char (in, key);
1863 update_input (in, 1);
1864 quote = 0;
1865 return v;
1867 for (i = 0; input_map[i].key; i++) {
1868 if (key == input_map[i].key) {
1869 if (input_map[i].command != CK_InputComplete)
1870 free_completions (in);
1871 input_execute_cmd (in, input_map[i].command);
1872 update_input (in, 1);
1873 v = MSG_HANDLED;
1874 break;
1877 if (input_map[i].command == 0) {
1878 if (key > 255)
1879 return MSG_NOT_HANDLED;
1880 if (in->first)
1881 port_region_marked_for_delete (in);
1882 free_completions (in);
1883 v = insert_char (in, key);
1885 update_input (in, 1);
1886 return v;
1889 /* Inserts text in input line */
1890 void
1891 stuff (WInput *in, const char *text, int insert_extra_space)
1893 input_disable_update (in);
1894 while (*text)
1895 handle_char (in, *text++);
1896 if (insert_extra_space)
1897 handle_char (in, ' ');
1898 input_enable_update (in);
1899 update_input (in, 1);
1902 void
1903 input_set_point (WInput *in, int pos)
1905 int max_pos = str_length (in->buffer);
1907 if (pos > max_pos)
1908 pos = max_pos;
1909 if (pos != in->point)
1910 free_completions (in);
1911 in->point = pos;
1912 in->charpoint = 0;
1913 update_input (in, 1);
1916 cb_ret_t
1917 input_callback (Widget *w, widget_msg_t msg, int parm)
1919 WInput *in = (WInput *) w;
1920 cb_ret_t v;
1922 switch (msg) {
1923 case WIDGET_KEY:
1924 if (parm == XCTRL ('q')) {
1925 quote = 1;
1926 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1927 quote = 0;
1928 return v;
1931 /* Keys we want others to handle */
1932 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1933 || parm == KEY_F (10) || parm == '\n')
1934 return MSG_NOT_HANDLED;
1936 /* When pasting multiline text, insert literal Enter */
1937 if ((parm & ~KEY_M_MASK) == '\n') {
1938 quote = 1;
1939 v = handle_char (in, '\n');
1940 quote = 0;
1941 return v;
1944 return handle_char (in, parm);
1946 case WIDGET_COMMAND:
1947 return input_execute_cmd (in, parm);
1949 case WIDGET_FOCUS:
1950 case WIDGET_UNFOCUS:
1951 case WIDGET_DRAW:
1952 update_input (in, 0);
1953 return MSG_HANDLED;
1955 case WIDGET_CURSOR:
1956 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1957 - in->term_first_shown);
1958 return MSG_HANDLED;
1960 case WIDGET_DESTROY:
1961 input_destroy (in);
1962 return MSG_HANDLED;
1964 default:
1965 return default_proc (msg, parm);
1969 static int
1970 input_event (Gpm_Event * event, void *data)
1972 WInput *in = data;
1974 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1975 dlg_select_widget (in);
1977 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1978 && should_show_history_button (in)) {
1979 do_show_hist (in);
1980 } else {
1981 in->point = str_length (in->buffer);
1982 if (event->x + in->term_first_shown - 1 <
1983 str_term_width1 (in->buffer))
1985 in->point = str_column_to_pos (in->buffer, event->x
1986 + in->term_first_shown - 1);
1989 update_input (in, 1);
1991 return MOU_NORMAL;
1994 WInput *
1995 input_new (int y, int x, int color, int width, const char *def_text,
1996 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
1998 WInput *in = g_new (WInput, 1);
1999 int initial_buffer_len;
2001 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2003 /* history setup */
2004 in->history = NULL;
2005 in->history_name = 0;
2006 if (histname) {
2007 if (*histname) {
2008 in->history_name = g_strdup (histname);
2009 in->history = history_get (histname);
2013 if (def_text == NULL)
2014 def_text = "";
2016 if (def_text == INPUT_LAST_TEXT) {
2017 def_text = "";
2018 if (in->history)
2019 if (in->history->data)
2020 def_text = (char *) in->history->data;
2022 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2023 in->widget.options |= W_IS_INPUT;
2024 in->completions = NULL;
2025 in->completion_flags = completion_flags;
2026 in->current_max_size = initial_buffer_len;
2027 in->buffer = g_new (char, initial_buffer_len);
2028 in->color = color;
2029 in->field_width = width;
2030 in->first = 1;
2031 in->term_first_shown = 0;
2032 in->disable_update = 0;
2033 in->mark = 0;
2034 in->need_push = 1;
2035 in->is_password = 0;
2037 strcpy (in->buffer, def_text);
2038 in->point = str_length (in->buffer);
2039 in->charpoint = 0;
2040 return in;
2043 /* Listbox widget */
2045 /* Should draw the scrollbar, but currently draws only
2046 * indications that there is more information
2048 static int listbox_cdiff (WLEntry *s, WLEntry *e);
2050 static void
2051 listbox_drawscroll (WListbox *l)
2053 int line = 0;
2054 int i, top;
2055 int max_line = l->widget.lines - 1;
2057 /* Are we at the top? */
2058 widget_move (&l->widget, 0, l->widget.cols);
2059 if (l->list == l->top)
2060 tty_print_one_vline ();
2061 else
2062 tty_print_char ('^');
2064 /* Are we at the bottom? */
2065 widget_move (&l->widget, max_line, l->widget.cols);
2066 top = listbox_cdiff (l->list, l->top);
2067 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
2068 tty_print_one_vline ();
2069 else
2070 tty_print_char ('v');
2072 /* Now draw the nice relative pointer */
2073 if (l->count != 0)
2074 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
2076 for (i = 1; i < max_line; i++){
2077 widget_move (&l->widget, i, l->widget.cols);
2078 if (i != line)
2079 tty_print_one_vline ();
2080 else
2081 tty_print_char ('*');
2085 static void
2086 listbox_draw (WListbox *l, gboolean focused)
2088 const Dlg_head *h = l->widget.parent;
2089 const int normalc = DLG_NORMALC (h);
2090 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2092 WLEntry *e;
2093 int i;
2094 int sel_line = -1;
2095 const char *text;
2097 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2098 /* Display the entry */
2099 if (e == l->current && sel_line == -1) {
2100 sel_line = i;
2101 tty_setcolor (selc);
2102 } else
2103 tty_setcolor (normalc);
2105 widget_move (&l->widget, i, 1);
2107 if ((i > 0 && e == l->list) || !l->list)
2108 text = "";
2109 else {
2110 text = e->text;
2111 e = e->next;
2113 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2115 l->cursor_y = sel_line;
2117 if (l->scrollbar && l->count > l->widget.lines) {
2118 tty_setcolor (normalc);
2119 listbox_drawscroll (l);
2123 /* Returns the number of items between s and e,
2124 must be on the same linked list */
2125 static int
2126 listbox_cdiff (WLEntry *s, WLEntry *e)
2128 int count;
2130 for (count = 0; s != e; count++)
2131 s = s->next;
2132 return count;
2135 static WLEntry *
2136 listbox_check_hotkey (WListbox *l, int key)
2138 int i;
2139 WLEntry *e;
2141 i = 0;
2142 e = l->list;
2143 if (!e)
2144 return NULL;
2146 while (1) {
2148 /* If we didn't find anything, return */
2149 if (i && e == l->list)
2150 return NULL;
2152 if (e->hotkey == key)
2153 return e;
2155 i++;
2156 e = e->next;
2160 /* Selects the last entry and scrolls the list to the bottom */
2161 void
2162 listbox_select_last (WListbox *l)
2164 unsigned int i;
2165 l->current = l->top = l->list->prev;
2166 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2167 l->top = l->top->prev;
2168 l->pos = l->count - 1;
2171 /* Selects the first entry and scrolls the list to the top */
2172 void
2173 listbox_select_first (WListbox *l)
2175 l->current = l->top = l->list;
2176 l->pos = 0;
2179 void
2180 listbox_remove_list (WListbox *l)
2182 WLEntry *p, *q;
2184 if (!l->count)
2185 return;
2187 p = l->list;
2189 while (l->count--) {
2190 q = p->next;
2191 g_free (p->text);
2192 g_free (p);
2193 p = q;
2195 l->pos = l->count = 0;
2196 l->list = l->top = l->current = 0;
2200 * bor 30.10.96: added force flag to remove *last* entry as well
2201 * bor 30.10.96: corrected selection bug if last entry was removed
2204 void
2205 listbox_remove_current (WListbox *l, int force)
2207 WLEntry *p;
2209 /* Ok, note: this won't allow for emtpy lists */
2210 if (!force && (!l->count || l->count == 1))
2211 return;
2213 l->count--;
2214 p = l->current;
2216 if (l->count) {
2217 l->current->next->prev = l->current->prev;
2218 l->current->prev->next = l->current->next;
2219 if (p->next == l->list) {
2220 l->current = p->prev;
2221 l->pos--;
2223 else
2224 l->current = p->next;
2226 if (p == l->list)
2227 l->list = l->top = p->next;
2228 } else {
2229 l->pos = 0;
2230 l->list = l->top = l->current = 0;
2233 g_free (p->text);
2234 g_free (p);
2237 /* Makes *e the selected entry (sets current and pos) */
2238 void
2239 listbox_select_entry (WListbox *l, WLEntry *dest)
2241 WLEntry *e;
2242 int pos;
2243 int top_seen;
2245 top_seen = 0;
2247 /* Special case */
2248 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2250 if (e == l->top)
2251 top_seen = 1;
2253 if (e == dest){
2254 l->current = e;
2255 if (top_seen){
2256 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2257 l->top = l->top->next;
2258 } else {
2259 l->top = l->current;
2261 l->pos = pos;
2262 return;
2265 /* If we are unable to find it, set decent values */
2266 l->current = l->top = l->list;
2267 l->pos = 0;
2270 /* Selects from base the pos element */
2271 static WLEntry *
2272 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2274 WLEntry *last = l->list->prev;
2276 if (base == last)
2277 return last;
2278 while (pos--){
2279 base = base->next;
2280 if (base == last)
2281 break;
2283 return base;
2286 static void
2287 listbox_back (WListbox *l)
2289 if (l->pos != 0)
2290 listbox_select_entry (l, l->current->prev);
2291 else
2292 listbox_select_last (l);
2295 /* Return MSG_HANDLED if we want a redraw */
2296 static cb_ret_t
2297 listbox_key (WListbox *l, int key)
2299 int i;
2301 cb_ret_t j = MSG_NOT_HANDLED;
2303 /* focus on listbox item N by '0'..'9' keys */
2304 if (key >= '0' && key <= '9') {
2305 int oldpos = l->pos;
2306 listbox_select_by_number(l, key - '0');
2308 /* need scroll to item? */
2309 if (abs(oldpos - l->pos) > l->widget.lines)
2310 l->top = l->current;
2312 return MSG_HANDLED;
2315 if (!l->list)
2316 return MSG_NOT_HANDLED;
2318 switch (key){
2319 case KEY_HOME:
2320 case KEY_A1:
2321 case ALT ('<'):
2322 listbox_select_first (l);
2323 return MSG_HANDLED;
2325 case KEY_END:
2326 case KEY_C1:
2327 case ALT ('>'):
2328 listbox_select_last (l);
2329 return MSG_HANDLED;
2331 case XCTRL('p'):
2332 case KEY_UP:
2333 listbox_back (l);
2334 return MSG_HANDLED;
2336 case XCTRL('n'):
2337 case KEY_DOWN:
2338 listbox_fwd (l);
2339 return MSG_HANDLED;
2341 case KEY_NPAGE:
2342 case XCTRL('v'):
2343 for (i = 0; ((i < l->widget.lines - 1)
2344 && (l->current != l->list->prev)); i++) {
2345 listbox_fwd (l);
2346 j = MSG_HANDLED;
2348 return j;
2350 case KEY_PPAGE:
2351 case ALT('v'):
2352 for (i = 0; ((i < l->widget.lines - 1)
2353 && (l->current != l->list)); i++) {
2354 listbox_back (l);
2355 j = MSG_HANDLED;
2357 return j;
2359 return MSG_NOT_HANDLED;
2362 static void
2363 listbox_destroy (WListbox *l)
2365 WLEntry *n, *p = l->list;
2366 int i;
2368 for (i = 0; i < l->count; i++){
2369 n = p->next;
2370 g_free (p->text);
2371 g_free (p);
2372 p = n;
2376 static cb_ret_t
2377 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2379 WListbox *l = (WListbox *) w;
2380 Dlg_head *h = l->widget.parent;
2381 WLEntry *e;
2382 cb_ret_t ret_code;
2384 switch (msg) {
2385 case WIDGET_INIT:
2386 return MSG_HANDLED;
2388 case WIDGET_HOTKEY:
2389 e = listbox_check_hotkey (l, parm);
2390 if (e != NULL) {
2391 int action;
2393 listbox_select_entry (l, e);
2394 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2396 if (l->cback)
2397 action = (*l->cback) (l);
2398 else
2399 action = LISTBOX_DONE;
2401 if (action == LISTBOX_DONE) {
2402 h->ret_value = B_ENTER;
2403 dlg_stop (h);
2405 return MSG_HANDLED;
2407 return MSG_NOT_HANDLED;
2409 case WIDGET_KEY:
2410 ret_code = listbox_key (l, parm);
2411 if (ret_code != MSG_NOT_HANDLED) {
2412 listbox_draw (l, TRUE);
2413 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2415 return ret_code;
2417 case WIDGET_CURSOR:
2418 widget_move (&l->widget, l->cursor_y, 0);
2419 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2420 return MSG_HANDLED;
2422 case WIDGET_FOCUS:
2423 case WIDGET_UNFOCUS:
2424 case WIDGET_DRAW:
2425 listbox_draw (l, msg != WIDGET_UNFOCUS);
2426 return MSG_HANDLED;
2428 case WIDGET_DESTROY:
2429 listbox_destroy (l);
2430 return MSG_HANDLED;
2432 case WIDGET_RESIZED:
2433 return MSG_HANDLED;
2435 default:
2436 return default_proc (msg, parm);
2440 static int
2441 listbox_event (Gpm_Event *event, void *data)
2443 WListbox *l = data;
2444 int i;
2446 Dlg_head *h = l->widget.parent;
2448 /* Single click */
2449 if (event->type & GPM_DOWN)
2450 dlg_select_widget (l);
2452 if (l->list == NULL)
2453 return MOU_NORMAL;
2455 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2456 int ret = MOU_REPEAT;
2458 if (event->x < 0 || event->x > l->widget.cols)
2459 return ret;
2461 if (event->y < 1)
2462 for (i = -event->y; i >= 0; i--)
2463 listbox_back (l);
2464 else if (event->y > l->widget.lines)
2465 for (i = event->y - l->widget.lines; i > 0; i--)
2466 listbox_fwd (l);
2467 else if (event->buttons & GPM_B_UP) {
2468 listbox_back (l);
2469 ret = MOU_NORMAL;
2470 } else if (event->buttons & GPM_B_DOWN) {
2471 listbox_fwd (l);
2472 ret = MOU_NORMAL;
2473 } else
2474 listbox_select_entry (l,
2475 listbox_select_pos (l, l->top,
2476 event->y - 1));
2478 /* We need to refresh ourselves since the dialog manager doesn't */
2479 /* know about this event */
2480 listbox_draw (l, TRUE);
2481 return ret;
2484 /* Double click */
2485 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2486 int action;
2488 if (event->x < 0 || event->x >= l->widget.cols
2489 || event->y < 1 || event->y > l->widget.lines)
2490 return MOU_NORMAL;
2492 dlg_select_widget (l);
2493 listbox_select_entry (l,
2494 listbox_select_pos (l, l->top,
2495 event->y - 1));
2497 if (l->cback)
2498 action = (*l->cback) (l);
2499 else
2500 action = LISTBOX_DONE;
2502 if (action == LISTBOX_DONE) {
2503 h->ret_value = B_ENTER;
2504 dlg_stop (h);
2505 return MOU_NORMAL;
2508 return MOU_NORMAL;
2511 WListbox *
2512 listbox_new (int y, int x, int height, int width, lcback callback)
2514 WListbox *l = g_new (WListbox, 1);
2516 if (height <= 0)
2517 height = 1;
2519 init_widget (&l->widget, y, x, height, width,
2520 listbox_callback, listbox_event);
2522 l->list = l->top = l->current = 0;
2523 l->pos = 0;
2524 l->count = 0;
2525 l->cback = callback;
2526 l->allow_duplicates = 1;
2527 l->scrollbar = !tty_is_slow ();
2528 widget_want_hotkey (l->widget, 1);
2530 return l;
2533 /* Listbox item adding function. They still lack a lot of functionality */
2534 /* any takers? */
2535 /* 1.11.96 bor: added pos argument to control placement of new entry */
2536 static void
2537 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2539 if (!l->list){
2540 l->list = e;
2541 l->top = e;
2542 l->current = e;
2543 e->next = l->list;
2544 e->prev = l->list;
2545 } else if (pos == LISTBOX_APPEND_AT_END) {
2546 e->next = l->list;
2547 e->prev = l->list->prev;
2548 l->list->prev->next = e;
2549 l->list->prev = e;
2550 } else if (pos == LISTBOX_APPEND_BEFORE){
2551 e->next = l->current;
2552 e->prev = l->current->prev;
2553 l->current->prev->next = e;
2554 l->current->prev = e;
2555 if (l->list == l->current) { /* move list one position down */
2556 l->list = e;
2557 l->top = e;
2559 } else if (pos == LISTBOX_APPEND_AFTER) {
2560 e->prev = l->current;
2561 e->next = l->current->next;
2562 l->current->next->prev = e;
2563 l->current->next = e;
2564 } else if (pos == LISTBOX_APPEND_SORTED) {
2565 WLEntry *w = l->list;
2567 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2568 w = w->next;
2569 if (w->next == l->list) {
2570 e->prev = w;
2571 e->next = l->list;
2572 w->next = e;
2573 l->list->prev = e;
2574 } else {
2575 e->next = w;
2576 e->prev = w->prev;
2577 w->prev->next = e;
2578 w->prev = e;
2581 l->count++;
2584 char *
2585 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2586 const char *text, void *data)
2588 WLEntry *entry;
2590 if (!l)
2591 return NULL;
2593 if (!l->allow_duplicates)
2594 if (listbox_search_text (l, text))
2595 return NULL;
2597 entry = g_new (WLEntry, 1);
2598 entry->text = g_strdup (text);
2599 entry->data = data;
2600 entry->hotkey = hotkey;
2602 listbox_append_item (l, entry, pos);
2604 return entry->text;
2607 /* Selects the nth entry in the listbox */
2608 void
2609 listbox_select_by_number (WListbox *l, int n)
2611 if (l->list != NULL)
2612 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2615 WLEntry *
2616 listbox_search_text (WListbox *l, const char *text)
2618 WLEntry *e;
2620 e = l->list;
2621 if (!e)
2622 return NULL;
2624 do {
2625 if(!strcmp (e->text, text))
2626 return e;
2627 e = e->next;
2628 } while (e!=l->list);
2630 return NULL;
2633 /* Returns the current string text as well as the associated extra data */
2634 void
2635 listbox_get_current (WListbox *l, char **string, char **extra)
2637 if (!l->current){
2638 *string = 0;
2639 *extra = 0;
2641 if (string && l->current)
2642 *string = l->current->text;
2643 if (extra && l->current)
2644 *extra = l->current->data;
2647 /* returns TRUE if a function has been called, FALSE otherwise. */
2648 static gboolean
2649 buttonbar_call (WButtonBar *bb, int i)
2651 cb_ret_t ret = MSG_NOT_HANDLED;
2653 if (bb != NULL)
2654 ret = bb->widget.parent->callback (bb->widget.parent,
2655 (Widget *) bb, DLG_ACTION,
2656 bb->labels[i].command,
2657 bb->labels[i].receiver);
2658 return ret;
2661 /* calculate width of one button, width is never lesser than 7 */
2662 static int
2663 buttonbat_get_button_width (void)
2665 int result = COLS / BUTTONBAR_LABELS_NUM;
2666 return (result >= 7) ? result : 7;
2669 static cb_ret_t
2670 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2672 WButtonBar *bb = (WButtonBar *) w;
2673 int i;
2674 const char *text;
2676 switch (msg) {
2677 case WIDGET_FOCUS:
2678 return MSG_NOT_HANDLED;
2680 case WIDGET_HOTKEY:
2681 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2682 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2683 return MSG_HANDLED;
2684 return MSG_NOT_HANDLED;
2686 case WIDGET_DRAW:
2687 if (bb->visible) {
2688 int offset = 0;
2689 int count_free_positions;
2691 widget_move (&bb->widget, 0, 0);
2692 tty_setcolor (DEFAULT_COLOR);
2693 bb->btn_width = buttonbat_get_button_width ();
2694 tty_printf ("%-*s", bb->widget.cols, "");
2695 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2697 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2698 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2699 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2700 tty_printf ("%2d", i + 1);
2701 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2702 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2703 tty_print_string (str_fit_to_term (
2704 text,
2705 bb->btn_width - 2 + (int)(offset < count_free_positions),
2706 J_LEFT_FIT));
2708 if (count_free_positions != 0 && offset < count_free_positions)
2709 offset++;
2712 return MSG_HANDLED;
2714 case WIDGET_DESTROY:
2715 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2716 g_free (bb->labels[i].text);
2717 return MSG_HANDLED;
2719 default:
2720 return default_proc (msg, parm);
2724 static int
2725 buttonbar_event (Gpm_Event *event, void *data)
2727 WButtonBar *bb = data;
2728 int button;
2730 if (!(event->type & GPM_UP))
2731 return MOU_NORMAL;
2732 if (event->y == 2)
2733 return MOU_NORMAL;
2734 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2735 if (button < BUTTONBAR_LABELS_NUM)
2736 buttonbar_call (bb, button);
2737 return MOU_NORMAL;
2740 WButtonBar *
2741 buttonbar_new (gboolean visible)
2743 WButtonBar *bb;
2745 bb = g_new0 (WButtonBar, 1);
2747 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2748 buttonbar_callback, buttonbar_event);
2749 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2750 bb->visible = visible;
2751 widget_want_hotkey (bb->widget, 1);
2752 widget_want_cursor (bb->widget, 0);
2753 bb->btn_width = buttonbat_get_button_width ();
2755 return bb;
2758 static void
2759 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2761 g_free (bb->labels[lc_index - 1].text);
2762 bb->labels[lc_index - 1].text = g_strdup (text);
2765 /* Find ButtonBar widget in the dialog */
2766 WButtonBar *
2767 find_buttonbar (const Dlg_head *h)
2769 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2772 void
2773 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2774 const struct global_keymap_t *keymap, const Widget *receiver)
2776 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2777 unsigned long command = CK_Ignore_Key;
2779 if (keymap != NULL)
2780 command = lookup_keymap_command (keymap, KEY_F (idx));
2782 if ((text == NULL) || (text[0] == '\0'))
2783 set_label_text (bb, idx, "");
2784 else
2785 set_label_text (bb, idx, text);
2787 bb->labels[idx - 1].command = command;
2788 bb->labels[idx - 1].receiver = (Widget *) receiver;
2792 void
2793 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2795 bb->visible = visible;
2798 void
2799 buttonbar_redraw (WButtonBar *bb)
2801 if (bb != NULL)
2802 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2805 static cb_ret_t
2806 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2808 WGroupbox *g = (WGroupbox *) w;
2810 switch (msg) {
2811 case WIDGET_INIT:
2812 return MSG_HANDLED;
2814 case WIDGET_FOCUS:
2815 return MSG_NOT_HANDLED;
2817 case WIDGET_DRAW:
2818 tty_setcolor (COLOR_NORMAL);
2819 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2820 g->widget.x - g->widget.parent->x, g->widget.lines,
2821 g->widget.cols);
2823 tty_setcolor (COLOR_HOT_NORMAL);
2824 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2825 g->widget.x - g->widget.parent->x + 1);
2826 tty_print_string (g->title);
2827 return MSG_HANDLED;
2829 case WIDGET_DESTROY:
2830 g_free (g->title);
2831 return MSG_HANDLED;
2833 default:
2834 return default_proc (msg, parm);
2838 WGroupbox *
2839 groupbox_new (int y, int x, int height, int width, const char *title)
2841 WGroupbox *g = g_new (WGroupbox, 1);
2843 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2845 g->widget.options &= ~W_WANT_CURSOR;
2846 widget_want_hotkey (g->widget, 0);
2848 /* Strip existing spaces, add one space before and after the title */
2849 if (title) {
2850 char *t;
2851 t = g_strstrip (g_strdup (title));
2852 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2853 g_free (t);
2856 return g;