change hotkey from char* to int
[midnight-commander.git] / src / widget.c
blob221912784dd20a341a59604382e6851d59e1dc37
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_key_map_t */
59 #include "fileloc.h"
61 const global_key_map_t *input_map;
63 static void
64 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
66 Dlg_head *h = w->parent;
68 tty_setcolor (hotkey
69 ? (focused
70 ? DLG_HOT_FOCUSC (h)
71 : DLG_HOT_NORMALC (h))
72 : (focused
73 ? DLG_FOCUSC (h)
74 : DLG_NORMALC (h)));
77 struct hotkey_t
78 parse_hotkey (const char *text)
80 struct hotkey_t result;
81 const char *cp, *p;
82 char *hotkey = NULL;
83 int res = 0;
85 /* search for '&', that is not on the of text */
86 cp = strchr (text, '&');
87 if (cp != NULL && cp[1] != '\0') {
88 result.start = g_strndup (text, cp - text);
90 /* skip '&' */
91 cp++;
92 p = str_cget_next_char (cp);
93 hotkey = g_strndup (cp, p - cp);
94 res = g_utf8_get_char_validated (hotkey, -1);
96 if ( res < 0 )
97 result.hotkey = (int) *hotkey;
98 else
99 result.hotkey = res;
101 g_free (hotkey);
103 cp = p;
104 result.end = g_strdup (cp);
105 } else {
106 result.start = g_strdup (text);
107 result.hotkey = 0;
108 result.end = NULL;
111 return result;
113 void
114 release_hotkey (const struct hotkey_t hotkey)
116 g_free (hotkey.start);
117 g_free (hotkey.end);
121 hotkey_width (const struct hotkey_t hotkey)
123 int result;
124 int len = 0;
126 result = str_term_width1 (hotkey.start);
127 if (hotkey.hotkey > 255) {
128 len = g_unichar_to_utf8 (hotkey.hotkey, (char *) NULL);
129 if (len != 0)
130 result += len;
131 } else {
132 if (hotkey.hotkey != 0)
133 result += 1;
135 result += (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
136 return result;
139 static void
140 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
142 widget_selectcolor (w, focused, FALSE);
143 tty_print_string (hotkey.start);
145 if (hotkey.hotkey != 0) {
146 widget_selectcolor (w, focused, TRUE);
147 tty_print_anychar (hotkey.hotkey);
148 widget_selectcolor (w, focused, FALSE);
151 if (hotkey.end != NULL)
152 tty_print_string (hotkey.end);
156 /* Default callback for widgets */
157 cb_ret_t
158 default_proc (widget_msg_t msg, int parm)
160 (void) parm;
162 switch (msg) {
163 case WIDGET_INIT:
164 case WIDGET_FOCUS:
165 case WIDGET_UNFOCUS:
166 case WIDGET_DRAW:
167 case WIDGET_DESTROY:
168 case WIDGET_CURSOR:
169 case WIDGET_IDLE:
170 return MSG_HANDLED;
172 default:
173 return MSG_NOT_HANDLED;
177 static int button_event (Gpm_Event *event, void *);
179 int quote = 0;
181 static cb_ret_t
182 button_callback (Widget *w, widget_msg_t msg, int parm)
184 WButton *b = (WButton *) w;
185 int stop = 0;
186 int off = 0;
187 Dlg_head *h = b->widget.parent;
189 switch (msg) {
190 case WIDGET_HOTKEY:
192 * Don't let the default button steal Enter from the current
193 * button. This is a workaround for the flawed event model
194 * when hotkeys are sent to all widgets before the key is
195 * handled by the current widget.
197 if (parm == '\n' && h->current == &b->widget) {
198 button_callback (w, WIDGET_KEY, ' ');
199 return MSG_HANDLED;
202 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
203 button_callback (w, WIDGET_KEY, ' ');
204 return MSG_HANDLED;
207 if (b->text.hotkey != 0) {
208 if (b->text.hotkey == parm) {
209 button_callback (w, WIDGET_KEY, ' ');
210 return MSG_HANDLED;
213 return MSG_NOT_HANDLED;
215 case WIDGET_KEY:
216 if (parm != ' ' && parm != '\n')
217 return MSG_NOT_HANDLED;
219 if (b->callback)
220 stop = (*b->callback) (b->action);
221 if (!b->callback || stop) {
222 h->ret_value = b->action;
223 dlg_stop (h);
225 return MSG_HANDLED;
227 case WIDGET_CURSOR:
228 switch (b->flags) {
229 case DEFPUSH_BUTTON:
230 off = 3;
231 break;
232 case NORMAL_BUTTON:
233 off = 2;
234 break;
235 case NARROW_BUTTON:
236 off = 1;
237 break;
238 case HIDDEN_BUTTON:
239 default:
240 off = 0;
241 break;
243 widget_move (&b->widget, 0, b->hotpos + off);
244 return MSG_HANDLED;
246 case WIDGET_UNFOCUS:
247 case WIDGET_FOCUS:
248 case WIDGET_DRAW:
249 if (msg == WIDGET_UNFOCUS)
250 b->selected = 0;
251 else if (msg == WIDGET_FOCUS)
252 b->selected = 1;
254 widget_selectcolor (w, b->selected, FALSE);
255 widget_move (w, 0, 0);
257 switch (b->flags) {
258 case DEFPUSH_BUTTON:
259 tty_print_string ("[< ");
260 break;
261 case NORMAL_BUTTON:
262 tty_print_string ("[ ");
263 break;
264 case NARROW_BUTTON:
265 tty_print_string ("[");
266 break;
267 case HIDDEN_BUTTON:
268 default:
269 return MSG_HANDLED;
272 draw_hotkey (w, b->text, b->selected);
274 switch (b->flags) {
275 case DEFPUSH_BUTTON:
276 tty_print_string (" >]");
277 break;
278 case NORMAL_BUTTON:
279 tty_print_string (" ]");
280 break;
281 case NARROW_BUTTON:
282 tty_print_string ("]");
283 break;
285 return MSG_HANDLED;
287 case WIDGET_DESTROY:
288 release_hotkey (b->text);
289 return MSG_HANDLED;
291 default:
292 return default_proc (msg, parm);
296 static int
297 button_event (Gpm_Event *event, void *data)
299 WButton *b = data;
301 if (event->type & (GPM_DOWN|GPM_UP)){
302 Dlg_head *h=b->widget.parent;
303 dlg_select_widget (b);
304 if (event->type & GPM_UP){
305 button_callback ((Widget *) data, WIDGET_KEY, ' ');
306 (*h->callback) (h, DLG_POST_KEY, ' ');
307 return MOU_NORMAL;
310 return MOU_NORMAL;
314 button_get_len (const WButton *b)
316 int ret = hotkey_width (b->text);
317 switch (b->flags) {
318 case DEFPUSH_BUTTON:
319 ret += 6;
320 break;
321 case NORMAL_BUTTON:
322 ret += 4;
323 break;
324 case NARROW_BUTTON:
325 ret += 2;
326 break;
327 case HIDDEN_BUTTON:
328 default:
329 return 0;
331 return ret;
334 WButton *
335 button_new (int y, int x, int action, int flags, const char *text,
336 bcback callback)
338 WButton *b = g_new (WButton, 1);
340 b->action = action;
341 b->flags = flags;
342 b->text = parse_hotkey (text);
344 init_widget (&b->widget, y, x, 1, button_get_len (b),
345 button_callback, button_event);
347 b->selected = 0;
348 b->callback = callback;
349 widget_want_hotkey (b->widget, 1);
350 b->hotpos = (b->text.hotkey != 0) ? str_term_width1 (b->text.start) : -1;
352 return b;
355 const char *
356 button_get_text (const WButton *b)
358 if (b->text.hotkey != NULL)
359 return g_strconcat (b->text.start, "&", b->text.hotkey,
360 b->text.end, NULL);
361 else
362 return g_strdup (b->text.start);
365 void
366 button_set_text (WButton *b, const char *text)
368 release_hotkey (b->text);
369 b->text = parse_hotkey (text);
370 b->widget.cols = button_get_len (b);
371 dlg_redraw (b->widget.parent);
375 /* Radio button widget */
376 static int radio_event (Gpm_Event *event, void *);
378 static cb_ret_t
379 radio_callback (Widget *w, widget_msg_t msg, int parm)
381 WRadio *r = (WRadio *) w;
382 int i;
383 Dlg_head *h = r->widget.parent;
385 switch (msg) {
386 case WIDGET_HOTKEY:
388 int i, lp = parm;
390 for (i = 0; i < r->count; i++) {
391 if (r->texts[i].hotkey != 0) {
392 int c = r->texts[i].hotkey;
394 if (c != lp)
395 continue;
396 r->pos = i;
398 /* Take action */
399 radio_callback (w, WIDGET_KEY, ' ');
400 return MSG_HANDLED;
404 return MSG_NOT_HANDLED;
406 case WIDGET_KEY:
407 switch (parm) {
408 case ' ':
409 r->sel = r->pos;
410 (*h->callback) (h, DLG_ACTION, 0);
411 radio_callback (w, WIDGET_FOCUS, ' ');
412 return MSG_HANDLED;
414 case KEY_UP:
415 case KEY_LEFT:
416 if (r->pos > 0) {
417 r->pos--;
418 return MSG_HANDLED;
420 return MSG_NOT_HANDLED;
422 case KEY_DOWN:
423 case KEY_RIGHT:
424 if (r->count - 1 > r->pos) {
425 r->pos++;
426 return MSG_HANDLED;
429 return MSG_NOT_HANDLED;
431 case WIDGET_CURSOR:
432 (*h->callback) (h, DLG_ACTION, 0);
433 radio_callback (w, WIDGET_FOCUS, ' ');
434 widget_move (&r->widget, r->pos, 1);
435 return MSG_HANDLED;
437 case WIDGET_UNFOCUS:
438 case WIDGET_FOCUS:
439 case WIDGET_DRAW:
440 for (i = 0; i < r->count; i++) {
441 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
442 widget_selectcolor (w, focused, FALSE);
443 widget_move (&r->widget, i, 0);
444 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
445 draw_hotkey (w, r->texts[i], focused);
447 return MSG_HANDLED;
449 case WIDGET_DESTROY:
450 for (i = 0; i < r->count; i++) {
451 release_hotkey (r->texts[i]);
453 g_free (r->texts);
454 return MSG_HANDLED;
456 default:
457 return default_proc (msg, parm);
461 static int
462 radio_event (Gpm_Event *event, void *data)
464 WRadio *r = data;
465 Widget *w = data;
467 if (event->type & (GPM_DOWN|GPM_UP)){
468 Dlg_head *h = r->widget.parent;
470 r->pos = event->y - 1;
471 dlg_select_widget (r);
472 if (event->type & GPM_UP){
473 radio_callback (w, WIDGET_KEY, ' ');
474 radio_callback (w, WIDGET_FOCUS, 0);
475 (*h->callback) (h, DLG_POST_KEY, ' ');
476 return MOU_NORMAL;
479 return MOU_NORMAL;
482 WRadio *
483 radio_new (int y, int x, int count, const char **texts)
485 WRadio *result = g_new (WRadio, 1);
486 int i, max, m;
488 /* Compute the longest string */
489 result->texts = g_new (struct hotkey_t, count);
491 max = 0;
492 for (i = 0; i < count; i++){
493 result->texts[i] = parse_hotkey (texts[i]);
494 m = hotkey_width (result->texts[i]);
495 if (m > max)
496 max = m;
499 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
500 result->state = 1;
501 result->pos = 0;
502 result->sel = 0;
503 result->count = count;
504 widget_want_hotkey (result->widget, 1);
506 return result;
510 /* Checkbutton widget */
512 static int check_event (Gpm_Event *event, void *);
514 static cb_ret_t
515 check_callback (Widget *w, widget_msg_t msg, int parm)
517 WCheck *c = (WCheck *) w;
518 Dlg_head *h = c->widget.parent;
520 switch (msg) {
521 case WIDGET_HOTKEY:
522 if (c->text.hotkey != 0) {
523 if (c->text.hotkey == parm) {
524 check_callback (w, WIDGET_KEY, ' '); /* make action */
525 return MSG_HANDLED;
528 return MSG_NOT_HANDLED;
530 case WIDGET_KEY:
531 if (parm != ' ')
532 return MSG_NOT_HANDLED;
533 c->state ^= C_BOOL;
534 c->state ^= C_CHANGE;
535 (*h->callback) (h, DLG_ACTION, 0);
536 check_callback (w, WIDGET_FOCUS, ' ');
537 return MSG_HANDLED;
539 case WIDGET_CURSOR:
540 widget_move (&c->widget, 0, 1);
541 return MSG_HANDLED;
543 case WIDGET_FOCUS:
544 case WIDGET_UNFOCUS:
545 case WIDGET_DRAW:
546 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
547 widget_move (&c->widget, 0, 0);
548 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
549 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
550 return MSG_HANDLED;
552 case WIDGET_DESTROY:
553 release_hotkey (c->text);
554 return MSG_HANDLED;
556 default:
557 return default_proc (msg, parm);
561 static int
562 check_event (Gpm_Event *event, void *data)
564 WCheck *c = data;
565 Widget *w = data;
567 if (event->type & (GPM_DOWN|GPM_UP)){
568 Dlg_head *h = c->widget.parent;
570 dlg_select_widget (c);
571 if (event->type & GPM_UP){
572 check_callback (w, WIDGET_KEY, ' ');
573 check_callback (w, WIDGET_FOCUS, 0);
574 (*h->callback) (h, DLG_POST_KEY, ' ');
575 return MOU_NORMAL;
578 return MOU_NORMAL;
581 WCheck *
582 check_new (int y, int x, int state, const char *text)
584 WCheck *c = g_new (WCheck, 1);
586 c->text = parse_hotkey (text);
588 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
589 check_callback, check_event);
590 c->state = state ? C_BOOL : 0;
591 widget_want_hotkey (c->widget, 1);
593 return c;
597 /* Label widget */
599 static cb_ret_t
600 label_callback (Widget *w, widget_msg_t msg, int parm)
602 WLabel *l = (WLabel *) w;
603 Dlg_head *h = l->widget.parent;
605 switch (msg) {
606 case WIDGET_INIT:
607 return MSG_HANDLED;
609 /* We don't want to get the focus */
610 case WIDGET_FOCUS:
611 return MSG_NOT_HANDLED;
613 case WIDGET_DRAW:
615 char *p = l->text, *q, c = 0;
616 int y = 0;
618 if (!l->text)
619 return MSG_HANDLED;
621 if (l->transparent)
622 tty_setcolor (DEFAULT_COLOR);
623 else
624 tty_setcolor (DLG_NORMALC (h));
626 for (;;) {
627 q = strchr (p, '\n');
628 if (q != NULL) {
629 c = q[0];
630 q[0] = '\0';
633 widget_move (&l->widget, y, 0);
634 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
636 if (q == NULL)
637 break;
638 q[0] = c;
639 p = q + 1;
640 y++;
642 return MSG_HANDLED;
645 case WIDGET_DESTROY:
646 g_free (l->text);
647 return MSG_HANDLED;
649 default:
650 return default_proc (msg, parm);
654 void
655 label_set_text (WLabel *label, const char *text)
657 int newcols = label->widget.cols;
658 int newlines;
660 if (label->text && text && !strcmp (label->text, text))
661 return; /* Flickering is not nice */
663 g_free (label->text);
665 if (text != NULL) {
666 label->text = g_strdup (text);
667 if (label->auto_adjust_cols) {
668 str_msg_term_size (text, &newlines, &newcols);
669 if (newcols > label->widget.cols)
670 label->widget.cols = newcols;
671 if (newlines > label->widget.lines)
672 label->widget.lines = newlines;
674 } else label->text = NULL;
676 if (label->widget.parent)
677 label_callback ((Widget *) label, WIDGET_DRAW, 0);
679 if (newcols < label->widget.cols)
680 label->widget.cols = newcols;
683 WLabel *
684 label_new (int y, int x, const char *text)
686 WLabel *l;
687 int cols = 1;
688 int lines = 1;
690 if (text != NULL)
691 str_msg_term_size (text, &lines, &cols);
693 l = g_new (WLabel, 1);
694 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
695 l->text = (text != NULL) ? g_strdup (text) : NULL;
696 l->auto_adjust_cols = 1;
697 l->transparent = 0;
698 widget_want_cursor (l->widget, 0);
699 return l;
703 /* Gauge widget (progress indicator) */
704 /* Currently width is hardcoded here for text mode */
705 #define gauge_len 47
707 static cb_ret_t
708 gauge_callback (Widget *w, widget_msg_t msg, int parm)
710 WGauge *g = (WGauge *) w;
711 Dlg_head *h = g->widget.parent;
713 if (msg == WIDGET_INIT)
714 return MSG_HANDLED;
716 /* We don't want to get the focus */
717 if (msg == WIDGET_FOCUS)
718 return MSG_NOT_HANDLED;
720 if (msg == WIDGET_DRAW){
721 widget_move (&g->widget, 0, 0);
722 tty_setcolor (DLG_NORMALC (h));
723 if (!g->shown)
724 tty_printf ("%*s", gauge_len, "");
725 else {
726 int percentage, columns;
727 long total = g->max, done = g->current;
729 if (total <= 0 || done < 0) {
730 done = 0;
731 total = 100;
733 if (done > total)
734 done = total;
735 while (total > 65535) {
736 total /= 256;
737 done /= 256;
739 percentage = (200 * done / total + 1) / 2;
740 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
741 tty_print_char ('[');
742 tty_setcolor (GAUGE_COLOR);
743 tty_printf ("%*s", (int) columns, "");
744 tty_setcolor (DLG_NORMALC (h));
745 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
747 return MSG_HANDLED;
750 return default_proc (msg, parm);
753 void
754 gauge_set_value (WGauge *g, int max, int current)
756 if (g->current == current && g->max == max)
757 return; /* Do not flicker */
758 if (max == 0)
759 max = 1; /* I do not like division by zero :) */
761 g->current = current;
762 g->max = max;
763 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
766 void
767 gauge_show (WGauge *g, int shown)
769 if (g->shown == shown)
770 return;
771 g->shown = shown;
772 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
775 WGauge *
776 gauge_new (int y, int x, int shown, int max, int current)
778 WGauge *g = g_new (WGauge, 1);
780 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
781 g->shown = shown;
782 if (max == 0)
783 max = 1; /* I do not like division by zero :) */
784 g->max = max;
785 g->current = current;
786 widget_want_cursor (g->widget, 0);
787 return g;
791 /* Input widget */
793 /* {{{ history button */
795 #define LARGE_HISTORY_BUTTON 1
797 #ifdef LARGE_HISTORY_BUTTON
798 # define HISTORY_BUTTON_WIDTH 3
799 #else
800 # define HISTORY_BUTTON_WIDTH 1
801 #endif
803 #define should_show_history_button(in) \
804 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
806 static void draw_history_button (WInput * in)
808 char c;
809 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
810 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
811 #ifdef LARGE_HISTORY_BUTTON
813 Dlg_head *h;
814 h = in->widget.parent;
815 tty_setcolor (NORMAL_COLOR);
816 tty_print_string ("[ ]");
817 /* Too distracting: tty_setcolor (MARKED_COLOR); */
818 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
819 tty_print_char (c);
821 #else
822 tty_setcolor (MARKED_COLOR);
823 tty_print_char (c);
824 #endif
827 /* }}} history button */
830 /* Input widgets now have a global kill ring */
831 /* Pointer to killed data */
832 static char *kill_buffer = 0;
834 void
835 update_input (WInput *in, int clear_first)
837 int has_history = 0;
838 int i;
839 int buf_len = str_length (in->buffer);
840 const char *cp;
841 int pw;
843 if (should_show_history_button (in))
844 has_history = HISTORY_BUTTON_WIDTH;
846 if (in->disable_update)
847 return;
849 pw = str_term_width2 (in->buffer, in->point);
851 /* Make the point visible */
852 if ((pw < in->term_first_shown) ||
853 (pw >= in->term_first_shown + in->field_width - has_history)) {
855 in->term_first_shown = pw - (in->field_width / 3);
856 if (in->term_first_shown < 0)
857 in->term_first_shown = 0;
860 /* Adjust the mark */
861 if (in->mark > buf_len)
862 in->mark = buf_len;
864 if (has_history)
865 draw_history_button (in);
867 tty_setcolor (in->color);
869 widget_move (&in->widget, 0, 0);
871 if (!in->is_password) {
872 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
873 in->field_width - has_history));
874 } else {
875 cp = in->buffer;
876 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
877 if (i >= 0) {
878 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
880 if (cp[0] != '\0') str_cnext_char (&cp);
884 if (clear_first)
885 in->first = 0;
888 void
889 winput_set_origin (WInput *in, int x, int field_width)
891 in->widget.x = x;
892 in->field_width = in->widget.cols = field_width;
893 update_input (in, 0);
896 /* {{{ history saving and loading */
898 int num_history_items_recorded = 60;
901 This loads and saves the history of an input line to and from the
902 widget. It is called with the widgets history name on creation of the
903 widget, and returns the GList list. It stores histories in the file
904 ~/.mc/history in using the profile code.
906 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
907 function) then input_new assigns the default text to be the last text
908 entered, or "" if not found.
911 GList *
912 history_get (const char *input_name)
914 size_t i;
915 GList *hist = NULL;
916 char *profile;
917 mc_config_t *cfg;
918 char **keys;
919 size_t keys_num = 0;
920 char *this_entry;
922 if (!num_history_items_recorded) /* this is how to disable */
923 return NULL;
924 if (!input_name || !*input_name)
925 return NULL;
927 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
928 cfg = mc_config_init (profile);
930 /* get number of keys */
931 keys = mc_config_get_keys (cfg, input_name, &keys_num);
932 g_strfreev (keys);
934 for (i = 0; i < keys_num; i++) {
935 char key_name[BUF_TINY];
936 g_snprintf (key_name, sizeof (key_name), "%lu", (unsigned long)i);
937 this_entry = mc_config_get_string (cfg, input_name, key_name, "");
939 if (this_entry && *this_entry)
940 hist = list_append_unique (hist, this_entry);
943 mc_config_deinit (cfg);
944 g_free (profile);
946 /* return pointer to the last entry in the list */
947 return g_list_last (hist);
950 void
951 history_put (const char *input_name, GList *h)
953 int i;
954 char *profile;
955 mc_config_t *cfg;
957 if (!input_name)
958 return;
960 if (!*input_name)
961 return;
963 if (!h)
964 return;
966 if (!num_history_items_recorded) /* this is how to disable */
967 return;
969 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
971 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
972 close (i);
974 /* Make sure the history is only readable by the user */
975 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
976 g_free (profile);
977 return;
980 /* go to end of list */
981 h = g_list_last (h);
983 /* go back 60 places */
984 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
985 h = g_list_previous (h);
987 cfg = mc_config_init(profile);
989 if (input_name)
990 mc_config_del_group(cfg,input_name);
992 /* dump histories into profile */
993 for (i = 0; h; h = g_list_next (h)) {
994 char *text;
996 text = (char *) h->data;
998 /* We shouldn't have null entries, but let's be sure */
999 if (text && *text) {
1000 char key_name[BUF_TINY];
1001 g_snprintf (key_name, sizeof (key_name), "%d", i++);
1002 mc_config_set_string(cfg,input_name, key_name, text);
1006 mc_config_save_file (cfg);
1007 mc_config_deinit(cfg);
1008 g_free (profile);
1011 /* }}} history saving and loading */
1014 /* {{{ history display */
1016 static const char *
1017 i18n_htitle (void)
1019 return _(" History ");
1022 static void
1023 listbox_fwd (WListbox *l)
1025 if (l->current != l->list->prev)
1026 listbox_select_entry (l, l->current->next);
1027 else
1028 listbox_select_first (l);
1031 typedef struct {
1032 Widget *widget;
1033 int count;
1034 size_t maxlen;
1035 } dlg_hist_data;
1037 static cb_ret_t
1038 dlg_hist_reposition (Dlg_head *dlg_head)
1040 dlg_hist_data *data;
1041 int x = 0, y, he, wi;
1043 /* guard checks */
1044 if ((dlg_head == NULL)
1045 || (dlg_head->data == NULL))
1046 return MSG_NOT_HANDLED;
1048 data = (dlg_hist_data *) dlg_head->data;
1050 y = data->widget->y;
1051 he = data->count + 2;
1053 if (he <= y || y > (LINES - 6)) {
1054 he = min (he, y - 1);
1055 y -= he;
1056 } else {
1057 y++;
1058 he = min (he, LINES - y);
1061 if (data->widget->x > 2)
1062 x = data->widget->x - 2;
1064 wi = data->maxlen + 4;
1066 if ((wi + x) > COLS) {
1067 wi = min (wi, COLS);
1068 x = COLS - wi;
1071 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1073 return MSG_HANDLED;
1076 static cb_ret_t
1077 dlg_hist_callback (Dlg_head *h, dlg_msg_t msg, int parm)
1079 switch (msg) {
1080 case DLG_RESIZE:
1081 return dlg_hist_reposition (h);
1083 default:
1084 return default_dlg_callback (h, msg, parm);
1088 char *
1089 show_hist (GList *history, Widget *widget)
1091 GList *hi, *z;
1092 size_t maxlen, i, count = 0;
1093 char *q, *r = NULL;
1094 Dlg_head *query_dlg;
1095 WListbox *query_list;
1096 dlg_hist_data hist_data;
1098 if (history == NULL)
1099 return NULL;
1101 maxlen = str_term_width1 (i18n_htitle ());
1103 z = g_list_first (history);
1104 hi = z;
1105 while (hi) {
1106 i = str_term_width1 ((char *) hi->data);
1107 maxlen = max (maxlen, i);
1108 count++;
1109 hi = g_list_next (hi);
1112 hist_data.maxlen = maxlen;
1113 hist_data.widget = widget;
1114 hist_data.count = count;
1116 query_dlg =
1117 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1118 "[History-query]", i18n_htitle (), DLG_COMPACT);
1119 query_dlg->data = &hist_data;
1121 query_list = listbox_new (1, 1, 2, 2, NULL);
1123 /* this call makes list stick to all sides of dialog, effectively make
1124 it be resized with dialog */
1125 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1127 /* to avoid diplicating of (calculating sizes in two places)
1128 code, call dlg_hist_callback function here, to set dialog and
1129 controls positions.
1130 The main idea - create 4x4 dialog and add 2x2 list in
1131 center of it, and let dialog function resize it to needed
1132 size. */
1133 dlg_hist_callback (query_dlg, DLG_RESIZE, 0);
1135 if (query_dlg->y < widget->y) {
1136 /* traverse */
1137 hi = z;
1138 while (hi) {
1139 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1140 0, (char *) hi->data, NULL);
1141 hi = g_list_next (hi);
1143 listbox_select_last (query_list);
1144 } else {
1145 /* traverse backwards */
1146 hi = g_list_last (history);
1147 while (hi) {
1148 listbox_add_item (query_list, LISTBOX_APPEND_AT_END,
1149 0, (char *) hi->data, NULL);
1150 hi = g_list_previous (hi);
1154 if (run_dlg (query_dlg) != B_CANCEL) {
1155 listbox_get_current (query_list, &q, NULL);
1156 if (q != NULL)
1157 r = g_strdup (q);
1159 destroy_dlg (query_dlg);
1160 return r;
1163 static void
1164 do_show_hist (WInput *in)
1166 char *r;
1167 r = show_hist (in->history, &in->widget);
1168 if (r) {
1169 assign_text (in, r);
1170 g_free (r);
1174 /* }}} history display */
1176 static void
1177 input_destroy (WInput *in)
1179 if (!in){
1180 fprintf (stderr, "Internal error: null Input *\n");
1181 exit (1);
1184 new_input (in);
1186 if (in->history){
1187 if (!in->is_password) /* don't save passwords ;-) */
1188 history_put (in->history_name, in->history);
1190 in->history = g_list_first (in->history);
1191 g_list_foreach (in->history, (GFunc) g_free, NULL);
1192 g_list_free (in->history);
1195 g_free (in->buffer);
1196 free_completions (in);
1197 g_free (in->history_name);
1200 void
1201 input_disable_update (WInput *in)
1203 in->disable_update++;
1206 void
1207 input_enable_update (WInput *in)
1209 in->disable_update--;
1210 update_input (in, 0);
1213 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1216 push_history (WInput *in, const char *text)
1218 static int i18n;
1219 /* input widget where urls with passwords are entered without any
1220 vfs prefix */
1221 static const char *password_input_fields[] = {
1222 N_(" Link to a remote machine "),
1223 N_(" FTP to machine "),
1224 N_(" SMB link to machine ")
1226 char *t;
1227 const char *p;
1228 size_t i;
1230 if (!i18n) {
1231 i18n = 1;
1232 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1233 password_input_fields[i] = _(password_input_fields[i]);
1236 for (p = text; *p == ' ' || *p == '\t'; p++);
1237 if (!*p)
1238 return 0;
1240 if (in->history) {
1241 /* Avoid duplicated entries */
1242 in->history = g_list_last (in->history);
1243 if (!strcmp ((char *) in->history->data, text))
1244 return 1;
1247 t = g_strdup (text);
1249 if (in->history_name) {
1250 p = in->history_name + 3;
1251 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1252 if (strcmp (p, password_input_fields[i]) == 0)
1253 break;
1254 if (i < ELEMENTS (password_input_fields))
1255 strip_password (t, 0);
1256 else
1257 strip_password (t, 1);
1260 in->history = list_append_unique (in->history, t);
1261 in->need_push = 0;
1263 return 2;
1266 #undef ELEMENTS
1268 /* Cleans the input line and adds the current text to the history */
1269 void
1270 new_input (WInput *in)
1272 if (in->buffer)
1273 push_history (in, in->buffer);
1274 in->need_push = 1;
1275 in->buffer[0] = '\0';
1276 in->point = 0;
1277 in->charpoint = 0;
1278 in->mark = 0;
1279 free_completions (in);
1280 update_input (in, 0);
1283 static void
1284 move_buffer_backward (WInput *in, int start, int end)
1286 int i, pos, len;
1287 int str_len = str_length (in->buffer);
1288 if (start >= str_len || end > str_len + 1) return;
1290 pos = str_offset_to_pos (in->buffer, start);
1291 len = str_offset_to_pos (in->buffer, end) - pos;
1293 for (i = pos; in->buffer[i + len - 1]; i++)
1294 in->buffer[i] = in->buffer[i + len];
1297 static cb_ret_t
1298 insert_char (WInput *in, int c_code)
1300 size_t i;
1301 int res;
1303 if (c_code == -1)
1304 return MSG_NOT_HANDLED;
1306 if (in->charpoint >= MB_LEN_MAX)
1307 return MSG_HANDLED;
1309 in->charbuf[in->charpoint] = c_code;
1310 in->charpoint++;
1312 res = str_is_valid_char (in->charbuf, in->charpoint);
1313 if (res < 0) {
1314 if (res != -2)
1315 in->charpoint = 0; /* broken multibyte char, skip */
1316 return MSG_HANDLED;
1319 in->need_push = 1;
1320 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1321 /* Expand the buffer */
1322 size_t new_length = in->current_max_size +
1323 in->field_width + in->charpoint;
1324 char *narea = g_try_renew (char, in->buffer, new_length);
1325 if (narea){
1326 in->buffer = narea;
1327 in->current_max_size = new_length;
1331 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1332 /* bytes from begin */
1333 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1334 /* move chars */
1335 size_t rest_bytes = strlen (in->buffer + ins_point);
1337 for (i = rest_bytes + 1; i > 0; i--)
1338 in->buffer[ins_point + i + in->charpoint - 1] =
1339 in->buffer[ins_point + i - 1];
1341 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1342 in->point++;
1345 in->charpoint = 0;
1346 return MSG_HANDLED;
1349 static void
1350 beginning_of_line (WInput *in)
1352 in->point = 0;
1353 in->charpoint = 0;
1356 static void
1357 end_of_line (WInput *in)
1359 in->point = str_length (in->buffer);
1360 in->charpoint = 0;
1363 static void
1364 backward_char (WInput *in)
1366 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1368 if (in->point > 0) {
1369 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1371 in->charpoint = 0;
1374 static void
1375 forward_char (WInput *in)
1377 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1378 if (act[0] != '\0') {
1379 in->point+= str_cnext_noncomb_char (&act);
1381 in->charpoint = 0;
1384 static void
1385 forward_word (WInput * in)
1387 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1389 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1390 str_cnext_char (&p);
1391 in->point++;
1393 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1394 str_cnext_char (&p);
1395 in->point++;
1399 static void
1400 backward_word (WInput *in)
1402 const char *p;
1403 const char *p_tmp;
1405 for (
1406 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1407 (p != in->buffer) && (p[0] == '\0');
1408 p-- , in->point--
1411 while (p != in->buffer) {
1412 p_tmp = p;
1413 str_cprev_char (&p);
1414 if (!str_isspace (p) && !str_ispunct (p)) {
1415 p = p_tmp;
1416 break;
1418 in->point--;
1420 while (p != in->buffer) {
1421 str_cprev_char (&p);
1422 if (str_isspace (p) || str_ispunct (p))
1423 break;
1425 in->point--;
1429 static void
1430 key_left (WInput *in)
1432 backward_char (in);
1435 static void
1436 key_ctrl_left (WInput *in)
1438 backward_word (in);
1441 static void
1442 key_right (WInput *in)
1444 forward_char (in);
1447 static void
1448 key_ctrl_right (WInput *in)
1450 forward_word (in);
1452 static void
1453 backward_delete (WInput *in)
1455 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1456 int start;
1458 if (in->point == 0)
1459 return;
1461 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1462 move_buffer_backward(in, start, in->point);
1463 in->charpoint = 0;
1464 in->need_push = 1;
1465 in->point = start;
1468 static void
1469 delete_char (WInput *in)
1471 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1472 int end = in->point;
1474 end+= str_cnext_noncomb_char (&act);
1476 move_buffer_backward(in, in->point, end);
1477 in->charpoint = 0;
1478 in->need_push = 1;
1481 static void
1482 copy_region (WInput *in, int x_first, int x_last)
1484 int first = min (x_first, x_last);
1485 int last = max (x_first, x_last);
1487 if (last == first)
1488 return;
1490 g_free (kill_buffer);
1492 first = str_offset_to_pos (in->buffer, first);
1493 last = str_offset_to_pos (in->buffer, last);
1495 kill_buffer = g_strndup(in->buffer + first, last - first);
1498 static void
1499 delete_region (WInput *in, int x_first, int x_last)
1501 int first = min (x_first, x_last);
1502 int last = max (x_first, x_last);
1503 size_t len;
1505 in->point = first;
1506 in->mark = first;
1507 last = str_offset_to_pos (in->buffer, last);
1508 first = str_offset_to_pos (in->buffer, first);
1509 len = strlen (&in->buffer[last]) + 1;
1510 memmove (&in->buffer[first], &in->buffer[last], len);
1511 in->charpoint = 0;
1512 in->need_push = 1;
1515 static void
1516 kill_word (WInput *in)
1518 int old_point = in->point;
1519 int new_point;
1521 forward_word (in);
1522 new_point = in->point;
1523 in->point = old_point;
1525 copy_region (in, old_point, new_point);
1526 delete_region (in, old_point, new_point);
1527 in->need_push = 1;
1528 in->charpoint = 0;
1529 in->charpoint = 0;
1532 static void
1533 back_kill_word (WInput *in)
1535 int old_point = in->point;
1536 int new_point;
1538 backward_word (in);
1539 new_point = in->point;
1540 in->point = old_point;
1542 copy_region (in, old_point, new_point);
1543 delete_region (in, old_point, new_point);
1544 in->need_push = 1;
1547 static void
1548 set_mark (WInput *in)
1550 in->mark = in->point;
1553 static void
1554 kill_save (WInput *in)
1556 copy_region (in, in->mark, in->point);
1559 static void
1560 kill_region (WInput *in)
1562 kill_save (in);
1563 delete_region (in, in->point, in->mark);
1566 static void
1567 yank (WInput *in)
1569 char *p;
1571 if (!kill_buffer)
1572 return;
1573 in->charpoint = 0;
1574 for (p = kill_buffer; *p; p++)
1575 insert_char (in, *p);
1576 in->charpoint = 0;
1579 static void
1580 kill_line (WInput *in)
1582 int chp = str_offset_to_pos (in->buffer, in->point);
1583 g_free (kill_buffer);
1584 kill_buffer = g_strdup (&in->buffer[chp]);
1585 in->buffer[chp] = '\0';
1586 in->charpoint = 0;
1589 void
1590 assign_text (WInput *in, const char *text)
1592 free_completions (in);
1593 g_free (in->buffer);
1594 in->buffer = g_strdup (text); /* was in->buffer->text */
1595 in->current_max_size = strlen (in->buffer) + 1;
1596 in->point = str_length (in->buffer);
1597 in->mark = 0;
1598 in->need_push = 1;
1599 in->charpoint = 0;
1602 static void
1603 hist_prev (WInput *in)
1605 if (!in->history)
1606 return;
1608 if (in->need_push) {
1609 switch (push_history (in, in->buffer)) {
1610 case 2:
1611 in->history = g_list_previous (in->history);
1612 break;
1613 case 1:
1614 if (in->history->prev)
1615 in->history = g_list_previous (in->history);
1616 break;
1617 case 0:
1618 break;
1620 } else if (in->history->prev)
1621 in->history = g_list_previous (in->history);
1622 else
1623 return;
1624 assign_text (in, (char *) in->history->data);
1625 in->need_push = 0;
1628 static void
1629 hist_next (WInput *in)
1631 if (in->need_push) {
1632 switch (push_history (in, in->buffer)) {
1633 case 2:
1634 assign_text (in, "");
1635 return;
1636 case 0:
1637 return;
1641 if (!in->history)
1642 return;
1644 if (!in->history->next) {
1645 assign_text (in, "");
1646 return;
1649 in->history = g_list_next (in->history);
1650 assign_text (in, (char *) in->history->data);
1651 in->need_push = 0;
1654 static void
1655 port_region_marked_for_delete (WInput *in)
1657 in->buffer[0] = '\0';
1658 in->point = 0;
1659 in->first = 0;
1660 in->charpoint = 0;
1663 static cb_ret_t
1664 input_execute_cmd (WInput *in, int command)
1666 cb_ret_t res = MSG_HANDLED;
1668 switch (command) {
1669 case CK_InputBol:
1670 beginning_of_line (in);
1671 break;
1672 case CK_InputEol:
1673 end_of_line (in);
1674 break;
1675 case CK_InputMoveLeft:
1676 key_left (in);
1677 break;
1678 case CK_InputWordLeft:
1679 key_ctrl_left (in);
1680 break;
1681 case CK_InputMoveRight:
1682 key_right (in);
1683 break;
1684 case CK_InputWordRight:
1685 key_ctrl_right (in);
1686 break;
1687 case CK_InputBackwardChar:
1688 backward_char (in);
1689 break;
1690 case CK_InputBackwardWord:
1691 backward_word (in);
1692 break;
1693 case CK_InputForwardChar:
1694 forward_char (in);
1695 break;
1696 case CK_InputForwardWord:
1697 forward_word (in);
1698 break;
1699 case CK_InputBackwardDelete:
1700 backward_delete (in);
1701 break;
1702 case CK_InputDeleteChar:
1703 delete_char (in);
1704 break;
1705 case CK_InputKillWord:
1706 kill_word (in);
1707 break;
1708 case CK_InputBackwardKillWord:
1709 back_kill_word (in);
1710 break;
1711 case CK_InputSetMark:
1712 set_mark (in);
1713 break;
1714 case CK_InputKillRegion:
1715 kill_region (in);
1716 break;
1717 case CK_InputKillSave:
1718 kill_save (in);
1719 break;
1720 case CK_InputYank:
1721 yank (in);
1722 break;
1723 case CK_InputKillLine:
1724 kill_line (in);
1725 break;
1726 case CK_InputHistoryPrev:
1727 hist_prev (in);
1728 break;
1729 case CK_InputHistoryNext:
1730 hist_next (in);
1731 break;
1732 case CK_InputHistoryShow:
1733 do_show_hist (in);
1734 break;
1735 case CK_InputComplete:
1736 complete (in);
1737 break;
1738 default:
1739 res = MSG_NOT_HANDLED;
1742 return res;
1745 /* This function is a test for a special input key used in complete.c */
1746 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1747 and 2 if it is a complete key */
1749 is_in_input_map (WInput *in, int key)
1751 int i;
1753 for (i = 0; input_map[i].key; i++) {
1754 if (key == input_map[i].key) {
1755 input_execute_cmd (in, input_map[i].command);
1756 if (input_map[i].command == CK_InputComplete)
1757 return 2;
1758 else
1759 return 1;
1762 return 0;
1765 cb_ret_t
1766 handle_char (WInput *in, int key)
1768 cb_ret_t v;
1769 int i;
1771 v = MSG_NOT_HANDLED;
1773 if (quote) {
1774 free_completions (in);
1775 v = insert_char (in, key);
1776 update_input (in, 1);
1777 quote = 0;
1778 return v;
1781 for (i = 0; input_map[i].key; i++) {
1782 if (key == input_map[i].key) {
1783 if (input_map[i].command != CK_InputComplete)
1784 free_completions (in);
1785 input_execute_cmd (in, input_map[i].command);
1786 update_input (in, 1);
1787 v = MSG_HANDLED;
1788 break;
1791 if (input_map[i].command == 0) {
1792 if (key > 255)
1793 return MSG_NOT_HANDLED;
1794 if (in->first)
1795 port_region_marked_for_delete (in);
1796 free_completions (in);
1797 v = insert_char (in, key);
1799 update_input (in, 1);
1800 return v;
1803 /* Inserts text in input line */
1804 void
1805 stuff (WInput *in, const char *text, int insert_extra_space)
1807 input_disable_update (in);
1808 while (*text)
1809 handle_char (in, *text++);
1810 if (insert_extra_space)
1811 handle_char (in, ' ');
1812 input_enable_update (in);
1813 update_input (in, 1);
1816 void
1817 input_set_point (WInput *in, int pos)
1819 int max_pos = str_length (in->buffer);
1821 if (pos > max_pos)
1822 pos = max_pos;
1823 if (pos != in->point)
1824 free_completions (in);
1825 in->point = pos;
1826 in->charpoint = 0;
1827 update_input (in, 1);
1830 cb_ret_t
1831 input_callback (Widget *w, widget_msg_t msg, int parm)
1833 WInput *in = (WInput *) w;
1834 cb_ret_t v;
1836 switch (msg) {
1837 case WIDGET_KEY:
1838 if (parm == XCTRL ('q')) {
1839 quote = 1;
1840 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1841 quote = 0;
1842 return v;
1845 /* Keys we want others to handle */
1846 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1847 || parm == KEY_F (10) || parm == XCTRL ('g') || parm == '\n')
1848 return MSG_NOT_HANDLED;
1850 /* When pasting multiline text, insert literal Enter */
1851 if ((parm & ~KEY_M_MASK) == '\n') {
1852 quote = 1;
1853 v = handle_char (in, '\n');
1854 quote = 0;
1855 return v;
1858 return handle_char (in, parm);
1860 case WIDGET_COMMAND:
1861 return input_execute_cmd (in, parm);
1863 case WIDGET_FOCUS:
1864 case WIDGET_UNFOCUS:
1865 case WIDGET_DRAW:
1866 update_input (in, 0);
1867 return MSG_HANDLED;
1869 case WIDGET_CURSOR:
1870 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
1871 - in->term_first_shown);
1872 return MSG_HANDLED;
1874 case WIDGET_DESTROY:
1875 input_destroy (in);
1876 return MSG_HANDLED;
1878 default:
1879 return default_proc (msg, parm);
1883 static int
1884 input_event (Gpm_Event * event, void *data)
1886 WInput *in = data;
1888 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1889 dlg_select_widget (in);
1891 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
1892 && should_show_history_button (in)) {
1893 do_show_hist (in);
1894 } else {
1895 in->point = str_length (in->buffer);
1896 if (event->x + in->term_first_shown - 1 <
1897 str_term_width1 (in->buffer))
1899 in->point = str_column_to_pos (in->buffer, event->x
1900 + in->term_first_shown - 1);
1903 update_input (in, 1);
1905 return MOU_NORMAL;
1908 WInput *
1909 input_new (int y, int x, int color, int width, const char *def_text,
1910 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
1912 WInput *in = g_new (WInput, 1);
1913 int initial_buffer_len;
1915 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
1917 /* history setup */
1918 in->history = NULL;
1919 in->history_name = 0;
1920 if (histname) {
1921 if (*histname) {
1922 in->history_name = g_strdup (histname);
1923 in->history = history_get (histname);
1927 if (def_text == NULL)
1928 def_text = "";
1930 if (def_text == INPUT_LAST_TEXT) {
1931 def_text = "";
1932 if (in->history)
1933 if (in->history->data)
1934 def_text = (char *) in->history->data;
1936 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
1937 in->widget.options |= W_IS_INPUT;
1938 in->completions = NULL;
1939 in->completion_flags = completion_flags;
1940 in->current_max_size = initial_buffer_len;
1941 in->buffer = g_new (char, initial_buffer_len);
1942 in->color = color;
1943 in->field_width = width;
1944 in->first = 1;
1945 in->term_first_shown = 0;
1946 in->disable_update = 0;
1947 in->mark = 0;
1948 in->need_push = 1;
1949 in->is_password = 0;
1951 strcpy (in->buffer, def_text);
1952 in->point = str_length (in->buffer);
1953 in->charpoint = 0;
1954 return in;
1957 /* Listbox widget */
1959 /* Should draw the scrollbar, but currently draws only
1960 * indications that there is more information
1962 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1964 static void
1965 listbox_drawscroll (WListbox *l)
1967 int line = 0;
1968 int i, top;
1969 int max_line = l->widget.lines - 1;
1971 /* Are we at the top? */
1972 widget_move (&l->widget, 0, l->widget.cols);
1973 if (l->list == l->top)
1974 tty_print_one_vline ();
1975 else
1976 tty_print_char ('^');
1978 /* Are we at the bottom? */
1979 widget_move (&l->widget, max_line, l->widget.cols);
1980 top = listbox_cdiff (l->list, l->top);
1981 if ((top + l->widget.lines == l->count) || l->widget.lines >= l->count)
1982 tty_print_one_vline ();
1983 else
1984 tty_print_char ('v');
1986 /* Now draw the nice relative pointer */
1987 if (l->count != 0)
1988 line = 1+ ((l->pos * (l->widget.lines - 2)) / l->count);
1990 for (i = 1; i < max_line; i++){
1991 widget_move (&l->widget, i, l->widget.cols);
1992 if (i != line)
1993 tty_print_one_vline ();
1994 else
1995 tty_print_char ('*');
1999 static void
2000 listbox_draw (WListbox *l, gboolean focused)
2002 const Dlg_head *h = l->widget.parent;
2003 const int normalc = DLG_NORMALC (h);
2004 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2006 WLEntry *e;
2007 int i;
2008 int sel_line = -1;
2009 const char *text;
2011 for (e = l->top, i = 0; i < l->widget.lines; i++) {
2012 /* Display the entry */
2013 if (e == l->current && sel_line == -1) {
2014 sel_line = i;
2015 tty_setcolor (selc);
2016 } else
2017 tty_setcolor (normalc);
2019 widget_move (&l->widget, i, 1);
2021 if ((i > 0 && e == l->list) || !l->list)
2022 text = "";
2023 else {
2024 text = e->text;
2025 e = e->next;
2027 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2029 l->cursor_y = sel_line;
2031 if (l->scrollbar && l->count > l->widget.lines) {
2032 tty_setcolor (normalc);
2033 listbox_drawscroll (l);
2037 /* Returns the number of items between s and e,
2038 must be on the same linked list */
2039 static int
2040 listbox_cdiff (WLEntry *s, WLEntry *e)
2042 int count;
2044 for (count = 0; s != e; count++)
2045 s = s->next;
2046 return count;
2049 static WLEntry *
2050 listbox_check_hotkey (WListbox *l, int key)
2052 int i;
2053 WLEntry *e;
2055 i = 0;
2056 e = l->list;
2057 if (!e)
2058 return NULL;
2060 while (1) {
2062 /* If we didn't find anything, return */
2063 if (i && e == l->list)
2064 return NULL;
2066 if (e->hotkey == key)
2067 return e;
2069 i++;
2070 e = e->next;
2074 /* Selects the last entry and scrolls the list to the bottom */
2075 void
2076 listbox_select_last (WListbox *l)
2078 unsigned int i;
2079 l->current = l->top = l->list->prev;
2080 for (i = min (l->widget.lines, l->count) - 1; i; i--)
2081 l->top = l->top->prev;
2082 l->pos = l->count - 1;
2085 /* Selects the first entry and scrolls the list to the top */
2086 void
2087 listbox_select_first (WListbox *l)
2089 l->current = l->top = l->list;
2090 l->pos = 0;
2093 void
2094 listbox_remove_list (WListbox *l)
2096 WLEntry *p, *q;
2098 if (!l->count)
2099 return;
2101 p = l->list;
2103 while (l->count--) {
2104 q = p->next;
2105 g_free (p->text);
2106 g_free (p);
2107 p = q;
2109 l->pos = l->count = 0;
2110 l->list = l->top = l->current = 0;
2114 * bor 30.10.96: added force flag to remove *last* entry as well
2115 * bor 30.10.96: corrected selection bug if last entry was removed
2118 void
2119 listbox_remove_current (WListbox *l, int force)
2121 WLEntry *p;
2123 /* Ok, note: this won't allow for emtpy lists */
2124 if (!force && (!l->count || l->count == 1))
2125 return;
2127 l->count--;
2128 p = l->current;
2130 if (l->count) {
2131 l->current->next->prev = l->current->prev;
2132 l->current->prev->next = l->current->next;
2133 if (p->next == l->list) {
2134 l->current = p->prev;
2135 l->pos--;
2137 else
2138 l->current = p->next;
2140 if (p == l->list)
2141 l->list = l->top = p->next;
2142 } else {
2143 l->pos = 0;
2144 l->list = l->top = l->current = 0;
2147 g_free (p->text);
2148 g_free (p);
2151 /* Makes *e the selected entry (sets current and pos) */
2152 void
2153 listbox_select_entry (WListbox *l, WLEntry *dest)
2155 WLEntry *e;
2156 int pos;
2157 int top_seen;
2159 top_seen = 0;
2161 /* Special case */
2162 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2164 if (e == l->top)
2165 top_seen = 1;
2167 if (e == dest){
2168 l->current = e;
2169 if (top_seen){
2170 while (listbox_cdiff (l->top, l->current) >= l->widget.lines)
2171 l->top = l->top->next;
2172 } else {
2173 l->top = l->current;
2175 l->pos = pos;
2176 return;
2179 /* If we are unable to find it, set decent values */
2180 l->current = l->top = l->list;
2181 l->pos = 0;
2184 /* Selects from base the pos element */
2185 static WLEntry *
2186 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2188 WLEntry *last = l->list->prev;
2190 if (base == last)
2191 return last;
2192 while (pos--){
2193 base = base->next;
2194 if (base == last)
2195 break;
2197 return base;
2200 static void
2201 listbox_back (WListbox *l)
2203 if (l->pos != 0)
2204 listbox_select_entry (l, l->current->prev);
2205 else
2206 listbox_select_last (l);
2209 /* Return MSG_HANDLED if we want a redraw */
2210 static cb_ret_t
2211 listbox_key (WListbox *l, int key)
2213 int i;
2215 cb_ret_t j = MSG_NOT_HANDLED;
2217 /* focus on listbox item N by '0'..'9' keys */
2218 if (key >= '0' && key <= '9') {
2219 int oldpos = l->pos;
2220 listbox_select_by_number(l, key - '0');
2222 /* need scroll to item? */
2223 if (abs(oldpos - l->pos) > l->widget.lines)
2224 l->top = l->current;
2226 return MSG_HANDLED;
2229 if (!l->list)
2230 return MSG_NOT_HANDLED;
2232 switch (key){
2233 case KEY_HOME:
2234 case KEY_A1:
2235 case ALT ('<'):
2236 listbox_select_first (l);
2237 return MSG_HANDLED;
2239 case KEY_END:
2240 case KEY_C1:
2241 case ALT ('>'):
2242 listbox_select_last (l);
2243 return MSG_HANDLED;
2245 case XCTRL('p'):
2246 case KEY_UP:
2247 listbox_back (l);
2248 return MSG_HANDLED;
2250 case XCTRL('n'):
2251 case KEY_DOWN:
2252 listbox_fwd (l);
2253 return MSG_HANDLED;
2255 case KEY_NPAGE:
2256 case XCTRL('v'):
2257 for (i = 0; ((i < l->widget.lines - 1)
2258 && (l->current != l->list->prev)); i++) {
2259 listbox_fwd (l);
2260 j = MSG_HANDLED;
2262 return j;
2264 case KEY_PPAGE:
2265 case ALT('v'):
2266 for (i = 0; ((i < l->widget.lines - 1)
2267 && (l->current != l->list)); i++) {
2268 listbox_back (l);
2269 j = MSG_HANDLED;
2271 return j;
2273 return MSG_NOT_HANDLED;
2276 static void
2277 listbox_destroy (WListbox *l)
2279 WLEntry *n, *p = l->list;
2280 int i;
2282 for (i = 0; i < l->count; i++){
2283 n = p->next;
2284 g_free (p->text);
2285 g_free (p);
2286 p = n;
2290 static cb_ret_t
2291 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2293 WListbox *l = (WListbox *) w;
2294 Dlg_head *h = l->widget.parent;
2295 WLEntry *e;
2296 cb_ret_t ret_code;
2298 switch (msg) {
2299 case WIDGET_INIT:
2300 return MSG_HANDLED;
2302 case WIDGET_HOTKEY:
2303 e = listbox_check_hotkey (l, parm);
2304 if (e != NULL) {
2305 int action;
2307 listbox_select_entry (l, e);
2309 (*h->callback) (h, DLG_ACTION, l->pos);
2311 if (l->cback)
2312 action = (*l->cback) (l);
2313 else
2314 action = LISTBOX_DONE;
2316 if (action == LISTBOX_DONE) {
2317 h->ret_value = B_ENTER;
2318 dlg_stop (h);
2320 return MSG_HANDLED;
2322 return MSG_NOT_HANDLED;
2324 case WIDGET_KEY:
2325 ret_code = listbox_key (l, parm);
2326 if (ret_code != MSG_NOT_HANDLED) {
2327 listbox_draw (l, TRUE);
2328 (*h->callback) (h, DLG_ACTION, l->pos);
2330 return ret_code;
2332 case WIDGET_CURSOR:
2333 widget_move (&l->widget, l->cursor_y, 0);
2334 (*h->callback) (h, DLG_ACTION, l->pos);
2335 return MSG_HANDLED;
2337 case WIDGET_FOCUS:
2338 case WIDGET_UNFOCUS:
2339 case WIDGET_DRAW:
2340 listbox_draw (l, msg != WIDGET_UNFOCUS);
2341 return MSG_HANDLED;
2343 case WIDGET_DESTROY:
2344 listbox_destroy (l);
2345 return MSG_HANDLED;
2347 case WIDGET_RESIZED:
2348 return MSG_HANDLED;
2350 default:
2351 return default_proc (msg, parm);
2355 static int
2356 listbox_event (Gpm_Event *event, void *data)
2358 WListbox *l = data;
2359 int i;
2361 Dlg_head *h = l->widget.parent;
2363 /* Single click */
2364 if (event->type & GPM_DOWN)
2365 dlg_select_widget (l);
2367 if (l->list == NULL)
2368 return MOU_NORMAL;
2370 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2371 int ret = MOU_REPEAT;
2373 if (event->x < 0 || event->x > l->widget.cols)
2374 return ret;
2376 if (event->y < 1)
2377 for (i = -event->y; i >= 0; i--)
2378 listbox_back (l);
2379 else if (event->y > l->widget.lines)
2380 for (i = event->y - l->widget.lines; i > 0; i--)
2381 listbox_fwd (l);
2382 else if (event->buttons & GPM_B_UP) {
2383 listbox_back (l);
2384 ret = MOU_NORMAL;
2385 } else if (event->buttons & GPM_B_DOWN) {
2386 listbox_fwd (l);
2387 ret = MOU_NORMAL;
2388 } else
2389 listbox_select_entry (l,
2390 listbox_select_pos (l, l->top,
2391 event->y - 1));
2393 /* We need to refresh ourselves since the dialog manager doesn't */
2394 /* know about this event */
2395 listbox_draw (l, TRUE);
2396 return ret;
2399 /* Double click */
2400 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2401 int action;
2403 if (event->x < 0 || event->x >= l->widget.cols
2404 || event->y < 1 || event->y > l->widget.lines)
2405 return MOU_NORMAL;
2407 dlg_select_widget (l);
2408 listbox_select_entry (l,
2409 listbox_select_pos (l, l->top,
2410 event->y - 1));
2412 if (l->cback)
2413 action = (*l->cback) (l);
2414 else
2415 action = LISTBOX_DONE;
2417 if (action == LISTBOX_DONE) {
2418 h->ret_value = B_ENTER;
2419 dlg_stop (h);
2420 return MOU_NORMAL;
2423 return MOU_NORMAL;
2426 WListbox *
2427 listbox_new (int y, int x, int height, int width, lcback callback)
2429 WListbox *l = g_new (WListbox, 1);
2431 if (height <= 0)
2432 height = 1;
2434 init_widget (&l->widget, y, x, height, width,
2435 listbox_callback, listbox_event);
2437 l->list = l->top = l->current = 0;
2438 l->pos = 0;
2439 l->count = 0;
2440 l->cback = callback;
2441 l->allow_duplicates = 1;
2442 l->scrollbar = !tty_is_slow ();
2443 widget_want_hotkey (l->widget, 1);
2445 return l;
2448 /* Listbox item adding function. They still lack a lot of functionality */
2449 /* any takers? */
2450 /* 1.11.96 bor: added pos argument to control placement of new entry */
2451 static void
2452 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2454 if (!l->list){
2455 l->list = e;
2456 l->top = e;
2457 l->current = e;
2458 e->next = l->list;
2459 e->prev = l->list;
2460 } else if (pos == LISTBOX_APPEND_AT_END) {
2461 e->next = l->list;
2462 e->prev = l->list->prev;
2463 l->list->prev->next = e;
2464 l->list->prev = e;
2465 } else if (pos == LISTBOX_APPEND_BEFORE){
2466 e->next = l->current;
2467 e->prev = l->current->prev;
2468 l->current->prev->next = e;
2469 l->current->prev = e;
2470 if (l->list == l->current) { /* move list one position down */
2471 l->list = e;
2472 l->top = e;
2474 } else if (pos == LISTBOX_APPEND_AFTER) {
2475 e->prev = l->current;
2476 e->next = l->current->next;
2477 l->current->next->prev = e;
2478 l->current->next = e;
2479 } else if (pos == LISTBOX_APPEND_SORTED) {
2480 WLEntry *w = l->list;
2482 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2483 w = w->next;
2484 if (w->next == l->list) {
2485 e->prev = w;
2486 e->next = l->list;
2487 w->next = e;
2488 l->list->prev = e;
2489 } else {
2490 e->next = w;
2491 e->prev = w->prev;
2492 w->prev->next = e;
2493 w->prev = e;
2496 l->count++;
2499 char *
2500 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2501 const char *text, void *data)
2503 WLEntry *entry;
2505 if (!l)
2506 return NULL;
2508 if (!l->allow_duplicates)
2509 if (listbox_search_text (l, text))
2510 return NULL;
2512 entry = g_new (WLEntry, 1);
2513 entry->text = g_strdup (text);
2514 entry->data = data;
2515 entry->hotkey = hotkey;
2517 listbox_append_item (l, entry, pos);
2519 return entry->text;
2522 /* Selects the nth entry in the listbox */
2523 void
2524 listbox_select_by_number (WListbox *l, int n)
2526 if (l->list != NULL)
2527 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2530 WLEntry *
2531 listbox_search_text (WListbox *l, const char *text)
2533 WLEntry *e;
2535 e = l->list;
2536 if (!e)
2537 return NULL;
2539 do {
2540 if(!strcmp (e->text, text))
2541 return e;
2542 e = e->next;
2543 } while (e!=l->list);
2545 return NULL;
2548 /* Returns the current string text as well as the associated extra data */
2549 void
2550 listbox_get_current (WListbox *l, char **string, char **extra)
2552 if (!l->current){
2553 *string = 0;
2554 *extra = 0;
2556 if (string && l->current)
2557 *string = l->current->text;
2558 if (extra && l->current)
2559 *extra = l->current->data;
2562 /* returns TRUE if a function has been called, FALSE otherwise. */
2563 static gboolean
2564 buttonbar_call (WButtonBar *bb, int i)
2566 switch (bb->labels[i].tag) {
2567 case BBFUNC_NONE:
2568 break;
2569 case BBFUNC_VOID:
2570 bb->labels[i].u.fn_void ();
2571 return TRUE;
2572 case BBFUNC_PTR:
2573 bb->labels[i].u.fn_ptr (bb->labels[i].data);
2574 return TRUE;
2576 return FALSE;
2579 /* calculate width of one button, width is never lesser than 7 */
2580 static int
2581 buttonbat_get_button_width ()
2583 int result = COLS / BUTTONBAR_LABELS_NUM;
2584 return (result >= 7) ? result : 7;
2588 static cb_ret_t
2589 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2591 WButtonBar *bb = (WButtonBar *) w;
2592 int i;
2593 const char *text;
2595 switch (msg) {
2596 case WIDGET_FOCUS:
2597 return MSG_NOT_HANDLED;
2599 case WIDGET_HOTKEY:
2600 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2601 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2602 return MSG_HANDLED;
2603 return MSG_NOT_HANDLED;
2605 case WIDGET_DRAW:
2606 if (bb->visible) {
2607 widget_move (&bb->widget, 0, 0);
2608 tty_setcolor (DEFAULT_COLOR);
2609 bb->btn_width = buttonbat_get_button_width ();
2610 tty_printf ("%-*s", bb->widget.cols, "");
2612 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2613 widget_move (&bb->widget, 0, i * bb->btn_width);
2614 tty_setcolor (DEFAULT_COLOR);
2615 tty_printf ("%2d", i + 1);
2616 tty_setcolor (SELECTED_COLOR);
2617 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2618 tty_print_string (str_fit_to_term (text, bb->btn_width - 2, J_CENTER_LEFT));
2621 return MSG_HANDLED;
2623 case WIDGET_DESTROY:
2624 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2625 g_free (bb->labels[i].text);
2626 return MSG_HANDLED;
2628 default:
2629 return default_proc (msg, parm);
2633 static int
2634 buttonbar_event (Gpm_Event *event, void *data)
2636 WButtonBar *bb = data;
2637 int button;
2639 if (!(event->type & GPM_UP))
2640 return MOU_NORMAL;
2641 if (event->y == 2)
2642 return MOU_NORMAL;
2643 button = (event->x - 1) / bb->btn_width;
2644 if (button < BUTTONBAR_LABELS_NUM)
2645 buttonbar_call (bb, button);
2646 return MOU_NORMAL;
2649 WButtonBar *
2650 buttonbar_new (int visible)
2652 WButtonBar *bb;
2653 int i;
2655 bb = g_new0 (WButtonBar, 1);
2657 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2658 buttonbar_callback, buttonbar_event);
2659 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2660 bb->visible = visible;
2661 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++){
2662 bb->labels[i].text = NULL;
2663 bb->labels[i].tag = BBFUNC_NONE;
2665 widget_want_hotkey (bb->widget, 1);
2666 widget_want_cursor (bb->widget, 0);
2667 bb->btn_width = buttonbat_get_button_width ();
2669 return bb;
2672 static void
2673 set_label_text (WButtonBar * bb, int index, const char *text)
2675 g_free (bb->labels[index - 1].text);
2677 bb->labels[index - 1].text = g_strdup (text);
2680 /* Find ButtonBar widget in the dialog */
2681 WButtonBar *
2682 find_buttonbar (Dlg_head *h)
2684 WButtonBar *bb;
2686 bb = (WButtonBar *) find_widget_type (h, buttonbar_callback);
2687 return bb;
2690 void
2691 buttonbar_clear_label (Dlg_head *h, int idx)
2693 WButtonBar *bb = find_buttonbar (h);
2695 if (!bb)
2696 return;
2698 set_label_text (bb, idx, "");
2699 bb->labels[idx - 1].tag = BBFUNC_NONE;
2702 void
2703 buttonbar_set_label_data (Dlg_head *h, int idx, const char *text,
2704 buttonbarfn cback, void *data)
2706 WButtonBar *bb = find_buttonbar (h);
2708 if (!bb)
2709 return;
2711 assert (cback != (buttonbarfn) 0);
2712 set_label_text (bb, idx, text);
2713 bb->labels[idx - 1].tag = BBFUNC_PTR;
2714 bb->labels[idx - 1].u.fn_ptr = cback;
2715 bb->labels[idx - 1].data = data;
2718 void
2719 buttonbar_set_label (Dlg_head *h, int idx, const char *text, voidfn cback)
2721 WButtonBar *bb = find_buttonbar (h);
2723 if (!bb)
2724 return;
2726 assert (cback != (voidfn) 0);
2727 set_label_text (bb, idx, text);
2728 bb->labels[idx - 1].tag = BBFUNC_VOID;
2729 bb->labels[idx - 1].u.fn_void = cback;
2732 void
2733 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2735 bb->visible = visible;
2738 void
2739 buttonbar_redraw (Dlg_head *h)
2741 WButtonBar *bb = find_buttonbar (h);
2743 if (!bb)
2744 return;
2746 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2749 static cb_ret_t
2750 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2752 WGroupbox *g = (WGroupbox *) w;
2754 switch (msg) {
2755 case WIDGET_INIT:
2756 return MSG_HANDLED;
2758 case WIDGET_FOCUS:
2759 return MSG_NOT_HANDLED;
2761 case WIDGET_DRAW:
2762 tty_setcolor (COLOR_NORMAL);
2763 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2764 g->widget.x - g->widget.parent->x, g->widget.lines,
2765 g->widget.cols);
2767 tty_setcolor (COLOR_HOT_NORMAL);
2768 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2769 g->widget.x - g->widget.parent->x + 1);
2770 tty_print_string (g->title);
2771 return MSG_HANDLED;
2773 case WIDGET_DESTROY:
2774 g_free (g->title);
2775 return MSG_HANDLED;
2777 default:
2778 return default_proc (msg, parm);
2782 WGroupbox *
2783 groupbox_new (int y, int x, int height, int width, const char *title)
2785 WGroupbox *g = g_new (WGroupbox, 1);
2787 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2789 g->widget.options &= ~W_WANT_CURSOR;
2790 widget_want_hotkey (g->widget, 0);
2792 /* Strip existing spaces, add one space before and after the title */
2793 if (title) {
2794 char *t;
2795 t = g_strstrip (g_strdup (title));
2796 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2797 g_free (t);
2800 return g;