Widget library: added new widget WHLine.
[midnight-commander.git] / src / widget.c
blob679792e0b7292e095e8336c308af2f79707a20e4
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
8 1995 Jakub Jelinek
9 1996 Andrej Borsenkow
10 1997 Norbert Warmuth
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 /** \file widget.c
29 * \brief Source: widgets
32 #include <config.h>
34 #include <assert.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <sys/types.h>
43 #include "lib/global.h"
45 #include "lib/tty/tty.h"
46 #include "lib/tty/mouse.h"
47 #include "lib/tty/key.h" /* XCTRL and ALT macros */
48 #include "lib/skin.h"
49 #include "lib/mcconfig.h" /* for history loading and saving */
50 #include "lib/vfs/mc-vfs/vfs.h"
51 #include "lib/fileloc.h"
52 #include "lib/strutil.h"
54 #include "dialog.h"
55 #include "widget.h"
56 #include "wtools.h"
58 #include "cmddef.h" /* CK_ cmd name const */
59 #include "keybind.h" /* global_keymap_t */
60 #include "panel.h" /* current_panel */
61 #include "main.h" /* confirm_history_cleanup */
63 const global_keymap_t *input_map;
65 static void
66 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
68 Dlg_head *h = w->parent;
70 tty_setcolor (hotkey
71 ? (focused
72 ? DLG_HOT_FOCUSC (h)
73 : DLG_HOT_NORMALC (h))
74 : (focused
75 ? DLG_FOCUSC (h)
76 : DLG_NORMALC (h)));
79 struct hotkey_t
80 parse_hotkey (const char *text)
82 struct hotkey_t result;
83 const char *cp, *p;
85 /* search for '&', that is not on the of text */
86 cp = strchr (text, '&');
87 if (cp != NULL && cp[1] != '\0') {
88 result.start = g_strndup (text, cp - text);
90 /* skip '&' */
91 cp++;
92 p = str_cget_next_char (cp);
93 result.hotkey = g_strndup (cp, p - cp);
95 cp = p;
96 result.end = g_strdup (cp);
97 } else {
98 result.start = g_strdup (text);
99 result.hotkey = NULL;
100 result.end = NULL;
103 return result;
105 void
106 release_hotkey (const struct hotkey_t hotkey)
108 g_free (hotkey.start);
109 g_free (hotkey.hotkey);
110 g_free (hotkey.end);
114 hotkey_width (const struct hotkey_t hotkey)
116 int result;
118 result = str_term_width1 (hotkey.start);
119 result+= (hotkey.hotkey != NULL) ? str_term_width1 (hotkey.hotkey) : 0;
120 result+= (hotkey.end != NULL) ? str_term_width1 (hotkey.end) : 0;
121 return result;
124 static void
125 draw_hotkey (Widget *w, const struct hotkey_t hotkey, gboolean focused)
127 widget_selectcolor (w, focused, FALSE);
128 tty_print_string (hotkey.start);
130 if (hotkey.hotkey != NULL) {
131 widget_selectcolor (w, focused, TRUE);
132 tty_print_string (hotkey.hotkey);
133 widget_selectcolor (w, focused, FALSE);
136 if (hotkey.end != NULL)
137 tty_print_string (hotkey.end);
141 /* Default callback for widgets */
142 cb_ret_t
143 default_proc (widget_msg_t msg, int parm)
145 (void) parm;
147 switch (msg) {
148 case WIDGET_INIT:
149 case WIDGET_FOCUS:
150 case WIDGET_UNFOCUS:
151 case WIDGET_DRAW:
152 case WIDGET_DESTROY:
153 case WIDGET_CURSOR:
154 case WIDGET_IDLE:
155 return MSG_HANDLED;
157 default:
158 return MSG_NOT_HANDLED;
162 static int button_event (Gpm_Event *event, void *);
164 int quote = 0;
166 static cb_ret_t
167 button_callback (Widget *w, widget_msg_t msg, int parm)
169 WButton *b = (WButton *) w;
170 int stop = 0;
171 int off = 0;
172 Dlg_head *h = b->widget.parent;
174 switch (msg) {
175 case WIDGET_HOTKEY:
177 * Don't let the default button steal Enter from the current
178 * button. This is a workaround for the flawed event model
179 * when hotkeys are sent to all widgets before the key is
180 * handled by the current widget.
182 if (parm == '\n' && h->current == &b->widget) {
183 button_callback (w, WIDGET_KEY, ' ');
184 return MSG_HANDLED;
187 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
188 button_callback (w, WIDGET_KEY, ' ');
189 return MSG_HANDLED;
192 if (b->text.hotkey != NULL) {
193 if (g_ascii_tolower ((gchar)b->text.hotkey[0]) ==
194 g_ascii_tolower ((gchar)parm)) {
195 button_callback (w, WIDGET_KEY, ' ');
196 return MSG_HANDLED;
199 return MSG_NOT_HANDLED;
201 case WIDGET_KEY:
202 if (parm != ' ' && parm != '\n')
203 return MSG_NOT_HANDLED;
205 if (b->callback)
206 stop = (*b->callback) (b->action);
207 if (!b->callback || stop) {
208 h->ret_value = b->action;
209 dlg_stop (h);
211 return MSG_HANDLED;
213 case WIDGET_CURSOR:
214 switch (b->flags) {
215 case DEFPUSH_BUTTON:
216 off = 3;
217 break;
218 case NORMAL_BUTTON:
219 off = 2;
220 break;
221 case NARROW_BUTTON:
222 off = 1;
223 break;
224 case HIDDEN_BUTTON:
225 default:
226 off = 0;
227 break;
229 widget_move (&b->widget, 0, b->hotpos + off);
230 return MSG_HANDLED;
232 case WIDGET_UNFOCUS:
233 case WIDGET_FOCUS:
234 case WIDGET_DRAW:
235 if (msg == WIDGET_UNFOCUS)
236 b->selected = 0;
237 else if (msg == WIDGET_FOCUS)
238 b->selected = 1;
240 widget_selectcolor (w, b->selected, FALSE);
241 widget_move (w, 0, 0);
243 switch (b->flags) {
244 case DEFPUSH_BUTTON:
245 tty_print_string ("[< ");
246 break;
247 case NORMAL_BUTTON:
248 tty_print_string ("[ ");
249 break;
250 case NARROW_BUTTON:
251 tty_print_string ("[");
252 break;
253 case HIDDEN_BUTTON:
254 default:
255 return MSG_HANDLED;
258 draw_hotkey (w, b->text, b->selected);
260 switch (b->flags) {
261 case DEFPUSH_BUTTON:
262 tty_print_string (" >]");
263 break;
264 case NORMAL_BUTTON:
265 tty_print_string (" ]");
266 break;
267 case NARROW_BUTTON:
268 tty_print_string ("]");
269 break;
271 return MSG_HANDLED;
273 case WIDGET_DESTROY:
274 release_hotkey (b->text);
275 return MSG_HANDLED;
277 default:
278 return default_proc (msg, parm);
282 static int
283 button_event (Gpm_Event *event, void *data)
285 WButton *b = data;
287 if (event->type & (GPM_DOWN|GPM_UP)){
288 Dlg_head *h = b->widget.parent;
289 dlg_select_widget (b);
290 if (event->type & GPM_UP){
291 button_callback ((Widget *) data, WIDGET_KEY, ' ');
292 h->callback (h, &b->widget, DLG_POST_KEY, ' ', NULL);
293 return MOU_NORMAL;
296 return MOU_NORMAL;
300 button_get_len (const WButton *b)
302 int ret = hotkey_width (b->text);
303 switch (b->flags) {
304 case DEFPUSH_BUTTON:
305 ret += 6;
306 break;
307 case NORMAL_BUTTON:
308 ret += 4;
309 break;
310 case NARROW_BUTTON:
311 ret += 2;
312 break;
313 case HIDDEN_BUTTON:
314 default:
315 return 0;
317 return ret;
320 WButton *
321 button_new (int y, int x, int action, int flags, const char *text,
322 bcback callback)
324 WButton *b = g_new (WButton, 1);
326 b->action = action;
327 b->flags = flags;
328 b->text = parse_hotkey (text);
330 init_widget (&b->widget, y, x, 1, button_get_len (b),
331 button_callback, button_event);
333 b->selected = 0;
334 b->callback = callback;
335 widget_want_hotkey (b->widget, 1);
336 b->hotpos = (b->text.hotkey != NULL) ? str_term_width1 (b->text.start) : -1;
338 return b;
341 const char *
342 button_get_text (const WButton *b)
344 if (b->text.hotkey != NULL)
345 return g_strconcat (b->text.start, "&", b->text.hotkey,
346 b->text.end, (char *) NULL);
347 else
348 return g_strdup (b->text.start);
351 void
352 button_set_text (WButton *b, const char *text)
354 release_hotkey (b->text);
355 b->text = parse_hotkey (text);
356 b->widget.cols = button_get_len (b);
357 dlg_redraw (b->widget.parent);
361 /* Radio button widget */
362 static int radio_event (Gpm_Event *event, void *);
364 static cb_ret_t
365 radio_callback (Widget *w, widget_msg_t msg, int parm)
367 WRadio *r = (WRadio *) w;
368 int i;
369 Dlg_head *h = r->widget.parent;
371 switch (msg) {
372 case WIDGET_HOTKEY:
374 int lp = g_ascii_tolower ((gchar)parm);
376 for (i = 0; i < r->count; i++) {
377 if (r->texts[i].hotkey != NULL) {
378 int c = g_ascii_tolower ((gchar)r->texts[i].hotkey[0]);
380 if (c != lp)
381 continue;
382 r->pos = i;
384 /* Take action */
385 radio_callback (w, WIDGET_KEY, ' ');
386 return MSG_HANDLED;
390 return MSG_NOT_HANDLED;
392 case WIDGET_KEY:
393 switch (parm) {
394 case ' ':
395 r->sel = r->pos;
396 h->callback (h, w, DLG_ACTION, 0, NULL);
397 radio_callback (w, WIDGET_FOCUS, ' ');
398 return MSG_HANDLED;
400 case KEY_UP:
401 case KEY_LEFT:
402 if (r->pos > 0) {
403 r->pos--;
404 return MSG_HANDLED;
406 return MSG_NOT_HANDLED;
408 case KEY_DOWN:
409 case KEY_RIGHT:
410 if (r->count - 1 > r->pos) {
411 r->pos++;
412 return MSG_HANDLED;
415 return MSG_NOT_HANDLED;
417 case WIDGET_CURSOR:
418 h->callback (h, w, DLG_ACTION, 0, NULL);
419 radio_callback (w, WIDGET_FOCUS, ' ');
420 widget_move (&r->widget, r->pos, 1);
421 return MSG_HANDLED;
423 case WIDGET_UNFOCUS:
424 case WIDGET_FOCUS:
425 case WIDGET_DRAW:
426 for (i = 0; i < r->count; i++) {
427 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
428 widget_selectcolor (w, focused, FALSE);
429 widget_move (&r->widget, i, 0);
430 tty_print_string ((r->sel == i) ? "(*) " : "( ) ");
431 draw_hotkey (w, r->texts[i], focused);
433 return MSG_HANDLED;
435 case WIDGET_DESTROY:
436 for (i = 0; i < r->count; i++) {
437 release_hotkey (r->texts[i]);
439 g_free (r->texts);
440 return MSG_HANDLED;
442 default:
443 return default_proc (msg, parm);
447 static int
448 radio_event (Gpm_Event *event, void *data)
450 WRadio *r = data;
451 Widget *w = data;
453 if (event->type & (GPM_DOWN|GPM_UP)){
454 Dlg_head *h = r->widget.parent;
456 r->pos = event->y - 1;
457 dlg_select_widget (r);
458 if (event->type & GPM_UP){
459 radio_callback (w, WIDGET_KEY, ' ');
460 radio_callback (w, WIDGET_FOCUS, 0);
461 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
462 return MOU_NORMAL;
465 return MOU_NORMAL;
468 WRadio *
469 radio_new (int y, int x, int count, const char **texts)
471 WRadio *result = g_new (WRadio, 1);
472 int i, max, m;
474 /* Compute the longest string */
475 result->texts = g_new (struct hotkey_t, count);
477 max = 0;
478 for (i = 0; i < count; i++){
479 result->texts[i] = parse_hotkey (texts[i]);
480 m = hotkey_width (result->texts[i]);
481 if (m > max)
482 max = m;
485 init_widget (&result->widget, y, x, count, max, radio_callback, radio_event);
486 result->state = 1;
487 result->pos = 0;
488 result->sel = 0;
489 result->count = count;
490 widget_want_hotkey (result->widget, 1);
492 return result;
496 /* Checkbutton widget */
498 static int check_event (Gpm_Event *event, void *);
500 static cb_ret_t
501 check_callback (Widget *w, widget_msg_t msg, int parm)
503 WCheck *c = (WCheck *) w;
504 Dlg_head *h = c->widget.parent;
506 switch (msg) {
507 case WIDGET_HOTKEY:
508 if (c->text.hotkey != NULL) {
509 if (g_ascii_tolower ((gchar)c->text.hotkey[0]) ==
510 g_ascii_tolower ((gchar)parm)) {
512 check_callback (w, WIDGET_KEY, ' '); /* make action */
513 return MSG_HANDLED;
516 return MSG_NOT_HANDLED;
518 case WIDGET_KEY:
519 if (parm != ' ')
520 return MSG_NOT_HANDLED;
521 c->state ^= C_BOOL;
522 c->state ^= C_CHANGE;
523 h->callback (h, w, DLG_ACTION, 0, NULL);
524 check_callback (w, WIDGET_FOCUS, ' ');
525 return MSG_HANDLED;
527 case WIDGET_CURSOR:
528 widget_move (&c->widget, 0, 1);
529 return MSG_HANDLED;
531 case WIDGET_FOCUS:
532 case WIDGET_UNFOCUS:
533 case WIDGET_DRAW:
534 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
535 widget_move (&c->widget, 0, 0);
536 tty_print_string ((c->state & C_BOOL) ? "[x] " : "[ ] ");
537 draw_hotkey (w, c->text, msg == WIDGET_FOCUS);
538 return MSG_HANDLED;
540 case WIDGET_DESTROY:
541 release_hotkey (c->text);
542 return MSG_HANDLED;
544 default:
545 return default_proc (msg, parm);
549 static int
550 check_event (Gpm_Event *event, void *data)
552 WCheck *c = data;
553 Widget *w = data;
555 if (event->type & (GPM_DOWN|GPM_UP)){
556 Dlg_head *h = c->widget.parent;
558 dlg_select_widget (c);
559 if (event->type & GPM_UP){
560 check_callback (w, WIDGET_KEY, ' ');
561 check_callback (w, WIDGET_FOCUS, 0);
562 h->callback (h, w, DLG_POST_KEY, ' ', NULL);
563 return MOU_NORMAL;
566 return MOU_NORMAL;
569 WCheck *
570 check_new (int y, int x, int state, const char *text)
572 WCheck *c = g_new (WCheck, 1);
574 c->text = parse_hotkey (text);
576 init_widget (&c->widget, y, x, 1, hotkey_width (c->text),
577 check_callback, check_event);
578 c->state = state ? C_BOOL : 0;
579 widget_want_hotkey (c->widget, 1);
581 return c;
584 static gboolean
585 save_text_to_clip_file (const char *text)
587 int file;
588 char *fname = NULL;
590 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
591 file = mc_open (fname, O_CREAT | O_WRONLY | O_TRUNC,
592 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
593 g_free (fname);
595 if (file == -1)
596 return FALSE;
598 mc_write (file, (char *) text, strlen (text));
599 mc_close (file);
600 return TRUE;
603 static gboolean
604 load_text_from_clip_file (char **text)
606 char buf[BUF_LARGE];
607 FILE *f;
608 char *fname = NULL;
609 gboolean first = TRUE;
611 fname = g_build_filename (home_dir, EDIT_CLIP_FILE, NULL);
612 f = fopen (fname, "r");
613 g_free (fname);
615 if (f == NULL)
616 return FALSE;
618 *text = NULL;
620 while (fgets (buf, sizeof (buf), f)) {
621 size_t len;
623 len = strlen (buf);
624 if ( len > 0 ) {
625 if (buf[len - 1] == '\n')
626 buf[len - 1] = '\0';
628 if (first) {
629 first = FALSE;
630 *text = g_strdup (buf);
631 } else {
632 /* remove \n on EOL */
633 char *tmp;
635 tmp = g_strconcat (*text, " ", buf, (char *) NULL);
636 g_free (*text);
637 *text = tmp;
642 fclose (f);
644 return (*text != NULL);
647 static gboolean
648 panel_save_curent_file_to_clip_file (void)
650 gboolean res;
652 if (current_panel->marked == 0)
653 res = save_text_to_clip_file (selection (current_panel)->fname);
654 else {
655 int i;
656 gboolean first = TRUE;
657 char *flist = NULL;
659 for (i = 0; i < current_panel->count; i++)
660 if (current_panel->dir.list[i].f.marked != 0) { /* Skip the unmarked ones */
661 if (first) {
662 flist = g_strdup (current_panel->dir.list[i].fname);
663 first = FALSE;
664 } else {
665 /* Add empty lines after the file */
666 char *tmp;
668 tmp = g_strconcat (flist, "\n", current_panel->dir.list[i].fname, (char *) NULL);
669 g_free (flist);
670 flist = tmp;
674 if (flist != NULL) {
675 res = save_text_to_clip_file (flist);
676 g_free (flist);
679 return res;
683 /* Label widget */
685 static cb_ret_t
686 label_callback (Widget *w, widget_msg_t msg, int parm)
688 WLabel *l = (WLabel *) w;
689 Dlg_head *h = l->widget.parent;
691 switch (msg) {
692 case WIDGET_INIT:
693 return MSG_HANDLED;
695 /* We don't want to get the focus */
696 case WIDGET_FOCUS:
697 return MSG_NOT_HANDLED;
699 case WIDGET_DRAW:
701 char *p = l->text, *q, c = 0;
702 int y = 0;
704 if (!l->text)
705 return MSG_HANDLED;
707 if (l->transparent)
708 tty_setcolor (DEFAULT_COLOR);
709 else
710 tty_setcolor (DLG_NORMALC (h));
712 for (;;) {
713 q = strchr (p, '\n');
714 if (q != NULL) {
715 c = q[0];
716 q[0] = '\0';
719 widget_move (&l->widget, y, 0);
720 tty_print_string (str_fit_to_term (p, l->widget.cols, J_LEFT));
722 if (q == NULL)
723 break;
724 q[0] = c;
725 p = q + 1;
726 y++;
728 return MSG_HANDLED;
731 case WIDGET_DESTROY:
732 g_free (l->text);
733 return MSG_HANDLED;
735 default:
736 return default_proc (msg, parm);
740 void
741 label_set_text (WLabel *label, const char *text)
743 int newcols = label->widget.cols;
744 int newlines;
746 if (label->text && text && !strcmp (label->text, text))
747 return; /* Flickering is not nice */
749 g_free (label->text);
751 if (text != NULL) {
752 label->text = g_strdup (text);
753 if (label->auto_adjust_cols) {
754 str_msg_term_size (text, &newlines, &newcols);
755 if (newcols > label->widget.cols)
756 label->widget.cols = newcols;
757 if (newlines > label->widget.lines)
758 label->widget.lines = newlines;
760 } else label->text = NULL;
762 if (label->widget.parent)
763 label_callback ((Widget *) label, WIDGET_DRAW, 0);
765 if (newcols < label->widget.cols)
766 label->widget.cols = newcols;
769 WLabel *
770 label_new (int y, int x, const char *text)
772 WLabel *l;
773 int cols = 1;
774 int lines = 1;
776 if (text != NULL)
777 str_msg_term_size (text, &lines, &cols);
779 l = g_new (WLabel, 1);
780 init_widget (&l->widget, y, x, lines, cols, label_callback, NULL);
781 l->text = (text != NULL) ? g_strdup (text) : NULL;
782 l->auto_adjust_cols = 1;
783 l->transparent = 0;
784 widget_want_cursor (l->widget, 0);
785 return l;
788 static cb_ret_t
789 hline_callback (Widget *w, widget_msg_t msg, int parm)
791 WHLine *l = (WHLine *) w;
792 Dlg_head *h = l->widget.parent;
794 switch (msg) {
795 case WIDGET_INIT:
796 case WIDGET_RESIZED:
797 if (l->auto_adjust_cols) {
798 if (((w->parent->flags & DLG_COMPACT) != 0)) {
799 w->x = w->parent->x;
800 w->cols = w->parent->cols;
801 } else {
802 w->x = w->parent->x + 1;
803 w->cols = w->parent->cols - 2;
807 case WIDGET_FOCUS:
808 /* We don't want to get the focus */
809 return MSG_NOT_HANDLED;
811 case WIDGET_DRAW:
812 if (l->transparent)
813 tty_setcolor (DEFAULT_COLOR);
814 else
815 tty_setcolor (DLG_NORMALC (h));
817 tty_draw_hline (w->y, w->x + 1, ACS_HLINE, w->cols - 2);
819 if (l->auto_adjust_cols) {
820 widget_move (w, 0, 0);
821 tty_print_alt_char (ACS_LTEE);
822 widget_move (w, 0, w->cols - 1);
823 tty_print_alt_char (ACS_RTEE);
825 return MSG_HANDLED;
827 default:
828 return default_proc (msg, parm);
833 WHLine *
834 hline_new (int y, int x, int width)
836 WHLine *l;
837 int cols = width;
838 int lines = 1;
840 l = g_new (WHLine, 1);
841 init_widget (&l->widget, y, x, lines, cols, hline_callback, NULL);
842 l->auto_adjust_cols = (width < 0);
843 l->transparent = FALSE;
844 widget_want_cursor (l->widget, 0);
845 return l;
849 /* Gauge widget (progress indicator) */
850 /* Currently width is hardcoded here for text mode */
851 #define gauge_len 47
853 static cb_ret_t
854 gauge_callback (Widget *w, widget_msg_t msg, int parm)
856 WGauge *g = (WGauge *) w;
857 Dlg_head *h = g->widget.parent;
859 if (msg == WIDGET_INIT)
860 return MSG_HANDLED;
862 /* We don't want to get the focus */
863 if (msg == WIDGET_FOCUS)
864 return MSG_NOT_HANDLED;
866 if (msg == WIDGET_DRAW){
867 widget_move (&g->widget, 0, 0);
868 tty_setcolor (DLG_NORMALC (h));
869 if (!g->shown)
870 tty_printf ("%*s", gauge_len, "");
871 else {
872 int percentage, columns;
873 long total = g->max, done = g->current;
875 if (total <= 0 || done < 0) {
876 done = 0;
877 total = 100;
879 if (done > total)
880 done = total;
881 while (total > 65535) {
882 total /= 256;
883 done /= 256;
885 percentage = (200 * done / total + 1) / 2;
886 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
887 tty_print_char ('[');
888 tty_setcolor (GAUGE_COLOR);
889 tty_printf ("%*s", (int) columns, "");
890 tty_setcolor (DLG_NORMALC (h));
891 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
893 return MSG_HANDLED;
896 return default_proc (msg, parm);
899 void
900 gauge_set_value (WGauge *g, int max, int current)
902 if (g->current == current && g->max == max)
903 return; /* Do not flicker */
904 if (max == 0)
905 max = 1; /* I do not like division by zero :) */
907 g->current = current;
908 g->max = max;
909 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
912 void
913 gauge_show (WGauge *g, int shown)
915 if (g->shown == shown)
916 return;
917 g->shown = shown;
918 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
921 WGauge *
922 gauge_new (int y, int x, int shown, int max, int current)
924 WGauge *g = g_new (WGauge, 1);
926 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
927 g->shown = shown;
928 if (max == 0)
929 max = 1; /* I do not like division by zero :) */
930 g->max = max;
931 g->current = current;
932 widget_want_cursor (g->widget, 0);
933 return g;
937 /* Input widget */
939 /* {{{ history button */
941 #define LARGE_HISTORY_BUTTON 1
943 #ifdef LARGE_HISTORY_BUTTON
944 # define HISTORY_BUTTON_WIDTH 3
945 #else
946 # define HISTORY_BUTTON_WIDTH 1
947 #endif
949 #define should_show_history_button(in) \
950 (in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
952 static void draw_history_button (WInput * in)
954 char c;
955 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
956 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH);
957 #ifdef LARGE_HISTORY_BUTTON
959 Dlg_head *h;
960 h = in->widget.parent;
961 tty_setcolor (NORMAL_COLOR);
962 tty_print_string ("[ ]");
963 /* Too distracting: tty_setcolor (MARKED_COLOR); */
964 widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
965 tty_print_char (c);
967 #else
968 tty_setcolor (MARKED_COLOR);
969 tty_print_char (c);
970 #endif
973 /* }}} history button */
976 /* Input widgets now have a global kill ring */
977 /* Pointer to killed data */
978 static char *kill_buffer = 0;
980 void
981 update_input (WInput *in, int clear_first)
983 int has_history = 0;
984 int i;
985 int buf_len = str_length (in->buffer);
986 const char *cp;
987 int pw;
989 if (should_show_history_button (in))
990 has_history = HISTORY_BUTTON_WIDTH;
992 if (in->disable_update)
993 return;
995 pw = str_term_width2 (in->buffer, in->point);
997 /* Make the point visible */
998 if ((pw < in->term_first_shown) ||
999 (pw >= in->term_first_shown + in->field_width - has_history)) {
1001 in->term_first_shown = pw - (in->field_width / 3);
1002 if (in->term_first_shown < 0)
1003 in->term_first_shown = 0;
1006 /* Adjust the mark */
1007 if (in->mark > buf_len)
1008 in->mark = buf_len;
1010 if (has_history)
1011 draw_history_button (in);
1013 tty_setcolor (in->color);
1015 widget_move (&in->widget, 0, 0);
1017 if (!in->is_password) {
1018 tty_print_string (str_term_substring (in->buffer, in->term_first_shown,
1019 in->field_width - has_history));
1020 } else {
1021 cp = in->buffer;
1022 for (i = -in->term_first_shown; i < in->field_width - has_history; i++){
1023 if (i >= 0) {
1024 tty_print_char ((cp[0] != '\0') ? '*' : ' ');
1026 if (cp[0] != '\0') str_cnext_char (&cp);
1030 if (clear_first)
1031 in->first = 0;
1034 void
1035 winput_set_origin (WInput *in, int x, int field_width)
1037 in->widget.x = x;
1038 in->field_width = in->widget.cols = field_width;
1039 update_input (in, 0);
1042 /* {{{ history saving and loading */
1044 int num_history_items_recorded = 60;
1047 This loads and saves the history of an input line to and from the
1048 widget. It is called with the widgets history name on creation of the
1049 widget, and returns the GList list. It stores histories in the file
1050 ~/.mc/history in using the profile code.
1052 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
1053 function) then input_new assigns the default text to be the last text
1054 entered, or "" if not found.
1057 GList *
1058 history_get (const char *input_name)
1060 size_t i;
1061 GList *hist = NULL;
1062 char *profile;
1063 mc_config_t *cfg;
1064 char **keys;
1065 size_t keys_num = 0;
1066 char *this_entry;
1068 if (num_history_items_recorded == 0) /* this is how to disable */
1069 return NULL;
1070 if ((input_name == NULL) || (*input_name == '\0'))
1071 return NULL;
1073 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1074 cfg = mc_config_init (profile);
1076 /* get number of keys */
1077 keys = mc_config_get_keys (cfg, input_name, &keys_num);
1078 g_strfreev (keys);
1080 for (i = 0; i < keys_num; i++) {
1081 char key[BUF_TINY];
1082 g_snprintf (key, sizeof (key), "%lu", (unsigned long)i);
1083 this_entry = mc_config_get_string (cfg, input_name, key, "");
1085 if (this_entry != NULL)
1086 hist = list_append_unique (hist, this_entry);
1089 mc_config_deinit (cfg);
1090 g_free (profile);
1092 /* return pointer to the last entry in the list */
1093 return g_list_last (hist);
1096 void
1097 history_put (const char *input_name, GList *h)
1099 int i;
1100 char *profile;
1101 mc_config_t *cfg;
1103 if (num_history_items_recorded == 0) /* this is how to disable */
1104 return;
1105 if ((input_name == NULL) || (*input_name == '\0'))
1106 return;
1107 if (h == NULL)
1108 return;
1110 profile = g_build_filename (home_dir, MC_USERCONF_DIR, MC_HISTORY_FILE, NULL);
1112 i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
1113 if (i != -1)
1114 close (i);
1116 /* Make sure the history is only readable by the user */
1117 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
1118 g_free (profile);
1119 return;
1122 /* go to end of list */
1123 h = g_list_last (h);
1125 /* go back 60 places */
1126 for (i = 0; (i < num_history_items_recorded - 1) && (h->prev != NULL); i++)
1127 h = g_list_previous (h);
1129 cfg = mc_config_init (profile);
1131 if (input_name != NULL)
1132 mc_config_del_group (cfg, input_name);
1134 /* dump history into profile */
1135 for (i = 0; h != NULL; h = g_list_next (h)) {
1136 char *text = (char *) h->data;
1138 /* We shouldn't have null entries, but let's be sure */
1139 if (text != NULL) {
1140 char key[BUF_TINY];
1141 g_snprintf (key, sizeof (key), "%d", i++);
1142 mc_config_set_string (cfg, input_name, key, text);
1146 mc_config_save_file (cfg, NULL);
1147 mc_config_deinit(cfg);
1148 g_free (profile);
1151 /* }}} history saving and loading */
1154 /* {{{ history display */
1156 static const char *
1157 i18n_htitle (void)
1159 return _(" History ");
1162 typedef struct {
1163 Widget *widget;
1164 size_t count;
1165 size_t maxlen;
1166 } dlg_hist_data;
1168 static cb_ret_t
1169 dlg_hist_reposition (Dlg_head *dlg_head)
1171 dlg_hist_data *data;
1172 int x = 0, y, he, wi;
1174 /* guard checks */
1175 if ((dlg_head == NULL) || (dlg_head->data == NULL))
1176 return MSG_NOT_HANDLED;
1178 data = (dlg_hist_data *) dlg_head->data;
1180 y = data->widget->y;
1181 he = data->count + 2;
1183 if (he <= y || y > (LINES - 6)) {
1184 he = min (he, y - 1);
1185 y -= he;
1186 } else {
1187 y++;
1188 he = min (he, LINES - y);
1191 if (data->widget->x > 2)
1192 x = data->widget->x - 2;
1194 wi = data->maxlen + 4;
1196 if ((wi + x) > COLS) {
1197 wi = min (wi, COLS);
1198 x = COLS - wi;
1201 dlg_set_position (dlg_head, y, x, y + he, x + wi);
1203 return MSG_HANDLED;
1206 static cb_ret_t
1207 dlg_hist_callback (Dlg_head *h, Widget *sender,
1208 dlg_msg_t msg, int parm, void *data)
1210 switch (msg) {
1211 case DLG_RESIZE:
1212 return dlg_hist_reposition (h);
1214 default:
1215 return default_dlg_callback (h, sender, msg, parm, data);
1219 char *
1220 show_hist (GList **history, Widget *widget)
1222 GList *z, *hlist = NULL, *hi;
1223 size_t maxlen, i, count = 0;
1224 char *r = NULL;
1225 Dlg_head *query_dlg;
1226 WListbox *query_list;
1227 dlg_hist_data hist_data;
1229 if (*history == NULL)
1230 return NULL;
1232 maxlen = str_term_width1 (i18n_htitle ()) + 2;
1234 for (z = *history; z != NULL; z = g_list_previous (z)) {
1235 WLEntry *entry;
1237 i = str_term_width1 ((char *) z->data);
1238 maxlen = max (maxlen, i);
1239 count++;
1241 entry = g_new0 (WLEntry, 1);
1242 /* history is being reverted here */
1243 entry->text = g_strdup ((char *) z->data);
1244 hlist = g_list_prepend (hlist, entry);
1247 hist_data.widget = widget;
1248 hist_data.count = count;
1249 hist_data.maxlen = maxlen;
1251 query_dlg =
1252 create_dlg (0, 0, 4, 4, dialog_colors, dlg_hist_callback,
1253 "[History-query]", i18n_htitle (), DLG_COMPACT);
1254 query_dlg->data = &hist_data;
1256 query_list = listbox_new (1, 1, 2, 2, TRUE, NULL);
1258 /* this call makes list stick to all sides of dialog, effectively make
1259 it be resized with dialog */
1260 add_widget_autopos (query_dlg, query_list, WPOS_KEEP_ALL);
1262 /* to avoid diplicating of (calculating sizes in two places)
1263 code, call dlg_hist_callback function here, to set dialog and
1264 controls positions.
1265 The main idea - create 4x4 dialog and add 2x2 list in
1266 center of it, and let dialog function resize it to needed
1267 size. */
1268 dlg_hist_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
1270 if (query_dlg->y < widget->y) {
1271 /* draw list entries from bottom upto top */
1272 listbox_set_list (query_list, hlist);
1273 listbox_select_last (query_list);
1274 } else {
1275 /* draw list entries from top downto bottom */
1276 /* revert history direction */
1277 hlist = g_list_reverse (hlist);
1278 listbox_set_list (query_list, hlist);
1281 if (run_dlg (query_dlg) != B_CANCEL) {
1282 char *q;
1284 listbox_get_current (query_list, &q, NULL);
1285 if (q != NULL)
1286 r = g_strdup (q);
1289 /* get modified history from dialog */
1290 z = NULL;
1291 for (hi = query_list->list; hi != NULL; hi = g_list_next (hi)) {
1292 WLEntry *entry;
1294 entry = (WLEntry *) hi->data;
1295 /* history is being reverted here again */
1296 z = g_list_prepend (z, entry->text);
1297 entry->text = NULL;
1300 destroy_dlg (query_dlg);
1302 /* restore history direction */
1303 if (query_dlg->y < widget->y)
1304 z = g_list_reverse (z);
1306 g_list_foreach (*history, (GFunc) g_free, NULL);
1307 g_list_free (*history);
1308 *history = g_list_last (z);
1310 return r;
1313 static void
1314 do_show_hist (WInput *in)
1316 char *r;
1318 r = show_hist (&in->history, &in->widget);
1319 if (r != NULL) {
1320 assign_text (in, r);
1321 g_free (r);
1325 /* }}} history display */
1327 static void
1328 input_destroy (WInput *in)
1330 if (!in){
1331 fprintf (stderr, "Internal error: null Input *\n");
1332 exit (1);
1335 new_input (in);
1337 if (in->history){
1338 if (!in->is_password) /* don't save passwords ;-) */
1339 history_put (in->history_name, in->history);
1341 in->history = g_list_first (in->history);
1342 g_list_foreach (in->history, (GFunc) g_free, NULL);
1343 g_list_free (in->history);
1346 g_free (in->buffer);
1347 free_completions (in);
1348 g_free (in->history_name);
1351 void
1352 input_disable_update (WInput *in)
1354 in->disable_update++;
1357 void
1358 input_enable_update (WInput *in)
1360 in->disable_update--;
1361 update_input (in, 0);
1365 static void
1366 push_history (WInput *in, const char *text)
1368 /* input widget where urls with passwords are entered without any
1369 vfs prefix */
1370 const char *password_input_fields[] = {
1371 N_(" Link to a remote machine "),
1372 N_(" FTP to machine "),
1373 N_(" SMB link to machine ")
1375 const size_t ELEMENTS = (sizeof (password_input_fields) /
1376 sizeof (password_input_fields[0]));
1378 char *t;
1379 size_t i;
1380 gboolean empty;
1382 if (text == NULL)
1383 return;
1385 #ifdef ENABLE_NLS
1386 for (i = 0; i < ELEMENTS; i++)
1387 password_input_fields[i] = _(password_input_fields[i]);
1388 #endif
1390 t = g_strstrip (g_strdup (text));
1391 empty = *t == '\0';
1392 g_free (t);
1393 t = g_strdup (empty ? "" : text);
1395 if (in->history_name != NULL) {
1396 const char *p = in->history_name + 3;
1398 for (i = 0; i < ELEMENTS; i++)
1399 if (strcmp (p, password_input_fields[i]) == 0)
1400 break;
1402 strip_password (t, i >= ELEMENTS);
1405 in->history = list_append_unique (in->history, t);
1406 in->need_push = 0;
1409 /* Cleans the input line and adds the current text to the history */
1410 void
1411 new_input (WInput *in)
1413 push_history (in, in->buffer);
1414 in->need_push = 1;
1415 in->buffer[0] = '\0';
1416 in->point = 0;
1417 in->charpoint = 0;
1418 in->mark = 0;
1419 free_completions (in);
1420 update_input (in, 0);
1423 static void
1424 move_buffer_backward (WInput *in, int start, int end)
1426 int i, pos, len;
1427 int str_len = str_length (in->buffer);
1428 if (start >= str_len || end > str_len + 1) return;
1430 pos = str_offset_to_pos (in->buffer, start);
1431 len = str_offset_to_pos (in->buffer, end) - pos;
1433 for (i = pos; in->buffer[i + len - 1]; i++)
1434 in->buffer[i] = in->buffer[i + len];
1437 static cb_ret_t
1438 insert_char (WInput *in, int c_code)
1440 size_t i;
1441 int res;
1443 if (c_code == -1)
1444 return MSG_NOT_HANDLED;
1446 if (in->charpoint >= MB_LEN_MAX)
1447 return MSG_HANDLED;
1449 in->charbuf[in->charpoint] = c_code;
1450 in->charpoint++;
1452 res = str_is_valid_char (in->charbuf, in->charpoint);
1453 if (res < 0) {
1454 if (res != -2)
1455 in->charpoint = 0; /* broken multibyte char, skip */
1456 return MSG_HANDLED;
1459 in->need_push = 1;
1460 if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){
1461 /* Expand the buffer */
1462 size_t new_length = in->current_max_size +
1463 in->field_width + in->charpoint;
1464 char *narea = g_try_renew (char, in->buffer, new_length);
1465 if (narea){
1466 in->buffer = narea;
1467 in->current_max_size = new_length;
1471 if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
1472 /* bytes from begin */
1473 size_t ins_point = str_offset_to_pos (in->buffer, in->point);
1474 /* move chars */
1475 size_t rest_bytes = strlen (in->buffer + ins_point);
1477 for (i = rest_bytes + 1; i > 0; i--)
1478 in->buffer[ins_point + i + in->charpoint - 1] =
1479 in->buffer[ins_point + i - 1];
1481 memcpy(in->buffer + ins_point, in->charbuf, in->charpoint);
1482 in->point++;
1485 in->charpoint = 0;
1486 return MSG_HANDLED;
1489 static void
1490 beginning_of_line (WInput *in)
1492 in->point = 0;
1493 in->charpoint = 0;
1496 static void
1497 end_of_line (WInput *in)
1499 in->point = str_length (in->buffer);
1500 in->charpoint = 0;
1503 static void
1504 backward_char (WInput *in)
1506 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1508 if (in->point > 0) {
1509 in->point-= str_cprev_noncomb_char (&act, in->buffer);
1511 in->charpoint = 0;
1514 static void
1515 forward_char (WInput *in)
1517 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1518 if (act[0] != '\0') {
1519 in->point+= str_cnext_noncomb_char (&act);
1521 in->charpoint = 0;
1524 static void
1525 forward_word (WInput * in)
1527 const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1529 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
1530 str_cnext_char (&p);
1531 in->point++;
1533 while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
1534 str_cnext_char (&p);
1535 in->point++;
1539 static void
1540 backward_word (WInput *in)
1542 const char *p;
1543 const char *p_tmp;
1545 for (
1546 p = in->buffer + str_offset_to_pos (in->buffer, in->point);
1547 (p != in->buffer) && (p[0] == '\0');
1548 str_cprev_char (&p), in->point--
1551 while (p != in->buffer) {
1552 p_tmp = p;
1553 str_cprev_char (&p);
1554 if (!str_isspace (p) && !str_ispunct (p)) {
1555 p = p_tmp;
1556 break;
1558 in->point--;
1560 while (p != in->buffer) {
1561 str_cprev_char (&p);
1562 if (str_isspace (p) || str_ispunct (p))
1563 break;
1565 in->point--;
1569 static void
1570 key_left (WInput *in)
1572 backward_char (in);
1575 static void
1576 key_ctrl_left (WInput *in)
1578 backward_word (in);
1581 static void
1582 key_right (WInput *in)
1584 forward_char (in);
1587 static void
1588 key_ctrl_right (WInput *in)
1590 forward_word (in);
1592 static void
1593 backward_delete (WInput *in)
1595 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1596 int start;
1598 if (in->point == 0)
1599 return;
1601 start = in->point - str_cprev_noncomb_char (&act, in->buffer);
1602 move_buffer_backward(in, start, in->point);
1603 in->charpoint = 0;
1604 in->need_push = 1;
1605 in->point = start;
1608 static void
1609 delete_char (WInput *in)
1611 const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
1612 int end = in->point;
1614 end+= str_cnext_noncomb_char (&act);
1616 move_buffer_backward(in, in->point, end);
1617 in->charpoint = 0;
1618 in->need_push = 1;
1621 static void
1622 copy_region (WInput *in, int x_first, int x_last)
1624 int first = min (x_first, x_last);
1625 int last = max (x_first, x_last);
1627 if (last == first) {
1628 /* Copy selected files to clipboard */
1629 panel_save_curent_file_to_clip_file ();
1630 return;
1633 g_free (kill_buffer);
1635 first = str_offset_to_pos (in->buffer, first);
1636 last = str_offset_to_pos (in->buffer, last);
1638 kill_buffer = g_strndup(in->buffer + first, last - first);
1639 save_text_to_clip_file (kill_buffer);
1642 static void
1643 delete_region (WInput *in, int x_first, int x_last)
1645 int first = min (x_first, x_last);
1646 int last = max (x_first, x_last);
1647 size_t len;
1649 in->point = first;
1650 if (in->mark > first)
1651 in->mark = first;
1652 last = str_offset_to_pos (in->buffer, last);
1653 first = str_offset_to_pos (in->buffer, first);
1654 len = strlen (&in->buffer[last]) + 1;
1655 memmove (&in->buffer[first], &in->buffer[last], len);
1656 in->charpoint = 0;
1657 in->need_push = 1;
1660 static void
1661 kill_word (WInput *in)
1663 int old_point = in->point;
1664 int new_point;
1666 forward_word (in);
1667 new_point = in->point;
1668 in->point = old_point;
1670 copy_region (in, old_point, new_point);
1671 delete_region (in, old_point, new_point);
1672 in->need_push = 1;
1673 in->charpoint = 0;
1674 in->charpoint = 0;
1677 static void
1678 back_kill_word (WInput *in)
1680 int old_point = in->point;
1681 int new_point;
1683 backward_word (in);
1684 new_point = in->point;
1685 in->point = old_point;
1687 copy_region (in, old_point, new_point);
1688 delete_region (in, old_point, new_point);
1689 in->need_push = 1;
1692 static void
1693 set_mark (WInput *in)
1695 in->mark = in->point;
1698 static void
1699 kill_save (WInput *in)
1701 copy_region (in, in->mark, in->point);
1704 static void
1705 kill_region (WInput *in)
1707 kill_save (in);
1708 delete_region (in, in->point, in->mark);
1711 static void
1712 clear_region (WInput *in)
1714 delete_region (in, in->point, in->mark);
1717 static void
1718 yank (WInput *in)
1720 char *p;
1722 if (!kill_buffer)
1723 return;
1724 in->charpoint = 0;
1725 for (p = kill_buffer; *p; p++)
1726 insert_char (in, *p);
1727 in->charpoint = 0;
1730 static void
1731 kill_line (WInput *in)
1733 int chp = str_offset_to_pos (in->buffer, in->point);
1734 g_free (kill_buffer);
1735 kill_buffer = g_strdup (&in->buffer[chp]);
1736 in->buffer[chp] = '\0';
1737 in->charpoint = 0;
1740 static void
1741 ins_from_clip (WInput *in)
1743 char *p = NULL;
1745 if (load_text_from_clip_file (&p)) {
1746 char *pp;
1748 for (pp = p; *pp != '\0'; pp++)
1749 insert_char (in, *pp);
1751 g_free (p);
1755 void
1756 assign_text (WInput *in, const char *text)
1758 free_completions (in);
1759 g_free (in->buffer);
1760 in->buffer = g_strdup (text); /* was in->buffer->text */
1761 in->current_max_size = strlen (in->buffer) + 1;
1762 in->point = str_length (in->buffer);
1763 in->mark = 0;
1764 in->need_push = 1;
1765 in->charpoint = 0;
1768 static void
1769 hist_prev (WInput *in)
1771 GList *prev;
1773 if (!in->history)
1774 return;
1776 if (in->need_push)
1777 push_history (in, in->buffer);
1779 prev = g_list_previous (in->history);
1780 if (prev != NULL) {
1781 in->history = prev;
1782 assign_text (in, (char *) prev->data);
1783 in->need_push = 0;
1787 static void
1788 hist_next (WInput *in)
1790 if (in->need_push) {
1791 push_history (in, in->buffer);
1792 assign_text (in, "");
1793 return;
1796 if (!in->history)
1797 return;
1799 if (!in->history->next) {
1800 assign_text (in, "");
1801 return;
1804 in->history = g_list_next (in->history);
1805 assign_text (in, (char *) in->history->data);
1806 in->need_push = 0;
1809 static void
1810 port_region_marked_for_delete (WInput *in)
1812 in->buffer[0] = '\0';
1813 in->point = 0;
1814 in->first = 0;
1815 in->charpoint = 0;
1818 static cb_ret_t
1819 input_execute_cmd (WInput *in, unsigned long command)
1821 cb_ret_t res = MSG_HANDLED;
1823 switch (command) {
1824 case CK_InputBol:
1825 beginning_of_line (in);
1826 break;
1827 case CK_InputEol:
1828 end_of_line (in);
1829 break;
1830 case CK_InputMoveLeft:
1831 key_left (in);
1832 break;
1833 case CK_InputWordLeft:
1834 key_ctrl_left (in);
1835 break;
1836 case CK_InputMoveRight:
1837 key_right (in);
1838 break;
1839 case CK_InputWordRight:
1840 key_ctrl_right (in);
1841 break;
1842 case CK_InputBackwardChar:
1843 backward_char (in);
1844 break;
1845 case CK_InputBackwardWord:
1846 backward_word (in);
1847 break;
1848 case CK_InputForwardChar:
1849 forward_char (in);
1850 break;
1851 case CK_InputForwardWord:
1852 forward_word (in);
1853 break;
1854 case CK_InputBackwardDelete:
1855 backward_delete (in);
1856 break;
1857 case CK_InputDeleteChar:
1858 delete_char (in);
1859 break;
1860 case CK_InputKillWord:
1861 kill_word (in);
1862 break;
1863 case CK_InputBackwardKillWord:
1864 back_kill_word (in);
1865 break;
1866 case CK_InputSetMark:
1867 set_mark (in);
1868 break;
1869 case CK_InputKillRegion:
1870 kill_region (in);
1871 break;
1872 case CK_InputClearLine:
1873 clear_region (in);
1874 break;
1875 case CK_InputKillSave:
1876 kill_save (in);
1877 break;
1878 case CK_InputYank:
1879 yank (in);
1880 break;
1881 case CK_InputPaste:
1882 ins_from_clip (in);
1883 break;
1884 case CK_InputKillLine:
1885 kill_line (in);
1886 break;
1887 case CK_InputHistoryPrev:
1888 hist_prev (in);
1889 break;
1890 case CK_InputHistoryNext:
1891 hist_next (in);
1892 break;
1893 case CK_InputHistoryShow:
1894 do_show_hist (in);
1895 break;
1896 case CK_InputComplete:
1897 complete (in);
1898 break;
1899 default:
1900 res = MSG_NOT_HANDLED;
1903 return res;
1906 /* This function is a test for a special input key used in complete.c */
1907 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1908 and 2 if it is a complete key */
1910 is_in_input_map (WInput *in, int key)
1912 size_t i;
1913 for (i = 0; input_map[i].key != 0; i++)
1914 if (key == input_map[i].key) {
1915 input_execute_cmd (in, input_map[i].command);
1916 return (input_map[i].command == CK_InputComplete) ? 2 : 1;
1918 return 0;
1921 cb_ret_t
1922 handle_char (WInput *in, int key)
1924 cb_ret_t v;
1925 int i;
1927 v = MSG_NOT_HANDLED;
1929 if (quote) {
1930 free_completions (in);
1931 v = insert_char (in, key);
1932 update_input (in, 1);
1933 quote = 0;
1934 return v;
1936 for (i = 0; input_map[i].key; i++) {
1937 if (key == input_map[i].key) {
1938 if (input_map[i].command != CK_InputComplete)
1939 free_completions (in);
1940 input_execute_cmd (in, input_map[i].command);
1941 update_input (in, 1);
1942 v = MSG_HANDLED;
1943 break;
1946 if (input_map[i].command == 0) {
1947 if (key > 255)
1948 return MSG_NOT_HANDLED;
1949 if (in->first)
1950 port_region_marked_for_delete (in);
1951 free_completions (in);
1952 v = insert_char (in, key);
1954 update_input (in, 1);
1955 return v;
1958 /* Inserts text in input line */
1959 void
1960 stuff (WInput *in, const char *text, int insert_extra_space)
1962 input_disable_update (in);
1963 while (*text != '\0')
1964 handle_char (in, (unsigned char) *text++); /* unsigned extension char->int */
1965 if (insert_extra_space)
1966 handle_char (in, ' ');
1967 input_enable_update (in);
1968 update_input (in, 1);
1971 void
1972 input_set_point (WInput *in, int pos)
1974 int max_pos = str_length (in->buffer);
1976 if (pos > max_pos)
1977 pos = max_pos;
1978 if (pos != in->point)
1979 free_completions (in);
1980 in->point = pos;
1981 in->charpoint = 0;
1982 update_input (in, 1);
1985 cb_ret_t
1986 input_callback (Widget *w, widget_msg_t msg, int parm)
1988 WInput *in = (WInput *) w;
1989 cb_ret_t v;
1991 switch (msg) {
1992 case WIDGET_KEY:
1993 if (parm == XCTRL ('q')) {
1994 quote = 1;
1995 v = handle_char (in, ascii_alpha_to_cntrl (tty_getch ()));
1996 quote = 0;
1997 return v;
2000 /* Keys we want others to handle */
2001 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
2002 || parm == KEY_F (10) || parm == '\n')
2003 return MSG_NOT_HANDLED;
2005 /* When pasting multiline text, insert literal Enter */
2006 if ((parm & ~KEY_M_MASK) == '\n') {
2007 quote = 1;
2008 v = handle_char (in, '\n');
2009 quote = 0;
2010 return v;
2013 return handle_char (in, parm);
2015 case WIDGET_COMMAND:
2016 return input_execute_cmd (in, parm);
2018 case WIDGET_FOCUS:
2019 case WIDGET_UNFOCUS:
2020 case WIDGET_DRAW:
2021 update_input (in, 0);
2022 return MSG_HANDLED;
2024 case WIDGET_CURSOR:
2025 widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point)
2026 - in->term_first_shown);
2027 return MSG_HANDLED;
2029 case WIDGET_DESTROY:
2030 input_destroy (in);
2031 return MSG_HANDLED;
2033 default:
2034 return default_proc (msg, parm);
2038 static int
2039 input_event (Gpm_Event * event, void *data)
2041 WInput *in = data;
2043 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2044 dlg_select_widget (in);
2046 if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1
2047 && should_show_history_button (in)) {
2048 do_show_hist (in);
2049 } else {
2050 in->point = str_length (in->buffer);
2051 if (event->x + in->term_first_shown - 1 < str_term_width1 (in->buffer))
2052 in->point = str_column_to_pos (in->buffer, event->x
2053 + in->term_first_shown - 1);
2055 update_input (in, 1);
2057 return MOU_NORMAL;
2060 WInput *
2061 input_new (int y, int x, int color, int width, const char *def_text,
2062 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
2064 WInput *in = g_new (WInput, 1);
2065 size_t initial_buffer_len;
2067 init_widget (&in->widget, y, x, 1, width, input_callback, input_event);
2069 /* history setup */
2070 in->history_name = NULL;
2071 in->history = NULL;
2072 if ((histname != NULL) && (*histname != '\0')) {
2073 in->history_name = g_strdup (histname);
2074 in->history = history_get (histname);
2077 if (def_text == NULL)
2078 def_text = "";
2079 else if (def_text == INPUT_LAST_TEXT) {
2080 if ((in->history != NULL) && (in->history->data != NULL))
2081 def_text = (char *) in->history->data;
2082 else
2083 def_text = "";
2086 initial_buffer_len = 1 + max ((size_t) width, strlen (def_text));
2087 in->widget.options |= W_IS_INPUT;
2088 in->completions = NULL;
2089 in->completion_flags = completion_flags;
2090 in->current_max_size = initial_buffer_len;
2091 in->buffer = g_new (char, initial_buffer_len);
2092 in->color = color;
2093 in->field_width = width;
2094 in->first = 1;
2095 in->term_first_shown = 0;
2096 in->disable_update = 0;
2097 in->mark = 0;
2098 in->need_push = 1;
2099 in->is_password = 0;
2101 strcpy (in->buffer, def_text);
2102 in->point = str_length (in->buffer);
2103 in->charpoint = 0;
2105 return in;
2109 /* Listbox widget */
2111 /* Should draw the scrollbar, but currently draws only
2112 * indications that there is more information
2115 static void
2116 listbox_entry_free (void *data)
2118 WLEntry *e = data;
2119 g_free (e->text);
2120 g_free (e);
2123 static void
2124 listbox_drawscroll (WListbox *l)
2126 const int max_line = l->widget.lines - 1;
2127 int line = 0;
2128 int i;
2130 /* Are we at the top? */
2131 widget_move (&l->widget, 0, l->widget.cols);
2132 if (l->top == 0)
2133 tty_print_one_vline ();
2134 else
2135 tty_print_char ('^');
2137 /* Are we at the bottom? */
2138 widget_move (&l->widget, max_line, l->widget.cols);
2139 if ((l->top + l->widget.lines == l->count) || (l->widget.lines >= l->count))
2140 tty_print_one_vline ();
2141 else
2142 tty_print_char ('v');
2144 /* Now draw the nice relative pointer */
2145 if (l->count != 0)
2146 line = 1 + ((l->pos * (l->widget.lines - 2)) / l->count);
2148 for (i = 1; i < max_line; i++) {
2149 widget_move (&l->widget, i, l->widget.cols);
2150 if (i != line)
2151 tty_print_one_vline ();
2152 else
2153 tty_print_char ('*');
2157 static void
2158 listbox_draw (WListbox *l, gboolean focused)
2160 const Dlg_head *h = l->widget.parent;
2161 const int normalc = DLG_NORMALC (h);
2162 int selc = focused ? DLG_HOT_FOCUSC (h) : DLG_FOCUSC (h);
2164 GList *le;
2165 int pos;
2166 int i;
2167 int sel_line = -1;
2169 le = g_list_nth (l->list, l->top);
2170 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
2171 pos = (le == NULL) ? 0 : l->top;
2173 for (i = 0; i < l->widget.lines; i++) {
2174 const char *text;
2176 /* Display the entry */
2177 if (pos == l->pos && sel_line == -1) {
2178 sel_line = i;
2179 tty_setcolor (selc);
2180 } else
2181 tty_setcolor (normalc);
2183 widget_move (&l->widget, i, 1);
2185 if ((i > 0 && pos >= l->count) || (l->list == NULL) || (le == NULL))
2186 text = "";
2187 else {
2188 WLEntry *e = (WLEntry *) le->data;
2189 text = e->text;
2190 le = g_list_next (le);
2191 pos++;
2194 tty_print_string (str_fit_to_term (text, l->widget.cols - 2, J_LEFT_FIT));
2197 l->cursor_y = sel_line;
2199 if (l->scrollbar && (l->count > l->widget.lines)) {
2200 tty_setcolor (normalc);
2201 listbox_drawscroll (l);
2205 static int
2206 listbox_check_hotkey (WListbox *l, int key)
2208 int i;
2209 GList *le;
2211 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2212 WLEntry *e = (WLEntry *) le->data;
2214 if (e->hotkey == key)
2215 return i;
2218 return (-1);
2221 /* Selects the last entry and scrolls the list to the bottom */
2222 void
2223 listbox_select_last (WListbox *l)
2225 l->pos = l->count - 1;
2226 l->top = (l->count > l->widget.lines) ? (l->count - l->widget.lines) : 0;
2229 /* Selects the first entry and scrolls the list to the top */
2230 void
2231 listbox_select_first (WListbox *l)
2233 l->pos = l->top = 0;
2236 void
2237 listbox_set_list (WListbox *l, GList *list)
2239 listbox_remove_list (l);
2241 if (l != NULL) {
2242 l->list = list;
2243 l->top = l->pos = 0;
2244 l->count = g_list_length (list);
2248 void
2249 listbox_remove_list (WListbox *l)
2251 if ((l != NULL) && (l->count != 0)) {
2252 g_list_foreach (l->list, (GFunc) listbox_entry_free, NULL);
2253 g_list_free (l->list);
2254 l->list = NULL;
2255 l->count = l->pos = l->top = 0;
2259 void
2260 listbox_remove_current (WListbox *l)
2262 if ((l != NULL) && (l->count != 0)) {
2263 GList *current;
2265 current = g_list_nth (l->list, l->pos);
2266 l->list = g_list_remove_link (l->list, current);
2267 listbox_entry_free ((WLEntry *) current->data);
2268 g_list_free_1 (current);
2269 l->count--;
2271 if (l->count == 0)
2272 l->top = l->pos = 0;
2273 else if (l->pos >= l->count)
2274 l->pos = l->count - 1;
2278 void
2279 listbox_select_entry (WListbox *l, int dest)
2281 GList *le;
2282 int pos;
2283 gboolean top_seen = FALSE;
2285 if (dest < 0)
2286 return;
2288 /* Special case */
2289 for (pos = 0, le = l->list; le != NULL; pos++, le = g_list_next (le)) {
2290 if (pos == l->top)
2291 top_seen = TRUE;
2293 if (pos == dest) {
2294 l->pos = dest;
2295 if (!top_seen)
2296 l->top = l->pos;
2297 else
2298 if (l->pos - l->top >= l->widget.lines)
2299 l->top = l->pos - l->widget.lines + 1;
2300 return;
2304 /* If we are unable to find it, set decent values */
2305 l->pos = l->top = 0;
2308 /* Selects from base the pos element */
2309 static int
2310 listbox_select_pos (WListbox *l, int base, int pos)
2312 int last = l->count - 1;
2314 base += pos;
2315 if (base >= last)
2316 base = last;
2318 return base;
2321 static void
2322 listbox_fwd (WListbox *l)
2324 if (l->pos + 1 >= l->count)
2325 listbox_select_first (l);
2326 else
2327 listbox_select_entry (l, l->pos + 1);
2330 static void
2331 listbox_back (WListbox *l)
2333 if (l->pos <= 0)
2334 listbox_select_last (l);
2335 else
2336 listbox_select_entry (l, l->pos - 1);
2339 /* Return MSG_HANDLED if we want a redraw */
2340 static cb_ret_t
2341 listbox_key (WListbox *l, int key)
2343 int i;
2345 cb_ret_t j = MSG_NOT_HANDLED;
2347 if (l->list == NULL)
2348 return MSG_NOT_HANDLED;
2350 /* focus on listbox item N by '0'..'9' keys */
2351 if (key >= '0' && key <= '9') {
2352 int oldpos = l->pos;
2353 listbox_select_entry (l, key - '0');
2355 /* need scroll to item? */
2356 if (abs (oldpos - l->pos) > l->widget.lines)
2357 l->top = l->pos;
2359 return MSG_HANDLED;
2362 switch (key){
2363 case KEY_HOME:
2364 case KEY_A1:
2365 case ALT ('<'):
2366 listbox_select_first (l);
2367 return MSG_HANDLED;
2369 case KEY_END:
2370 case KEY_C1:
2371 case ALT ('>'):
2372 listbox_select_last (l);
2373 return MSG_HANDLED;
2375 case XCTRL('p'):
2376 case KEY_UP:
2377 listbox_back (l);
2378 return MSG_HANDLED;
2380 case XCTRL('n'):
2381 case KEY_DOWN:
2382 listbox_fwd (l);
2383 return MSG_HANDLED;
2385 case KEY_NPAGE:
2386 case XCTRL('v'):
2387 for (i = 0; (i < l->widget.lines - 1) && (l->pos < l->count - 1); i++) {
2388 listbox_fwd (l);
2389 j = MSG_HANDLED;
2391 break;
2393 case KEY_PPAGE:
2394 case ALT('v'):
2395 for (i = 0; (i < l->widget.lines - 1) && (l->pos > 0); i++) {
2396 listbox_back (l);
2397 j = MSG_HANDLED;
2399 break;
2401 case KEY_DC:
2402 case 'd':
2403 if (l->deletable) {
2404 gboolean is_last = (l->pos + 1 >= l->count);
2405 gboolean is_more = (l->top + l->widget.lines >= l->count);
2407 listbox_remove_current (l);
2408 if ((l->top > 0) && (is_last || is_more))
2409 l->top--;
2411 return MSG_HANDLED;
2413 case (KEY_M_SHIFT | KEY_DC):
2414 case 'D':
2415 if (l->deletable && confirm_history_cleanup
2416 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
2417 && (query_dialog (Q_("DialogTitle|History cleanup"),
2418 _("Do you want clean this history?"),
2419 D_ERROR, 2, _("&Yes"), _("&No")) == 0)) {
2420 listbox_remove_list (l);
2421 j = MSG_HANDLED;
2423 break;
2425 default:
2426 break;
2429 return j;
2432 static inline void
2433 listbox_destroy (WListbox *l)
2435 /* don't delete list in modifable listbox */
2436 if (!l->deletable)
2437 listbox_remove_list (l);
2440 static cb_ret_t
2441 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2443 WListbox *l = (WListbox *) w;
2444 Dlg_head *h = l->widget.parent;
2445 cb_ret_t ret_code;
2447 switch (msg) {
2448 case WIDGET_INIT:
2449 return MSG_HANDLED;
2451 case WIDGET_HOTKEY:
2453 int pos, action;
2455 pos = listbox_check_hotkey (l, parm);
2456 if (pos < 0)
2457 return MSG_NOT_HANDLED;
2459 listbox_select_entry (l, pos);
2460 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2462 if (l->cback != NULL)
2463 action = l->cback (l);
2464 else
2465 action = LISTBOX_DONE;
2467 if (action == LISTBOX_DONE) {
2468 h->ret_value = B_ENTER;
2469 dlg_stop (h);
2472 return MSG_HANDLED;
2475 case WIDGET_KEY:
2476 ret_code = listbox_key (l, parm);
2477 if (ret_code != MSG_NOT_HANDLED) {
2478 listbox_draw (l, TRUE);
2479 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2481 return ret_code;
2483 case WIDGET_CURSOR:
2484 widget_move (&l->widget, l->cursor_y, 0);
2485 h->callback (h, w, DLG_ACTION, l->pos, NULL);
2486 return MSG_HANDLED;
2488 case WIDGET_FOCUS:
2489 case WIDGET_UNFOCUS:
2490 case WIDGET_DRAW:
2491 listbox_draw (l, msg != WIDGET_UNFOCUS);
2492 return MSG_HANDLED;
2494 case WIDGET_DESTROY:
2495 listbox_destroy (l);
2496 return MSG_HANDLED;
2498 case WIDGET_RESIZED:
2499 return MSG_HANDLED;
2501 default:
2502 return default_proc (msg, parm);
2506 static int
2507 listbox_event (Gpm_Event *event, void *data)
2509 WListbox *l = data;
2510 int i;
2512 Dlg_head *h = l->widget.parent;
2514 /* Single click */
2515 if (event->type & GPM_DOWN)
2516 dlg_select_widget (l);
2518 if (l->list == NULL)
2519 return MOU_NORMAL;
2521 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2522 int ret = MOU_REPEAT;
2524 if (event->x < 0 || event->x > l->widget.cols)
2525 return ret;
2527 if (event->y < 1)
2528 for (i = -event->y; i >= 0; i--)
2529 listbox_back (l);
2530 else if (event->y > l->widget.lines)
2531 for (i = event->y - l->widget.lines; i > 0; i--)
2532 listbox_fwd (l);
2533 else if (event->buttons & GPM_B_UP) {
2534 listbox_back (l);
2535 ret = MOU_NORMAL;
2536 } else if (event->buttons & GPM_B_DOWN) {
2537 listbox_fwd (l);
2538 ret = MOU_NORMAL;
2539 } else
2540 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2542 /* We need to refresh ourselves since the dialog manager doesn't */
2543 /* know about this event */
2544 listbox_draw (l, TRUE);
2545 return ret;
2548 /* Double click */
2549 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2550 int action;
2552 if (event->x < 0 || event->x >= l->widget.cols
2553 || event->y < 1 || event->y > l->widget.lines)
2554 return MOU_NORMAL;
2556 dlg_select_widget (l);
2557 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2559 if (l->cback != NULL)
2560 action = l->cback (l);
2561 else
2562 action = LISTBOX_DONE;
2564 if (action == LISTBOX_DONE) {
2565 h->ret_value = B_ENTER;
2566 dlg_stop (h);
2567 return MOU_NORMAL;
2570 return MOU_NORMAL;
2573 WListbox *
2574 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback callback)
2576 WListbox *l = g_new (WListbox, 1);
2578 if (height <= 0)
2579 height = 1;
2581 init_widget (&l->widget, y, x, height, width,
2582 listbox_callback, listbox_event);
2584 l->list = NULL;
2585 l->top = l->pos = 0;
2586 l->count = 0;
2587 l->deletable = deletable;
2588 l->cback = callback;
2589 l->allow_duplicates = TRUE;
2590 l->scrollbar = !tty_is_slow ();
2591 widget_want_hotkey (l->widget, 1);
2593 return l;
2596 static int
2597 listbox_entry_cmp (const void *a, const void *b)
2599 const WLEntry *ea = (const WLEntry *) a;
2600 const WLEntry *eb = (const WLEntry *) b;
2602 return strcmp (ea->text, eb->text);
2605 /* Listbox item adding function */
2606 static inline void
2607 listbox_append_item (WListbox *l, WLEntry *e, listbox_append_t pos)
2609 switch (pos) {
2610 case LISTBOX_APPEND_AT_END:
2611 l->list = g_list_append (l->list, e);
2612 break;
2614 case LISTBOX_APPEND_BEFORE:
2615 l->list = g_list_insert_before (l->list, g_list_nth (l->list, l->pos), e);
2616 if (l->pos > 0)
2617 l->pos--;
2618 break;
2620 case LISTBOX_APPEND_AFTER:
2621 l->list = g_list_insert (l->list, e, l->pos + 1);
2622 break;
2624 case LISTBOX_APPEND_SORTED:
2625 l->list = g_list_insert_sorted (l->list, e, (GCompareFunc) listbox_entry_cmp);
2626 break;
2628 default:
2629 return;
2632 l->count++;
2635 char *
2636 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey,
2637 const char *text, void *data)
2639 WLEntry *entry;
2641 if (l == NULL)
2642 return NULL;
2644 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
2645 return NULL;
2647 entry = g_new (WLEntry, 1);
2648 entry->text = g_strdup (text);
2649 entry->data = data;
2650 entry->hotkey = hotkey;
2652 listbox_append_item (l, entry, pos);
2654 return entry->text;
2658 listbox_search_text (WListbox *l, const char *text)
2660 if (l != NULL) {
2661 int i;
2662 GList *le;
2664 for (i = 0, le = l->list; le != NULL; i++, le = g_list_next (le)) {
2665 WLEntry *e = (WLEntry *) le->data;
2667 if (strcmp (e->text, text) == 0)
2668 return i;
2672 return (-1);
2675 /* Returns the current string text as well as the associated extra data */
2676 void
2677 listbox_get_current (WListbox *l, char **string, void **extra)
2679 WLEntry *e = NULL;
2680 gboolean ok;
2682 if (l != NULL)
2683 e = (WLEntry *) g_list_nth_data (l->list, l->pos);
2685 ok = (e != NULL);
2687 if (string != NULL)
2688 *string = ok ? e->text : NULL;
2690 if (extra != NULL)
2691 *extra = ok ? e->data : NULL;
2695 /* ButtonBar widget */
2697 /* returns TRUE if a function has been called, FALSE otherwise. */
2698 static gboolean
2699 buttonbar_call (WButtonBar *bb, int i)
2701 cb_ret_t ret = MSG_NOT_HANDLED;
2703 if (bb != NULL)
2704 ret = bb->widget.parent->callback (bb->widget.parent,
2705 (Widget *) bb, DLG_ACTION,
2706 bb->labels[i].command,
2707 bb->labels[i].receiver);
2708 return ret;
2711 /* calculate width of one button, width is never lesser than 7 */
2712 static int
2713 buttonbat_get_button_width (void)
2715 int result = COLS / BUTTONBAR_LABELS_NUM;
2716 return (result >= 7) ? result : 7;
2719 static cb_ret_t
2720 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2722 WButtonBar *bb = (WButtonBar *) w;
2723 int i;
2724 const char *text;
2726 switch (msg) {
2727 case WIDGET_FOCUS:
2728 return MSG_NOT_HANDLED;
2730 case WIDGET_HOTKEY:
2731 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2732 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2733 return MSG_HANDLED;
2734 return MSG_NOT_HANDLED;
2736 case WIDGET_DRAW:
2737 if (bb->visible) {
2738 int offset = 0;
2739 int count_free_positions;
2741 widget_move (&bb->widget, 0, 0);
2742 tty_setcolor (DEFAULT_COLOR);
2743 bb->btn_width = buttonbat_get_button_width ();
2744 tty_printf ("%-*s", bb->widget.cols, "");
2745 count_free_positions = COLS - bb->btn_width * BUTTONBAR_LABELS_NUM;
2747 for (i = 0; i < COLS / bb->btn_width && i < BUTTONBAR_LABELS_NUM; i++) {
2748 widget_move (&bb->widget, 0, (i * bb->btn_width) + offset);
2749 tty_setcolor (BUTTONBAR_HOTKEY_COLOR);
2750 tty_printf ("%2d", i + 1);
2751 tty_setcolor (BUTTONBAR_BUTTON_COLOR);
2752 text = (bb->labels[i].text != NULL) ? bb->labels[i].text : "";
2753 tty_print_string (str_fit_to_term (
2754 text,
2755 bb->btn_width - 2 + (int)(offset < count_free_positions),
2756 J_LEFT_FIT));
2758 if (count_free_positions != 0 && offset < count_free_positions)
2759 offset++;
2762 return MSG_HANDLED;
2764 case WIDGET_DESTROY:
2765 for (i = 0; i < BUTTONBAR_LABELS_NUM; i++)
2766 g_free (bb->labels[i].text);
2767 return MSG_HANDLED;
2769 default:
2770 return default_proc (msg, parm);
2774 static int
2775 buttonbar_event (Gpm_Event *event, void *data)
2777 WButtonBar *bb = data;
2778 int button;
2780 if (!(event->type & GPM_UP))
2781 return MOU_NORMAL;
2782 if (event->y == 2)
2783 return MOU_NORMAL;
2784 button = (event->x - 1) * BUTTONBAR_LABELS_NUM / COLS;
2785 if (button < BUTTONBAR_LABELS_NUM)
2786 buttonbar_call (bb, button);
2787 return MOU_NORMAL;
2790 WButtonBar *
2791 buttonbar_new (gboolean visible)
2793 WButtonBar *bb;
2795 bb = g_new0 (WButtonBar, 1);
2797 init_widget (&bb->widget, LINES - 1, 0, 1, COLS,
2798 buttonbar_callback, buttonbar_event);
2799 bb->widget.pos_flags = WPOS_KEEP_HORZ | WPOS_KEEP_BOTTOM;
2800 bb->visible = visible;
2801 widget_want_hotkey (bb->widget, 1);
2802 widget_want_cursor (bb->widget, 0);
2803 bb->btn_width = buttonbat_get_button_width ();
2805 return bb;
2808 static void
2809 set_label_text (WButtonBar *bb, int lc_index, const char *text)
2811 g_free (bb->labels[lc_index - 1].text);
2812 bb->labels[lc_index - 1].text = g_strdup (text);
2815 /* Find ButtonBar widget in the dialog */
2816 WButtonBar *
2817 find_buttonbar (const Dlg_head *h)
2819 return (WButtonBar *) find_widget_type (h, buttonbar_callback);
2822 void
2823 buttonbar_set_label (WButtonBar *bb, int idx, const char *text,
2824 const struct global_keymap_t *keymap, const Widget *receiver)
2826 if ((bb != NULL) && (idx >= 1) && (idx <= BUTTONBAR_LABELS_NUM)) {
2827 unsigned long command = CK_Ignore_Key;
2829 if (keymap != NULL)
2830 command = lookup_keymap_command (keymap, KEY_F (idx));
2832 if ((text == NULL) || (text[0] == '\0'))
2833 set_label_text (bb, idx, "");
2834 else
2835 set_label_text (bb, idx, text);
2837 bb->labels[idx - 1].command = command;
2838 bb->labels[idx - 1].receiver = (Widget *) receiver;
2842 void
2843 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2845 bb->visible = visible;
2848 void
2849 buttonbar_redraw (WButtonBar *bb)
2851 if (bb != NULL)
2852 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2855 static cb_ret_t
2856 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2858 WGroupbox *g = (WGroupbox *) w;
2860 switch (msg) {
2861 case WIDGET_INIT:
2862 return MSG_HANDLED;
2864 case WIDGET_FOCUS:
2865 return MSG_NOT_HANDLED;
2867 case WIDGET_DRAW:
2868 tty_setcolor (COLOR_NORMAL);
2869 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2870 g->widget.x - g->widget.parent->x, g->widget.lines,
2871 g->widget.cols);
2873 tty_setcolor (COLOR_HOT_NORMAL);
2874 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2875 g->widget.x - g->widget.parent->x + 1);
2876 tty_print_string (g->title);
2877 return MSG_HANDLED;
2879 case WIDGET_DESTROY:
2880 g_free (g->title);
2881 return MSG_HANDLED;
2883 default:
2884 return default_proc (msg, parm);
2888 WGroupbox *
2889 groupbox_new (int y, int x, int height, int width, const char *title)
2891 WGroupbox *g = g_new (WGroupbox, 1);
2893 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2895 g->widget.options &= ~W_WANT_CURSOR;
2896 widget_want_hotkey (g->widget, 0);
2898 /* Strip existing spaces, add one space before and after the title */
2899 if (title) {
2900 char *t;
2901 t = g_strstrip (g_strdup (title));
2902 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2903 g_free (t);
2906 return g;