added a new parameter for completion flags to input_new
[midnight-commander.git] / src / widget.c
blobbc30bb18b3b207db971d268dcb84ad5bf07bef30
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007 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 #include <config.h>
30 #include <assert.h>
31 #include <ctype.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <string.h>
36 #include <sys/types.h>
38 #include "global.h"
39 #include "tty.h"
40 #include "color.h"
41 #include "mouse.h"
42 #include "dialog.h"
43 #include "widget.h"
44 #include "win.h"
45 #include "key.h" /* XCTRL and ALT macros */
46 #include "profile.h" /* for history loading and saving */
47 #include "wtools.h" /* For common_dialog_repaint() */
48 #include "main.h" /* for `slow_terminal' */
50 #define HISTORY_FILE_NAME ".mc/history"
52 struct WButtonBar {
53 Widget widget;
54 int visible; /* Is it visible? */
55 struct {
56 char *text;
57 enum { BBFUNC_NONE, BBFUNC_VOID, BBFUNC_PTR } tag;
58 union {
59 voidfn fn_void;
60 buttonbarfn fn_ptr;
61 } u;
62 void *data;
63 } labels [10];
66 static int button_event (Gpm_Event *event, void *);
68 int quote = 0;
70 static void
71 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
73 Dlg_head *h = w->parent;
75 attrset (hotkey
76 ? (focused
77 ? DLG_HOT_FOCUSC (h)
78 : DLG_HOT_NORMALC (h))
79 : (focused
80 ? DLG_FOCUSC (h)
81 : DLG_NORMALC (h)));
84 static cb_ret_t
85 button_callback (Widget *w, widget_msg_t msg, int parm)
87 WButton *b = (WButton *) w;
88 char buf[BUF_SMALL];
89 int stop = 0;
90 int off = 0;
91 Dlg_head *h = b->widget.parent;
93 switch (msg) {
94 case WIDGET_HOTKEY:
96 * Don't let the default button steal Enter from the current
97 * button. This is a workaround for the flawed event model
98 * when hotkeys are sent to all widgets before the key is
99 * handled by the current widget.
101 if (parm == '\n' && h->current == &b->widget) {
102 button_callback (w, WIDGET_KEY, ' ');
103 return MSG_HANDLED;
106 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
107 button_callback (w, WIDGET_KEY, ' ');
108 return MSG_HANDLED;
111 if (b->hotkey == tolower (parm)) {
112 button_callback (w, WIDGET_KEY, ' ');
113 return MSG_HANDLED;
116 return MSG_NOT_HANDLED;
118 case WIDGET_KEY:
119 if (parm != ' ' && parm != '\n')
120 return MSG_NOT_HANDLED;
122 if (b->callback)
123 stop = (*b->callback) (b->action);
124 if (!b->callback || stop) {
125 h->ret_value = b->action;
126 dlg_stop (h);
128 return MSG_HANDLED;
130 case WIDGET_CURSOR:
131 switch (b->flags) {
132 case DEFPUSH_BUTTON:
133 off = 3;
134 break;
135 case NORMAL_BUTTON:
136 off = 2;
137 break;
138 case NARROW_BUTTON:
139 off = 1;
140 break;
141 case HIDDEN_BUTTON:
142 default:
143 off = 0;
144 break;
146 widget_move (&b->widget, 0, b->hotpos + off);
147 return MSG_HANDLED;
149 case WIDGET_UNFOCUS:
150 case WIDGET_FOCUS:
151 case WIDGET_DRAW:
152 if (msg == WIDGET_UNFOCUS)
153 b->selected = 0;
154 else if (msg == WIDGET_FOCUS)
155 b->selected = 1;
157 switch (b->flags) {
158 case DEFPUSH_BUTTON:
159 g_snprintf (buf, sizeof (buf), "[< %s >]", b->text);
160 off = 3;
161 break;
162 case NORMAL_BUTTON:
163 g_snprintf (buf, sizeof (buf), "[ %s ]", b->text);
164 off = 2;
165 break;
166 case NARROW_BUTTON:
167 g_snprintf (buf, sizeof (buf), "[%s]", b->text);
168 off = 1;
169 break;
170 case HIDDEN_BUTTON:
171 default:
172 buf[0] = '\0';
173 off = 0;
174 break;
177 widget_selectcolor (w, b->selected, FALSE);
178 widget_move (w, 0, 0);
180 addstr (buf);
182 if (b->hotpos >= 0) {
183 widget_selectcolor (w, b->selected, TRUE);
184 widget_move (w, 0, b->hotpos + off);
185 addch ((unsigned char) b->text[b->hotpos]);
187 return MSG_HANDLED;
189 case WIDGET_DESTROY:
190 g_free (b->text);
191 return MSG_HANDLED;
193 default:
194 return default_proc (msg, parm);
198 static int
199 button_event (Gpm_Event *event, void *data)
201 WButton *b = data;
203 if (event->type & (GPM_DOWN|GPM_UP)){
204 Dlg_head *h=b->widget.parent;
205 dlg_select_widget (b);
206 if (event->type & GPM_UP){
207 button_callback ((Widget *) data, WIDGET_KEY, ' ');
208 (*h->callback) (h, DLG_POST_KEY, ' ');
209 return MOU_NORMAL;
212 return MOU_NORMAL;
215 static int
216 button_len (const char *text, unsigned int flags)
218 int ret = strlen (text);
219 switch (flags){
220 case DEFPUSH_BUTTON:
221 ret += 6;
222 break;
223 case NORMAL_BUTTON:
224 ret += 4;
225 break;
226 case NARROW_BUTTON:
227 ret += 2;
228 break;
229 case HIDDEN_BUTTON:
230 default:
231 return 0;
233 return ret;
237 * Locate the hotkey and remove it from the button text. Assuming that
238 * the button text is g_malloc()ed, we can safely change and shorten it.
240 static void
241 button_scan_hotkey (WButton *b)
243 char *cp = strchr (b->text, '&');
245 if (cp != NULL && cp[1] != '\0') {
246 g_strlcpy (cp, cp + 1, strlen (cp));
247 b->hotkey = tolower ((unsigned char) *cp);
248 b->hotpos = cp - b->text;
252 WButton *
253 button_new (int y, int x, int action, int flags, const char *text,
254 bcback callback)
256 WButton *b = g_new (WButton, 1);
258 init_widget (&b->widget, y, x, 1, button_len (text, flags),
259 button_callback, button_event);
261 b->action = action;
262 b->flags = flags;
263 b->selected = 0;
264 b->text = g_strdup (text);
265 b->callback = callback;
266 widget_want_hotkey (b->widget, 1);
267 b->hotkey = 0;
268 b->hotpos = -1;
270 button_scan_hotkey(b);
271 return b;
274 const char *
275 button_get_text (WButton *b)
277 return b->text;
280 void
281 button_set_text (WButton *b, const char *text)
283 g_free (b->text);
284 b->text = g_strdup (text);
285 b->widget.cols = button_len (text, b->flags);
286 button_scan_hotkey(b);
287 dlg_redraw (b->widget.parent);
291 /* Radio button widget */
292 static int radio_event (Gpm_Event *event, void *);
294 static cb_ret_t
295 radio_callback (Widget *w, widget_msg_t msg, int parm)
297 WRadio *r = (WRadio *) w;
298 int i;
299 Dlg_head *h = r->widget.parent;
301 switch (msg) {
302 case WIDGET_HOTKEY:
304 int i, lp = tolower (parm);
305 const char *cp;
307 for (i = 0; i < r->count; i++) {
308 cp = strchr (r->texts[i], '&');
309 if (cp != NULL && cp[1] != '\0') {
310 int c = tolower ((unsigned char) cp[1]);
312 if (c != lp)
313 continue;
314 r->pos = i;
316 /* Take action */
317 radio_callback (w, WIDGET_KEY, ' ');
318 return MSG_HANDLED;
322 return MSG_NOT_HANDLED;
324 case WIDGET_KEY:
325 switch (parm) {
326 case ' ':
327 r->sel = r->pos;
328 (*h->callback) (h, DLG_ACTION, 0);
329 radio_callback (w, WIDGET_FOCUS, ' ');
330 return MSG_HANDLED;
332 case KEY_UP:
333 case KEY_LEFT:
334 if (r->pos > 0) {
335 r->pos--;
336 return MSG_HANDLED;
338 return MSG_NOT_HANDLED;
340 case KEY_DOWN:
341 case KEY_RIGHT:
342 if (r->count - 1 > r->pos) {
343 r->pos++;
344 return MSG_HANDLED;
347 return MSG_NOT_HANDLED;
349 case WIDGET_CURSOR:
350 (*h->callback) (h, DLG_ACTION, 0);
351 radio_callback (w, WIDGET_FOCUS, ' ');
352 widget_move (&r->widget, r->pos, 1);
353 return MSG_HANDLED;
355 case WIDGET_UNFOCUS:
356 case WIDGET_FOCUS:
357 case WIDGET_DRAW:
358 for (i = 0; i < r->count; i++) {
359 register const char *cp;
360 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
361 widget_selectcolor (w, focused, FALSE);
362 widget_move (&r->widget, i, 0);
364 tty_printf ("(%c) ", (r->sel == i) ? '*' : ' ');
365 for (cp = r->texts[i]; *cp; cp++) {
366 if (*cp == '&') {
367 widget_selectcolor (w, focused, TRUE);
368 addch (*++cp);
369 widget_selectcolor (w, focused, FALSE);
370 } else
371 addch (*cp);
374 return MSG_HANDLED;
376 default:
377 return default_proc (msg, parm);
381 static int
382 radio_event (Gpm_Event *event, void *data)
384 WRadio *r = data;
385 Widget *w = data;
387 if (event->type & (GPM_DOWN|GPM_UP)){
388 Dlg_head *h = r->widget.parent;
390 r->pos = event->y - 1;
391 dlg_select_widget (r);
392 if (event->type & GPM_UP){
393 radio_callback (w, WIDGET_KEY, ' ');
394 radio_callback (w, WIDGET_FOCUS, 0);
395 (*h->callback) (h, DLG_POST_KEY, ' ');
396 return MOU_NORMAL;
399 return MOU_NORMAL;
402 WRadio *
403 radio_new (int y, int x, int count, const char **texts)
405 WRadio *r = g_new (WRadio, 1);
406 int i, max, m;
408 /* Compute the longest string */
409 max = 0;
410 for (i = 0; i < count; i++){
411 m = strlen (texts [i]);
412 if (m > max)
413 max = m;
416 init_widget (&r->widget, y, x, count, max, radio_callback, radio_event);
417 r->state = 1;
418 r->pos = 0;
419 r->sel = 0;
420 r->count = count;
421 r->texts = texts;
422 widget_want_hotkey (r->widget, 1);
424 return r;
428 /* Checkbutton widget */
430 static int check_event (Gpm_Event *event, void *);
432 static cb_ret_t
433 check_callback (Widget *w, widget_msg_t msg, int parm)
435 WCheck *c = (WCheck *) w;
436 Dlg_head *h = c->widget.parent;
438 switch (msg) {
439 case WIDGET_HOTKEY:
440 if (c->hotkey == parm
441 || (c->hotkey >= 'a' && c->hotkey <= 'z'
442 && c->hotkey - 32 == parm)) {
443 check_callback (w, WIDGET_KEY, ' '); /* make action */
444 return MSG_HANDLED;
446 return MSG_NOT_HANDLED;
448 case WIDGET_KEY:
449 if (parm != ' ')
450 return MSG_NOT_HANDLED;
451 c->state ^= C_BOOL;
452 c->state ^= C_CHANGE;
453 (*h->callback) (h, DLG_ACTION, 0);
454 check_callback (w, WIDGET_FOCUS, ' ');
455 return MSG_HANDLED;
457 case WIDGET_CURSOR:
458 widget_move (&c->widget, 0, 1);
459 return MSG_HANDLED;
461 case WIDGET_FOCUS:
462 case WIDGET_UNFOCUS:
463 case WIDGET_DRAW:
464 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
465 widget_move (&c->widget, 0, 0);
466 tty_printf ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
468 if (c->hotpos >= 0) {
469 widget_selectcolor (w, msg == WIDGET_FOCUS, TRUE);
470 widget_move (&c->widget, 0, +c->hotpos + 4);
471 addch ((unsigned char) c->text[c->hotpos]);
473 return MSG_HANDLED;
475 case WIDGET_DESTROY:
476 g_free (c->text);
477 return MSG_HANDLED;
479 default:
480 return default_proc (msg, parm);
484 static int
485 check_event (Gpm_Event *event, void *data)
487 WCheck *c = data;
488 Widget *w = data;
490 if (event->type & (GPM_DOWN|GPM_UP)){
491 Dlg_head *h = c->widget.parent;
493 dlg_select_widget (c);
494 if (event->type & GPM_UP){
495 check_callback (w, WIDGET_KEY, ' ');
496 check_callback (w, WIDGET_FOCUS, 0);
497 (*h->callback) (h, DLG_POST_KEY, ' ');
498 return MOU_NORMAL;
501 return MOU_NORMAL;
504 WCheck *
505 check_new (int y, int x, int state, const char *text)
507 WCheck *c = g_new (WCheck, 1);
508 const char *s;
509 char *t;
511 init_widget (&c->widget, y, x, 1, strlen (text),
512 check_callback, check_event);
513 c->state = state ? C_BOOL : 0;
514 c->text = g_strdup (text);
515 c->hotkey = 0;
516 c->hotpos = -1;
517 widget_want_hotkey (c->widget, 1);
519 /* Scan for the hotkey */
520 for (s = text, t = c->text; *s; s++, t++){
521 if (*s != '&'){
522 *t = *s;
523 continue;
525 s++;
526 if (*s){
527 c->hotkey = tolower ((unsigned char) *s);
528 c->hotpos = t - c->text;
530 *t = *s;
532 *t = 0;
533 return c;
537 /* Label widget */
539 static cb_ret_t
540 label_callback (Widget *w, widget_msg_t msg, int parm)
542 WLabel *l = (WLabel *) w;
543 Dlg_head *h = l->widget.parent;
545 switch (msg) {
546 case WIDGET_INIT:
547 return MSG_HANDLED;
549 /* We don't want to get the focus */
550 case WIDGET_FOCUS:
551 return MSG_NOT_HANDLED;
553 case WIDGET_DRAW:
555 char *p = l->text, *q, c = 0;
556 int y = 0;
558 if (!l->text)
559 return MSG_HANDLED;
561 if (l->transparent)
562 attrset (DEFAULT_COLOR);
563 else
564 attrset (DLG_NORMALC (h));
565 for (;;) {
566 int xlen;
568 q = strchr (p, '\n');
569 if (q) {
570 c = *q;
571 *q = 0;
573 widget_move (&l->widget, y, 0);
574 tty_printf ("%s", p);
575 xlen = l->widget.cols - strlen (p);
576 if (xlen > 0)
577 tty_printf ("%*s", xlen, " ");
578 if (!q)
579 break;
580 *q = c;
581 p = q + 1;
582 y++;
584 return MSG_HANDLED;
587 case WIDGET_DESTROY:
588 g_free (l->text);
589 return MSG_HANDLED;
591 default:
592 return default_proc (msg, parm);
596 void
597 label_set_text (WLabel *label, const char *text)
599 int newcols = label->widget.cols;
601 if (label->text && text && !strcmp (label->text, text))
602 return; /* Flickering is not nice */
604 g_free (label->text);
606 if (text){
607 label->text = g_strdup (text);
608 if (label->auto_adjust_cols) {
609 newcols = strlen (text);
610 if (newcols > label->widget.cols)
611 label->widget.cols = newcols;
613 } else
614 label->text = 0;
616 if (label->widget.parent)
617 label_callback ((Widget *) label, WIDGET_DRAW, 0);
619 if (newcols < label->widget.cols)
620 label->widget.cols = newcols;
623 WLabel *
624 label_new (int y, int x, const char *text)
626 WLabel *l;
627 int width;
629 /* Multiline labels are immutable - no need to compute their sizes */
630 if (!text || strchr(text, '\n'))
631 width = 1;
632 else
633 width = strlen (text);
635 l = g_new (WLabel, 1);
636 init_widget (&l->widget, y, x, 1, width, label_callback, NULL);
637 l->text = text ? g_strdup (text) : 0;
638 l->auto_adjust_cols = 1;
639 l->transparent = 0;
640 widget_want_cursor (l->widget, 0);
641 return l;
645 /* Gauge widget (progress indicator) */
646 /* Currently width is hardcoded here for text mode */
647 #define gauge_len 47
649 static cb_ret_t
650 gauge_callback (Widget *w, widget_msg_t msg, int parm)
652 WGauge *g = (WGauge *) w;
653 Dlg_head *h = g->widget.parent;
655 if (msg == WIDGET_INIT)
656 return MSG_HANDLED;
658 /* We don't want to get the focus */
659 if (msg == WIDGET_FOCUS)
660 return MSG_NOT_HANDLED;
662 if (msg == WIDGET_DRAW){
663 widget_move (&g->widget, 0, 0);
664 attrset (DLG_NORMALC (h));
665 if (!g->shown)
666 tty_printf ("%*s", gauge_len, "");
667 else {
668 int percentage, columns;
669 long total = g->max, done = g->current;
671 if (total <= 0 || done < 0) {
672 done = 0;
673 total = 100;
675 if (done > total)
676 done = total;
677 while (total > 65535) {
678 total /= 256;
679 done /= 256;
681 percentage = (200 * done / total + 1) / 2;
682 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
683 addch ('[');
684 attrset (GAUGE_COLOR);
685 tty_printf ("%*s", (int) columns, "");
686 attrset (DLG_NORMALC (h));
687 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
689 return MSG_HANDLED;
691 return default_proc (msg, parm);
694 void
695 gauge_set_value (WGauge *g, int max, int current)
697 if (g->current == current && g->max == max)
698 return; /* Do not flicker */
699 if (max == 0)
700 max = 1; /* I do not like division by zero :) */
702 g->current = current;
703 g->max = max;
704 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
707 void
708 gauge_show (WGauge *g, int shown)
710 if (g->shown == shown)
711 return;
712 g->shown = shown;
713 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
716 WGauge *
717 gauge_new (int y, int x, int shown, int max, int current)
719 WGauge *g = g_new (WGauge, 1);
721 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
722 g->shown = shown;
723 if (max == 0)
724 max = 1; /* I do not like division by zero :) */
725 g->max = max;
726 g->current = current;
727 widget_want_cursor (g->widget, 0);
728 return g;
732 /* Input widget */
734 /* {{{ history button */
736 #define LARGE_HISTORY_BUTTON 1
738 #ifdef LARGE_HISTORY_BUTTON
739 # define HISTORY_BUTTON_WIDTH 3
740 #else
741 # define HISTORY_BUTTON_WIDTH 1
742 #endif
744 #define should_show_history_button(in) \
745 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
747 static void draw_history_button (WInput * in)
749 char c;
750 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
751 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
752 #ifdef LARGE_HISTORY_BUTTON
754 Dlg_head *h;
755 h = in->widget.parent;
756 #if 0
757 attrset (NORMALC); /* button has the same color as other buttons */
758 addstr ("[ ]");
759 attrset (HOT_NORMALC);
760 #else
761 attrset (NORMAL_COLOR);
762 addstr ("[ ]");
763 /* Too distracting: attrset (MARKED_COLOR); */
764 #endif
765 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
766 addch (c);
768 #else
769 attrset (MARKED_COLOR);
770 addch (c);
771 #endif
774 /* }}} history button */
777 /* Input widgets now have a global kill ring */
778 /* Pointer to killed data */
779 static char *kill_buffer = 0;
781 void
782 update_input (WInput *in, int clear_first)
784 int has_history = 0;
785 int i, j;
786 unsigned char c;
787 int buf_len = strlen (in->buffer);
789 if (should_show_history_button (in))
790 has_history = HISTORY_BUTTON_WIDTH;
792 if (in->disable_update)
793 return;
795 /* Make the point visible */
796 if ((in->point < in->first_shown) ||
797 (in->point >= in->first_shown+in->field_len - has_history)){
798 in->first_shown = in->point - (in->field_len / 3);
799 if (in->first_shown < 0)
800 in->first_shown = 0;
803 /* Adjust the mark */
804 if (in->mark > buf_len)
805 in->mark = buf_len;
807 if (has_history)
808 draw_history_button (in);
810 attrset (in->color);
812 widget_move (&in->widget, 0, 0);
813 for (i = 0; i < in->field_len - has_history; i++)
814 addch (' ');
815 widget_move (&in->widget, 0, 0);
817 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
818 c = in->buffer [j++];
819 c = is_printable (c) ? c : '.';
820 if (in->is_password)
821 c = '*';
822 addch (c);
824 widget_move (&in->widget, 0, in->point - in->first_shown);
826 if (clear_first)
827 in->first = 0;
830 void
831 winput_set_origin (WInput *in, int x, int field_len)
833 in->widget.x = x;
834 in->field_len = in->widget.cols = field_len;
835 update_input (in, 0);
838 /* {{{ history saving and loading */
840 int num_history_items_recorded = 60;
843 This loads and saves the history of an input line to and from the
844 widget. It is called with the widgets history name on creation of the
845 widget, and returns the GList list. It stores histories in the file
846 ~/.mc/history in using the profile code.
848 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
849 function) then input_new assigns the default text to be the last text
850 entered, or "" if not found.
853 GList *
854 history_get (const char *input_name)
856 int i;
857 GList *hist;
858 char *profile;
860 hist = NULL;
862 if (!num_history_items_recorded) /* this is how to disable */
863 return NULL;
864 if (!input_name)
865 return NULL;
866 if (!*input_name)
867 return NULL;
868 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
869 for (i = 0;; i++) {
870 char key_name[BUF_TINY];
871 char this_entry[BUF_LARGE];
872 g_snprintf (key_name, sizeof (key_name), "%d", i);
873 GetPrivateProfileString (input_name, key_name, "", this_entry,
874 sizeof (this_entry), profile);
875 if (!*this_entry)
876 break;
878 hist = list_append_unique (hist, g_strdup (this_entry));
880 g_free (profile);
882 /* return pointer to the last entry in the list */
883 hist = g_list_last (hist);
885 return hist;
888 void
889 history_put (const char *input_name, GList *h)
891 int i;
892 char *profile;
894 if (!input_name)
895 return;
897 if (!*input_name)
898 return;
900 if (!h)
901 return;
903 if (!num_history_items_recorded) /* this is how to disable */
904 return;
906 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
908 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
909 close (i);
911 /* Make sure the history is only readable by the user */
912 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
913 g_free (profile);
914 return;
917 /* go to end of list */
918 h = g_list_last (h);
920 /* go back 60 places */
921 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
922 h = g_list_previous (h);
924 if (input_name)
925 profile_clean_section (input_name, profile);
927 /* dump histories into profile */
928 for (i = 0; h; h = g_list_next (h)) {
929 char *text;
931 text = (char *) h->data;
933 /* We shouldn't have null entries, but let's be sure */
934 if (text && *text) {
935 char key_name[BUF_TINY];
936 g_snprintf (key_name, sizeof (key_name), "%d", i++);
937 WritePrivateProfileString (input_name, key_name, text,
938 profile);
942 g_free (profile);
945 /* }}} history saving and loading */
948 /* {{{ history display */
950 static const char *
951 i18n_htitle (void)
953 static const char *history_title = NULL;
955 if (history_title == NULL)
956 history_title = _(" History ");
957 return history_title;
960 static WLEntry *listbox_select_pos (WListbox *l, WLEntry *base, int
961 pos);
963 static inline cb_ret_t
964 listbox_fwd (WListbox *l)
966 if (l->current != l->list->prev){
967 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
968 return MSG_HANDLED;
970 return MSG_NOT_HANDLED;
973 char *
974 show_hist (GList *history, int widget_x, int widget_y)
976 GList *hi, *z;
977 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
978 int x, y, w, h;
979 char *q, *r = 0;
980 Dlg_head *query_dlg;
981 WListbox *query_list;
983 z = history;
984 if (!z)
985 return NULL;
987 z = g_list_first (history);
988 hi = z;
989 while (hi) {
990 if ((i = strlen ((char *) hi->data)) > maxlen)
991 maxlen = i;
992 count++;
993 hi = g_list_next (hi);
996 y = widget_y;
997 h = count + 2;
998 if (h <= y || y > LINES - 6) {
999 h = min (h, y - 1);
1000 y -= h;
1001 } else {
1002 y++;
1003 h = min (h, LINES - y);
1006 if (widget_x > 2)
1007 x = widget_x - 2;
1008 else
1009 x = 0;
1010 if ((w = maxlen + 4) + x > COLS) {
1011 w = min (w, COLS);
1012 x = COLS - w;
1015 query_dlg =
1016 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
1017 i18n_htitle (), DLG_COMPACT);
1018 query_list = listbox_new (1, 1, w - 2, h - 2, 0);
1019 add_widget (query_dlg, query_list);
1020 hi = z;
1021 if (y < widget_y) {
1022 /* traverse */
1023 while (hi) {
1024 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1025 hi = g_list_next (hi);
1027 while (listbox_fwd (query_list));
1028 } else {
1029 /* traverse backwards */
1030 hi = g_list_last (history);
1031 while (hi) {
1032 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1033 hi = g_list_previous (hi);
1036 run_dlg (query_dlg);
1037 q = NULL;
1038 if (query_dlg->ret_value != B_CANCEL) {
1039 listbox_get_current (query_list, &q, NULL);
1040 if (q)
1041 r = g_strdup (q);
1043 destroy_dlg (query_dlg);
1044 return r;
1047 static void do_show_hist (WInput * in)
1049 char *r;
1050 r = show_hist (in->history, in->widget.x, in->widget.y);
1051 if (r) {
1052 assign_text (in, r);
1053 g_free (r);
1057 /* }}} history display */
1059 static void
1060 input_destroy (WInput *in)
1062 if (!in){
1063 fprintf (stderr, "Internal error: null Input *\n");
1064 exit (1);
1067 new_input (in);
1069 if (in->history){
1070 if (!in->is_password) /* don't save passwords ;-) */
1071 history_put (in->history_name, in->history);
1073 in->history = g_list_first (in->history);
1074 g_list_foreach (in->history, (GFunc) g_free, NULL);
1075 g_list_free (in->history);
1078 g_free (in->buffer);
1079 free_completions (in);
1080 g_free (in->history_name);
1083 void
1084 input_disable_update (WInput *in)
1086 in->disable_update++;
1089 void
1090 input_enable_update (WInput *in)
1092 in->disable_update--;
1093 update_input (in, 0);
1096 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1099 push_history (WInput *in, const char *text)
1101 static int i18n;
1102 /* input widget where urls with passwords are entered without any
1103 vfs prefix */
1104 static const char *password_input_fields[] = {
1105 N_(" Link to a remote machine "),
1106 N_(" FTP to machine "),
1107 N_(" SMB link to machine ")
1109 char *t;
1110 const char *p;
1111 size_t i;
1113 if (!i18n) {
1114 i18n = 1;
1115 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1116 password_input_fields[i] = _(password_input_fields[i]);
1119 for (p = text; *p == ' ' || *p == '\t'; p++);
1120 if (!*p)
1121 return 0;
1123 if (in->history) {
1124 /* Avoid duplicated entries */
1125 in->history = g_list_last (in->history);
1126 if (!strcmp ((char *) in->history->data, text))
1127 return 1;
1130 t = g_strdup (text);
1132 if (in->history_name) {
1133 p = in->history_name + 3;
1134 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1135 if (strcmp (p, password_input_fields[i]) == 0)
1136 break;
1137 if (i < ELEMENTS (password_input_fields))
1138 strip_password (t, 0);
1139 else
1140 strip_password (t, 1);
1143 in->history = list_append_unique (in->history, t);
1144 in->need_push = 0;
1146 return 2;
1149 #undef ELEMENTS
1151 /* Cleans the input line and adds the current text to the history */
1152 void
1153 new_input (WInput *in)
1155 if (in->buffer)
1156 push_history (in, in->buffer);
1157 in->need_push = 1;
1158 in->buffer [0] = 0;
1159 in->point = 0;
1160 in->mark = 0;
1161 free_completions (in);
1162 update_input (in, 0);
1165 static cb_ret_t
1166 insert_char (WInput *in, int c_code)
1168 size_t i;
1170 if (c_code == -1)
1171 return MSG_NOT_HANDLED;
1173 in->need_push = 1;
1174 if (strlen (in->buffer)+1 == (size_t) in->current_max_len){
1175 /* Expand the buffer */
1176 char *narea = g_realloc (in->buffer, in->current_max_len + in->field_len);
1177 if (narea){
1178 in->buffer = narea;
1179 in->current_max_len += in->field_len;
1182 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
1183 size_t l = strlen (&in->buffer [in->point]);
1184 for (i = l+1; i > 0; i--)
1185 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1186 in->buffer [in->point] = c_code;
1187 in->point++;
1189 return MSG_HANDLED;
1192 static void
1193 beginning_of_line (WInput *in)
1195 in->point = 0;
1198 static void
1199 end_of_line (WInput *in)
1201 in->point = strlen (in->buffer);
1204 static void
1205 backward_char (WInput *in)
1207 if (in->point)
1208 in->point--;
1211 static void
1212 forward_char (WInput *in)
1214 if (in->buffer [in->point])
1215 in->point++;
1218 static void
1219 forward_word (WInput * in)
1221 char *p = in->buffer + in->point;
1223 while (*p
1224 && (isspace ((unsigned char) *p)
1225 || ispunct ((unsigned char) *p)))
1226 p++;
1227 while (*p && isalnum ((unsigned char) *p))
1228 p++;
1229 in->point = p - in->buffer;
1232 static void
1233 backward_word (WInput *in)
1235 char *p = in->buffer + in->point;
1237 while (p - 1 > in->buffer - 1 && (isspace ((unsigned char) *(p - 1))
1238 || ispunct ((unsigned char)
1239 *(p - 1))))
1240 p--;
1241 while (p - 1 > in->buffer - 1 && isalnum ((unsigned char) *(p - 1)))
1242 p--;
1243 in->point = p - in->buffer;
1246 static void
1247 key_left (WInput *in)
1249 backward_char (in);
1252 static void
1253 key_ctrl_left (WInput *in)
1255 backward_word (in);
1258 static void
1259 key_right (WInput *in)
1261 forward_char (in);
1264 static void
1265 key_ctrl_right (WInput *in)
1267 forward_word (in);
1269 static void
1270 backward_delete (WInput *in)
1272 int i;
1274 if (!in->point)
1275 return;
1276 for (i = in->point; in->buffer [i-1]; i++)
1277 in->buffer [i-1] = in->buffer [i];
1278 in->need_push = 1;
1279 in->point--;
1282 static void
1283 delete_char (WInput *in)
1285 int i;
1287 for (i = in->point; in->buffer [i]; i++)
1288 in->buffer [i] = in->buffer [i+1];
1289 in->need_push = 1;
1292 static void
1293 copy_region (WInput *in, int x_first, int x_last)
1295 int first = min (x_first, x_last);
1296 int last = max (x_first, x_last);
1298 if (last == first)
1299 return;
1301 g_free (kill_buffer);
1303 kill_buffer = g_strndup(in->buffer+first,last-first);
1306 static void
1307 delete_region (WInput *in, int x_first, int x_last)
1309 int first = min (x_first, x_last);
1310 int last = max (x_first, x_last);
1311 size_t len = strlen (&in->buffer [last]) + 1;
1313 in->point = first;
1314 in->mark = first;
1315 memmove (&in->buffer [first], &in->buffer [last], len);
1316 in->need_push = 1;
1319 static void
1320 kill_word (WInput *in)
1322 int old_point = in->point;
1323 int new_point;
1325 forward_word (in);
1326 new_point = in->point;
1327 in->point = old_point;
1329 copy_region (in, old_point, new_point);
1330 delete_region (in, old_point, new_point);
1331 in->need_push = 1;
1334 static void
1335 back_kill_word (WInput *in)
1337 int old_point = in->point;
1338 int new_point;
1340 backward_word (in);
1341 new_point = in->point;
1342 in->point = old_point;
1344 copy_region (in, old_point, new_point);
1345 delete_region (in, old_point, new_point);
1346 in->need_push = 1;
1349 static void
1350 set_mark (WInput *in)
1352 in->mark = in->point;
1355 static void
1356 kill_save (WInput *in)
1358 copy_region (in, in->mark, in->point);
1361 static void
1362 kill_region (WInput *in)
1364 kill_save (in);
1365 delete_region (in, in->point, in->mark);
1368 static void
1369 yank (WInput *in)
1371 char *p;
1373 if (!kill_buffer)
1374 return;
1375 for (p = kill_buffer; *p; p++)
1376 insert_char (in, *p);
1379 static void
1380 kill_line (WInput *in)
1382 g_free (kill_buffer);
1383 kill_buffer = g_strdup (&in->buffer [in->point]);
1384 in->buffer [in->point] = 0;
1387 void
1388 assign_text (WInput *in, const char *text)
1390 free_completions (in);
1391 g_free (in->buffer);
1392 in->buffer = g_strdup (text); /* was in->buffer->text */
1393 in->current_max_len = strlen (in->buffer) + 1;
1394 in->point = strlen (in->buffer);
1395 in->mark = 0;
1396 in->need_push = 1;
1399 static void
1400 hist_prev (WInput *in)
1402 if (!in->history)
1403 return;
1405 if (in->need_push) {
1406 switch (push_history (in, in->buffer)) {
1407 case 2:
1408 in->history = g_list_previous (in->history);
1409 break;
1410 case 1:
1411 if (in->history->prev)
1412 in->history = g_list_previous (in->history);
1413 break;
1414 case 0:
1415 break;
1417 } else if (in->history->prev)
1418 in->history = g_list_previous (in->history);
1419 else
1420 return;
1421 assign_text (in, (char *) in->history->data);
1422 in->need_push = 0;
1425 static void
1426 hist_next (WInput *in)
1428 if (in->need_push) {
1429 switch (push_history (in, in->buffer)) {
1430 case 2:
1431 assign_text (in, "");
1432 return;
1433 case 0:
1434 return;
1438 if (!in->history)
1439 return;
1441 if (!in->history->next) {
1442 assign_text (in, "");
1443 return;
1446 in->history = g_list_next (in->history);
1447 assign_text (in, (char *) in->history->data);
1448 in->need_push = 0;
1451 static const struct {
1452 int key_code;
1453 void (*fn)(WInput *in);
1454 } input_map [] = {
1455 /* Motion */
1456 { XCTRL('a'), beginning_of_line },
1457 { KEY_HOME, beginning_of_line },
1458 { KEY_A1, beginning_of_line },
1459 { ALT ('<'), beginning_of_line },
1460 { XCTRL('e'), end_of_line },
1461 { KEY_END, end_of_line },
1462 { KEY_C1, end_of_line },
1463 { ALT ('>'), end_of_line },
1464 { KEY_LEFT, key_left },
1465 { KEY_LEFT | KEY_M_CTRL, key_ctrl_left },
1466 { XCTRL('b'), backward_char },
1467 { ALT('b'), backward_word },
1468 { KEY_RIGHT, key_right },
1469 { KEY_RIGHT | KEY_M_CTRL, key_ctrl_right },
1470 { XCTRL('f'), forward_char },
1471 { ALT('f'), forward_word },
1473 /* Editing */
1474 { KEY_BACKSPACE, backward_delete },
1475 { KEY_DC, delete_char },
1476 { ALT('d'), kill_word },
1477 { ALT(KEY_BACKSPACE), back_kill_word },
1479 /* Region manipulation */
1480 { 0, set_mark },
1481 { XCTRL('w'), kill_region },
1482 { ALT('w'), kill_save },
1483 { XCTRL('y'), yank },
1484 { XCTRL('k'), kill_line },
1486 /* History */
1487 { ALT('p'), hist_prev },
1488 { ALT('n'), hist_next },
1489 { ALT('h'), do_show_hist },
1491 /* Completion */
1492 { ALT('\t'), complete },
1494 { 0, 0 }
1497 /* This function is a test for a special input key used in complete.c */
1498 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1499 and 2 if it is a complete key */
1501 is_in_input_map (WInput *in, int c_code)
1503 int i;
1505 (void) in;
1507 for (i = 0; input_map [i].fn; i++)
1508 if (c_code == input_map [i].key_code) {
1509 if (input_map [i].fn == complete)
1510 return 2;
1511 else
1512 return 1;
1514 return 0;
1517 static void
1518 port_region_marked_for_delete (WInput *in)
1520 *in->buffer = 0;
1521 in->point = 0;
1522 in->first = 0;
1525 cb_ret_t
1526 handle_char (WInput *in, int c_code)
1528 cb_ret_t v;
1529 int i;
1531 v = MSG_NOT_HANDLED;
1533 if (quote){
1534 free_completions (in);
1535 v = insert_char (in, c_code);
1536 update_input (in, 1);
1537 quote = 0;
1538 return v;
1541 for (i = 0; input_map [i].fn; i++){
1542 if (c_code == input_map [i].key_code){
1543 if (input_map [i].fn != complete)
1544 free_completions (in);
1545 (*input_map [i].fn)(in);
1546 v = MSG_HANDLED;
1547 break;
1550 if (!input_map [i].fn){
1551 if (c_code > 255 || !is_printable (c_code))
1552 return MSG_NOT_HANDLED;
1553 if (in->first){
1554 port_region_marked_for_delete (in);
1556 free_completions (in);
1557 v = insert_char (in, c_code);
1559 update_input (in, 1);
1560 return v;
1563 /* Inserts text in input line */
1564 void
1565 stuff (WInput *in, const char *text, int insert_extra_space)
1567 input_disable_update (in);
1568 while (*text)
1569 handle_char (in, *text++);
1570 if (insert_extra_space)
1571 handle_char (in, ' ');
1572 input_enable_update (in);
1573 update_input (in, 1);
1576 void
1577 input_set_point (WInput *in, int pos)
1579 if (pos > in->current_max_len)
1580 pos = in->current_max_len;
1581 if (pos != in->point)
1582 free_completions (in);
1583 in->point = pos;
1584 update_input (in, 1);
1587 cb_ret_t
1588 input_callback (Widget *w, widget_msg_t msg, int parm)
1590 WInput *in = (WInput *) w;
1591 cb_ret_t v;
1593 switch (msg) {
1594 case WIDGET_KEY:
1595 if (parm == XCTRL ('q')) {
1596 quote = 1;
1597 v = handle_char (in, ascii_alpha_to_cntrl (mi_getch ()));
1598 quote = 0;
1599 return v;
1602 /* Keys we want others to handle */
1603 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1604 || parm == KEY_F (10) || parm == XCTRL ('g') || parm == '\n')
1605 return MSG_NOT_HANDLED;
1607 /* When pasting multiline text, insert literal Enter */
1608 if ((parm & ~KEY_M_MASK) == '\n') {
1609 quote = 1;
1610 v = handle_char (in, '\n');
1611 quote = 0;
1612 return v;
1615 return handle_char (in, parm);
1617 case WIDGET_FOCUS:
1618 case WIDGET_UNFOCUS:
1619 case WIDGET_DRAW:
1620 update_input (in, 0);
1621 return MSG_HANDLED;
1623 case WIDGET_CURSOR:
1624 widget_move (&in->widget, 0, in->point - in->first_shown);
1625 return MSG_HANDLED;
1627 case WIDGET_DESTROY:
1628 input_destroy (in);
1629 return MSG_HANDLED;
1631 default:
1632 return default_proc (msg, parm);
1636 static int
1637 input_event (Gpm_Event * event, void *data)
1639 WInput *in = data;
1641 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1642 dlg_select_widget (in);
1644 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1645 && should_show_history_button (in)) {
1646 do_show_hist (in);
1647 } else {
1648 in->point = strlen (in->buffer);
1649 if (event->x - in->first_shown - 1 < in->point)
1650 in->point = event->x - in->first_shown - 1;
1651 if (in->point < 0)
1652 in->point = 0;
1654 update_input (in, 1);
1656 return MOU_NORMAL;
1659 WInput *
1660 input_new (int y, int x, int color, int len, const char *def_text,
1661 const char *histname, int completion_flags)
1663 WInput *in = g_new (WInput, 1);
1664 int initial_buffer_len;
1666 init_widget (&in->widget, y, x, 1, len, input_callback, input_event);
1668 /* history setup */
1669 in->history = NULL;
1670 in->history_name = 0;
1671 if (histname) {
1672 if (*histname) {
1673 in->history_name = g_strdup (histname);
1674 in->history = history_get (histname);
1678 if (!def_text)
1679 def_text = "";
1681 if (def_text == INPUT_LAST_TEXT) {
1682 def_text = "";
1683 if (in->history)
1684 if (in->history->data)
1685 def_text = (char *) in->history->data;
1687 initial_buffer_len = 1 + max ((size_t) len, strlen (def_text));
1688 in->widget.options |= W_IS_INPUT;
1689 in->completions = NULL;
1690 in->completion_flags = completion_flags;
1691 in->current_max_len = initial_buffer_len;
1692 in->buffer = g_malloc (initial_buffer_len);
1693 in->color = color;
1694 in->field_len = len;
1695 in->first = 1;
1696 in->first_shown = 0;
1697 in->disable_update = 0;
1698 in->mark = 0;
1699 in->need_push = 1;
1700 in->is_password = 0;
1702 strcpy (in->buffer, def_text);
1703 in->point = strlen (in->buffer);
1704 return in;
1708 /* Listbox widget */
1710 /* Should draw the scrollbar, but currently draws only
1711 * indications that there is more information
1713 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1715 static void
1716 listbox_drawscroll (WListbox *l)
1718 int line;
1719 int i, top;
1720 int max_line = l->height-1;
1722 /* Are we at the top? */
1723 widget_move (&l->widget, 0, l->width);
1724 if (l->list == l->top)
1725 one_vline ();
1726 else
1727 addch ('^');
1729 /* Are we at the bottom? */
1730 widget_move (&l->widget, max_line, l->width);
1731 top = listbox_cdiff (l->list, l->top);
1732 if ((top + l->height == l->count) || l->height >= l->count)
1733 one_vline ();
1734 else
1735 addch ('v');
1737 /* Now draw the nice relative pointer */
1738 if (l->count)
1739 line = 1+ ((l->pos * (l->height-2)) / l->count);
1740 else
1741 line = 0;
1743 for (i = 1; i < max_line; i++){
1744 widget_move (&l->widget, i, l->width);
1745 if (i != line)
1746 one_vline ();
1747 else
1748 addch ('*');
1752 static void
1753 listbox_draw (WListbox *l, int focused)
1755 WLEntry *e;
1756 int i;
1757 int sel_line;
1758 Dlg_head *h = l->widget.parent;
1759 int normalc = DLG_NORMALC (h);
1760 int selc;
1761 const char *text;
1763 if (focused){
1764 selc = DLG_FOCUSC (h);
1765 } else {
1766 selc = DLG_HOT_FOCUSC (h);
1768 sel_line = -1;
1770 for (e = l->top, i = 0; (i < l->height); i++){
1772 /* Display the entry */
1773 if (e == l->current && sel_line == -1){
1774 sel_line = i;
1775 attrset (selc);
1776 } else
1777 attrset (normalc);
1779 widget_move (&l->widget, i, 0);
1781 if ((i > 0 && e == l->list) || !l->list)
1782 text = "";
1783 else {
1784 text = e->text;
1785 e = e->next;
1787 tty_printf (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1789 l->cursor_y = sel_line;
1790 if (!l->scrollbar)
1791 return;
1792 attrset (normalc);
1793 listbox_drawscroll (l);
1796 /* Returns the number of items between s and e,
1797 must be on the same linked list */
1798 static int
1799 listbox_cdiff (WLEntry *s, WLEntry *e)
1801 int count;
1803 for (count = 0; s != e; count++)
1804 s = s->next;
1805 return count;
1808 static WLEntry *
1809 listbox_check_hotkey (WListbox *l, int key)
1811 int i;
1812 WLEntry *e;
1814 i = 0;
1815 e = l->list;
1816 if (!e)
1817 return NULL;
1819 while (1) {
1821 /* If we didn't find anything, return */
1822 if (i && e == l->list)
1823 return NULL;
1825 if (e->hotkey == key)
1826 return e;
1828 i++;
1829 e = e->next;
1833 /* Used only for display updating, for avoiding line at a time scroll */
1834 void
1835 listbox_select_last (WListbox *l, int set_top)
1837 if (l->list){
1838 l->current = l->list->prev;
1839 l->pos = l->count - 1;
1840 if (set_top)
1841 l->top = l->list->prev;
1845 void
1846 listbox_remove_list (WListbox *l)
1848 WLEntry *p, *q;
1850 if (!l->count)
1851 return;
1853 p = l->list;
1855 while (l->count--) {
1856 q = p->next;
1857 g_free (p->text);
1858 g_free (p);
1859 p = q;
1861 l->pos = l->count = 0;
1862 l->list = l->top = l->current = 0;
1866 * bor 30.10.96: added force flag to remove *last* entry as well
1867 * bor 30.10.96: corrected selection bug if last entry was removed
1870 void
1871 listbox_remove_current (WListbox *l, int force)
1873 WLEntry *p;
1875 /* Ok, note: this won't allow for emtpy lists */
1876 if (!force && (!l->count || l->count == 1))
1877 return;
1879 l->count--;
1880 p = l->current;
1882 if (l->count) {
1883 l->current->next->prev = l->current->prev;
1884 l->current->prev->next = l->current->next;
1885 if (p->next == l->list) {
1886 l->current = p->prev;
1887 l->pos--;
1889 else
1890 l->current = p->next;
1892 if (p == l->list)
1893 l->list = l->top = p->next;
1894 } else {
1895 l->pos = 0;
1896 l->list = l->top = l->current = 0;
1899 g_free (p->text);
1900 g_free (p);
1903 /* Makes *e the selected entry (sets current and pos) */
1904 void
1905 listbox_select_entry (WListbox *l, WLEntry *dest)
1907 WLEntry *e;
1908 int pos;
1909 int top_seen;
1911 top_seen = 0;
1913 /* Special case */
1914 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1916 if (e == l->top)
1917 top_seen = 1;
1919 if (e == dest){
1920 l->current = e;
1921 if (top_seen){
1922 while (listbox_cdiff (l->top, l->current) >= l->height)
1923 l->top = l->top->next;
1924 } else {
1925 l->top = l->current;
1927 l->pos = pos;
1928 return;
1931 /* If we are unable to find it, set decent values */
1932 l->current = l->top = l->list;
1933 l->pos = 0;
1936 /* Selects from base the pos element */
1937 static WLEntry *
1938 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1940 WLEntry *last = l->list->prev;
1942 if (base == last)
1943 return last;
1944 while (pos--){
1945 base = base->next;
1946 if (base == last)
1947 break;
1949 return base;
1952 static inline cb_ret_t
1953 listbox_back (WListbox *l)
1955 if (l->pos){
1956 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1957 return MSG_HANDLED;
1959 return MSG_NOT_HANDLED;
1962 /* Return MSG_HANDLED if we want a redraw */
1963 static cb_ret_t
1964 listbox_key (WListbox *l, int key)
1966 int i;
1967 int j = 0;
1969 if (!l->list)
1970 return MSG_NOT_HANDLED;
1972 switch (key){
1973 case KEY_HOME:
1974 case KEY_A1:
1975 case ALT ('<'):
1976 l->current = l->top = l->list;
1977 l->pos = 0;
1978 return MSG_HANDLED;
1980 case KEY_END:
1981 case KEY_C1:
1982 case ALT ('>'):
1983 l->current = l->top = l->list->prev;
1984 for (i = min (l->height - 1, l->count - 1); i; i--)
1985 l->top = l->top->prev;
1986 l->pos = l->count - 1;
1987 return MSG_HANDLED;
1989 case XCTRL('p'):
1990 case KEY_UP:
1991 listbox_back (l);
1992 return MSG_HANDLED;
1994 case XCTRL('n'):
1995 case KEY_DOWN:
1996 listbox_fwd (l);
1997 return MSG_HANDLED;
1999 case KEY_NPAGE:
2000 case XCTRL('v'):
2001 for (i = 0; i < l->height-1; i++)
2002 j |= listbox_fwd (l);
2003 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2005 case KEY_PPAGE:
2006 case ALT('v'):
2007 for (i = 0; i < l->height-1; i++)
2008 j |= listbox_back (l);
2009 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2011 return MSG_NOT_HANDLED;
2014 static void
2015 listbox_destroy (WListbox *l)
2017 WLEntry *n, *p = l->list;
2018 int i;
2020 for (i = 0; i < l->count; i++){
2021 n = p->next;
2022 g_free (p->text);
2023 g_free (p);
2024 p = n;
2028 static cb_ret_t
2029 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2031 WListbox *l = (WListbox *) w;
2032 cb_ret_t ret_code;
2033 WLEntry *e;
2034 Dlg_head *h = l->widget.parent;
2036 switch (msg) {
2037 case WIDGET_INIT:
2038 return MSG_HANDLED;
2040 case WIDGET_HOTKEY:
2041 if ((e = listbox_check_hotkey (l, parm)) != NULL) {
2042 int action;
2044 listbox_select_entry (l, e);
2046 if (l->cback)
2047 action = (*l->cback) (l);
2048 else
2049 action = LISTBOX_DONE;
2051 if (action == LISTBOX_DONE) {
2052 h->ret_value = B_ENTER;
2053 dlg_stop (h);
2055 return MSG_HANDLED;
2056 } else
2057 return MSG_NOT_HANDLED;
2059 case WIDGET_KEY:
2060 if ((ret_code = listbox_key (l, parm)))
2061 listbox_draw (l, 1);
2062 return ret_code;
2064 case WIDGET_CURSOR:
2065 widget_move (&l->widget, l->cursor_y, 0);
2066 return MSG_HANDLED;
2068 case WIDGET_FOCUS:
2069 case WIDGET_UNFOCUS:
2070 case WIDGET_DRAW:
2071 listbox_draw (l, msg != WIDGET_UNFOCUS);
2072 return MSG_HANDLED;
2074 case WIDGET_DESTROY:
2075 listbox_destroy (l);
2076 return MSG_HANDLED;
2078 default:
2079 return default_proc (msg, parm);
2083 static int
2084 listbox_event (Gpm_Event *event, void *data)
2086 WListbox *l = data;
2087 Widget *w = data;
2088 int i;
2090 Dlg_head *h = l->widget.parent;
2092 /* Single click */
2093 if (event->type & GPM_DOWN)
2094 dlg_select_widget (l);
2095 if (!l->list)
2096 return MOU_NORMAL;
2097 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2098 if (event->x < 0 || event->x >= l->width)
2099 return MOU_REPEAT;
2100 if (event->y < 1)
2101 for (i = -event->y; i >= 0; i--)
2102 listbox_back (l);
2103 else if (event->y > l->height)
2104 for (i = event->y - l->height; i > 0; i--)
2105 listbox_fwd (l);
2106 else
2107 listbox_select_entry (l,
2108 listbox_select_pos (l, l->top,
2109 event->y - 1));
2111 /* We need to refresh ourselves since the dialog manager doesn't */
2112 /* know about this event */
2113 listbox_callback (w, WIDGET_DRAW, 0);
2114 mc_refresh ();
2115 return MOU_REPEAT;
2118 /* Double click */
2119 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2120 int action;
2122 if (event->x < 0 || event->x >= l->width)
2123 return MOU_NORMAL;
2124 if (event->y < 1 || event->y > l->height)
2125 return MOU_NORMAL;
2127 dlg_select_widget (l);
2128 listbox_select_entry (l,
2129 listbox_select_pos (l, l->top,
2130 event->y - 1));
2132 if (l->cback)
2133 action = (*l->cback) (l);
2134 else
2135 action = LISTBOX_DONE;
2137 if (action == LISTBOX_DONE) {
2138 h->ret_value = B_ENTER;
2139 dlg_stop (h);
2140 return MOU_NORMAL;
2143 return MOU_NORMAL;
2146 WListbox *
2147 listbox_new (int y, int x, int width, int height, lcback callback)
2149 WListbox *l = g_new (WListbox, 1);
2151 init_widget (&l->widget, y, x, height, width,
2152 listbox_callback, listbox_event);
2154 l->list = l->top = l->current = 0;
2155 l->pos = 0;
2156 l->width = width;
2157 if (height <= 0)
2158 l->height = 1;
2159 else
2160 l->height = height;
2161 l->count = 0;
2162 l->cback = callback;
2163 l->allow_duplicates = 1;
2164 l->scrollbar = slow_terminal ? 0 : 1;
2165 widget_want_hotkey (l->widget, 1);
2167 return l;
2170 /* Listbox item adding function. They still lack a lot of functionality */
2171 /* any takers? */
2172 /* 1.11.96 bor: added pos argument to control placement of new entry */
2173 static void
2174 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2176 if (!l->list){
2177 l->list = e;
2178 l->top = e;
2179 l->current = e;
2180 e->next = l->list;
2181 e->prev = l->list;
2182 } else if (pos == LISTBOX_APPEND_AT_END) {
2183 e->next = l->list;
2184 e->prev = l->list->prev;
2185 l->list->prev->next = e;
2186 l->list->prev = e;
2187 } else if (pos == LISTBOX_APPEND_BEFORE){
2188 e->next = l->current;
2189 e->prev = l->current->prev;
2190 l->current->prev->next = e;
2191 l->current->prev = e;
2192 if (l->list == l->current) { /* move list one position down */
2193 l->list = e;
2194 l->top = e;
2196 } else if (pos == LISTBOX_APPEND_AFTER) {
2197 e->prev = l->current;
2198 e->next = l->current->next;
2199 l->current->next->prev = e;
2200 l->current->next = e;
2201 } else if (pos == LISTBOX_APPEND_SORTED) {
2202 WLEntry *w = l->list;
2204 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2205 w = w->next;
2206 if (w->next == l->list) {
2207 e->prev = w;
2208 e->next = l->list;
2209 w->next = e;
2210 l->list->prev = e;
2211 } else {
2212 e->next = w;
2213 e->prev = w->prev;
2214 w->prev->next = e;
2215 w->prev = e;
2218 l->count++;
2221 char *
2222 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2223 const char *text, void *data)
2225 WLEntry *entry;
2227 if (!l)
2228 return NULL;
2230 if (!l->allow_duplicates)
2231 if (listbox_search_text (l, text))
2232 return NULL;
2234 entry = g_new (WLEntry, 1);
2235 entry->text = g_strdup (text);
2236 entry->data = data;
2237 entry->hotkey = hotkey;
2239 listbox_append_item (l, entry, pos);
2241 return entry->text;
2244 /* Selects the nth entry in the listbox */
2245 void
2246 listbox_select_by_number (WListbox *l, int n)
2248 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2251 WLEntry *
2252 listbox_search_text (WListbox *l, const char *text)
2254 WLEntry *e;
2256 e = l->list;
2257 if (!e)
2258 return NULL;
2260 do {
2261 if(!strcmp (e->text, text))
2262 return e;
2263 e = e->next;
2264 } while (e!=l->list);
2266 return NULL;
2269 /* Returns the current string text as well as the associated extra data */
2270 void
2271 listbox_get_current (WListbox *l, char **string, char **extra)
2273 if (!l->current){
2274 *string = 0;
2275 *extra = 0;
2277 if (string && l->current)
2278 *string = l->current->text;
2279 if (extra && l->current)
2280 *extra = l->current->data;
2283 /* returns TRUE if a function has been called, FALSE otherwise. */
2284 static gboolean
2285 buttonbar_call (WButtonBar *bb, int i)
2287 switch (bb->labels[i].tag) {
2288 case BBFUNC_NONE:
2289 break;
2290 case BBFUNC_VOID:
2291 bb->labels[i].u.fn_void ();
2292 return TRUE;
2293 case BBFUNC_PTR:
2294 bb->labels[i].u.fn_ptr (bb->labels[i].data);
2295 return TRUE;
2297 return FALSE;
2301 static cb_ret_t
2302 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2304 WButtonBar *bb = (WButtonBar *) w;
2305 int i;
2307 switch (msg) {
2308 case WIDGET_FOCUS:
2309 return MSG_NOT_HANDLED;
2311 case WIDGET_HOTKEY:
2312 for (i = 0; i < 10; i++) {
2313 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2314 return MSG_HANDLED;
2316 return MSG_NOT_HANDLED;
2318 case WIDGET_DRAW:
2319 if (!bb->visible)
2320 return MSG_HANDLED;
2321 widget_move (&bb->widget, 0, 0);
2322 attrset (DEFAULT_COLOR);
2323 tty_printf ("%-*s", bb->widget.cols, "");
2324 for (i = 0; i < COLS / 8 && i < 10; i++) {
2325 widget_move (&bb->widget, 0, i * 8);
2326 attrset (DEFAULT_COLOR);
2327 tty_printf ("%d", i + 1);
2328 attrset (SELECTED_COLOR);
2329 tty_printf ("%-*s", ((i + 1) * 8 == COLS ? 5 : 6),
2330 bb->labels[i].text ? bb->labels[i].text : "");
2331 attrset (DEFAULT_COLOR);
2333 attrset (SELECTED_COLOR);
2334 return MSG_HANDLED;
2336 case WIDGET_DESTROY:
2337 for (i = 0; i < 10; i++)
2338 g_free (bb->labels[i].text);
2339 return MSG_HANDLED;
2341 default:
2342 return default_proc (msg, parm);
2346 static int
2347 buttonbar_event (Gpm_Event *event, void *data)
2349 WButtonBar *bb = data;
2350 int button;
2352 if (!(event->type & GPM_UP))
2353 return MOU_NORMAL;
2354 if (event->y == 2)
2355 return MOU_NORMAL;
2356 button = event->x / 8;
2357 if (button < 10)
2358 buttonbar_call (bb, button);
2359 return MOU_NORMAL;
2362 WButtonBar *
2363 buttonbar_new (int visible)
2365 int i;
2366 WButtonBar *bb = g_new (WButtonBar, 1);
2368 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2369 buttonbar_callback, buttonbar_event);
2371 bb->visible = visible;
2372 for (i = 0; i < 10; i++){
2373 bb->labels[i].text = NULL;
2374 bb->labels[i].tag = BBFUNC_NONE;
2376 widget_want_hotkey (bb->widget, 1);
2377 widget_want_cursor (bb->widget, 0);
2379 return bb;
2382 static void
2383 set_label_text (WButtonBar * bb, int index, const char *text)
2385 g_free (bb->labels[index - 1].text);
2387 bb->labels[index - 1].text = g_strdup (text);
2390 /* Find ButtonBar widget in the dialog */
2391 WButtonBar *
2392 find_buttonbar (Dlg_head *h)
2394 WButtonBar *bb;
2396 bb = (WButtonBar *) find_widget_type (h, buttonbar_callback);
2397 return bb;
2400 void
2401 buttonbar_clear_label (Dlg_head *h, int idx)
2403 WButtonBar *bb = find_buttonbar (h);
2405 if (!bb)
2406 return;
2408 set_label_text (bb, idx, "");
2409 bb->labels[idx - 1].tag = BBFUNC_NONE;
2412 void
2413 buttonbar_set_label_data (Dlg_head *h, int idx, const char *text, buttonbarfn cback,
2414 void *data)
2416 WButtonBar *bb = find_buttonbar (h);
2418 if (!bb)
2419 return;
2421 assert (cback != (buttonbarfn) 0);
2422 set_label_text (bb, idx, text);
2423 bb->labels[idx - 1].tag = BBFUNC_PTR;
2424 bb->labels[idx - 1].u.fn_ptr = cback;
2425 bb->labels[idx - 1].data = data;
2428 void
2429 buttonbar_set_label (Dlg_head *h, int idx, const char *text, voidfn cback)
2431 WButtonBar *bb = find_buttonbar (h);
2433 if (!bb)
2434 return;
2436 assert (cback != (voidfn) 0);
2437 set_label_text (bb, idx, text);
2438 bb->labels[idx - 1].tag = BBFUNC_VOID;
2439 bb->labels[idx - 1].u.fn_void = cback;
2442 void
2443 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2445 bb->visible = visible;
2448 void
2449 buttonbar_redraw (Dlg_head *h)
2451 WButtonBar *bb = find_buttonbar (h);
2453 if (!bb)
2454 return;
2456 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2459 static cb_ret_t
2460 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2462 WGroupbox *g = (WGroupbox *) w;
2464 switch (msg) {
2465 case WIDGET_INIT:
2466 return MSG_HANDLED;
2468 case WIDGET_FOCUS:
2469 return MSG_NOT_HANDLED;
2471 case WIDGET_DRAW:
2472 attrset (COLOR_NORMAL);
2473 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2474 g->widget.x - g->widget.parent->x, g->widget.lines,
2475 g->widget.cols);
2477 attrset (COLOR_HOT_NORMAL);
2478 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2479 g->widget.x - g->widget.parent->x + 1);
2480 addstr (g->title);
2481 return MSG_HANDLED;
2483 case WIDGET_DESTROY:
2484 g_free (g->title);
2485 return MSG_HANDLED;
2487 default:
2488 return default_proc (msg, parm);
2492 WGroupbox *
2493 groupbox_new (int x, int y, int width, int height, const char *title)
2495 WGroupbox *g = g_new (WGroupbox, 1);
2497 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2499 g->widget.options &= ~W_WANT_CURSOR;
2500 widget_want_hotkey (g->widget, 0);
2502 /* Strip existing spaces, add one space before and after the title */
2503 if (title) {
2504 char *t;
2505 t = g_strstrip (g_strdup (title));
2506 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2507 g_free (t);
2510 return g;