replaced gboolean by bool (from mhl/types.h)
[free-mc.git] / src / widget.c
blob9fb0d47fefea82eca8f8de641de1d73579560b2a
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>
35 #include <sys/types.h>
37 #include <mhl/types.h>
38 #include <mhl/string.h>
40 #include "global.h"
41 #include "tty.h"
42 #include "color.h"
43 #include "mouse.h"
44 #include "dialog.h"
45 #include "widget.h"
46 #include "win.h"
47 #include "key.h" /* XCTRL and ALT macros */
48 #include "profile.h" /* for history loading and saving */
49 #include "wtools.h" /* For common_dialog_repaint() */
50 #include "main.h" /* for `slow_terminal' */
52 #define HISTORY_FILE_NAME ".mc/history"
54 struct WButtonBar {
55 Widget widget;
56 int visible; /* Is it visible? */
57 struct {
58 char *text;
59 enum { BBFUNC_NONE, BBFUNC_VOID, BBFUNC_PTR } tag;
60 union {
61 voidfn fn_void;
62 buttonbarfn fn_ptr;
63 } u;
64 void *data;
65 } labels [10];
68 static int button_event (Gpm_Event *event, void *);
70 int quote = 0;
72 static void
73 widget_selectcolor (Widget *w, bool focused, bool hotkey)
75 Dlg_head *h = w->parent;
77 attrset (hotkey
78 ? (focused
79 ? DLG_HOT_FOCUSC (h)
80 : DLG_HOT_NORMALC (h))
81 : (focused
82 ? DLG_FOCUSC (h)
83 : DLG_NORMALC (h)));
86 static cb_ret_t
87 button_callback (Widget *w, widget_msg_t msg, int parm)
89 WButton *b = (WButton *) w;
90 char buf[BUF_SMALL];
91 int stop = 0;
92 int off = 0;
93 Dlg_head *h = b->widget.parent;
95 switch (msg) {
96 case WIDGET_HOTKEY:
98 * Don't let the default button steal Enter from the current
99 * button. This is a workaround for the flawed event model
100 * when hotkeys are sent to all widgets before the key is
101 * handled by the current widget.
103 if (parm == '\n' && h->current == &b->widget) {
104 button_callback (w, WIDGET_KEY, ' ');
105 return MSG_HANDLED;
108 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
109 button_callback (w, WIDGET_KEY, ' ');
110 return MSG_HANDLED;
113 if (b->hotkey == tolower (parm)) {
114 button_callback (w, WIDGET_KEY, ' ');
115 return MSG_HANDLED;
118 return MSG_NOT_HANDLED;
120 case WIDGET_KEY:
121 if (parm != ' ' && parm != '\n')
122 return MSG_NOT_HANDLED;
124 if (b->callback)
125 stop = (*b->callback) (b->action);
126 if (!b->callback || stop) {
127 h->ret_value = b->action;
128 dlg_stop (h);
130 return MSG_HANDLED;
132 case WIDGET_CURSOR:
133 switch (b->flags) {
134 case DEFPUSH_BUTTON:
135 off = 3;
136 break;
137 case NORMAL_BUTTON:
138 off = 2;
139 break;
140 case NARROW_BUTTON:
141 off = 1;
142 break;
143 case HIDDEN_BUTTON:
144 default:
145 off = 0;
146 break;
148 widget_move (&b->widget, 0, b->hotpos + off);
149 return MSG_HANDLED;
151 case WIDGET_UNFOCUS:
152 case WIDGET_FOCUS:
153 case WIDGET_DRAW:
154 if (msg == WIDGET_UNFOCUS)
155 b->selected = 0;
156 else if (msg == WIDGET_FOCUS)
157 b->selected = 1;
159 switch (b->flags) {
160 case DEFPUSH_BUTTON:
161 g_snprintf (buf, sizeof (buf), "[< %s >]", b->text);
162 off = 3;
163 break;
164 case NORMAL_BUTTON:
165 g_snprintf (buf, sizeof (buf), "[ %s ]", b->text);
166 off = 2;
167 break;
168 case NARROW_BUTTON:
169 g_snprintf (buf, sizeof (buf), "[%s]", b->text);
170 off = 1;
171 break;
172 case HIDDEN_BUTTON:
173 default:
174 buf[0] = '\0';
175 off = 0;
176 break;
179 widget_selectcolor (w, b->selected, FALSE);
180 widget_move (w, 0, 0);
182 addstr (buf);
184 if (b->hotpos >= 0) {
185 widget_selectcolor (w, b->selected, TRUE);
186 widget_move (w, 0, b->hotpos + off);
187 addch ((unsigned char) b->text[b->hotpos]);
189 return MSG_HANDLED;
191 case WIDGET_DESTROY:
192 g_free (b->text);
193 return MSG_HANDLED;
195 default:
196 return default_proc (msg, parm);
200 static int
201 button_event (Gpm_Event *event, void *data)
203 WButton *b = data;
205 if (event->type & (GPM_DOWN|GPM_UP)){
206 Dlg_head *h=b->widget.parent;
207 dlg_select_widget (b);
208 if (event->type & GPM_UP){
209 button_callback ((Widget *) data, WIDGET_KEY, ' ');
210 (*h->callback) (h, DLG_POST_KEY, ' ');
211 return MOU_NORMAL;
214 return MOU_NORMAL;
217 static int
218 button_len (const char *text, unsigned int flags)
220 int ret = strlen (text);
221 switch (flags){
222 case DEFPUSH_BUTTON:
223 ret += 6;
224 break;
225 case NORMAL_BUTTON:
226 ret += 4;
227 break;
228 case NARROW_BUTTON:
229 ret += 2;
230 break;
231 case HIDDEN_BUTTON:
232 default:
233 return 0;
235 return ret;
239 * Locate the hotkey and remove it from the button text. Assuming that
240 * the button text is g_malloc()ed, we can safely change and shorten it.
242 static void
243 button_scan_hotkey (WButton *b)
245 char *cp = strchr (b->text, '&');
247 if (cp != NULL && cp[1] != '\0') {
248 g_strlcpy (cp, cp + 1, strlen (cp));
249 b->hotkey = tolower ((unsigned char) *cp);
250 b->hotpos = cp - b->text;
254 WButton *
255 button_new (int y, int x, int action, int flags, const char *text,
256 bcback callback)
258 WButton *b = g_new (WButton, 1);
260 init_widget (&b->widget, y, x, 1, button_len (text, flags),
261 button_callback, button_event);
263 b->action = action;
264 b->flags = flags;
265 b->selected = 0;
266 b->text = g_strdup (text);
267 b->callback = callback;
268 widget_want_hotkey (b->widget, 1);
269 b->hotkey = 0;
270 b->hotpos = -1;
272 button_scan_hotkey(b);
273 return b;
276 const char *
277 button_get_text (WButton *b)
279 return b->text;
282 void
283 button_set_text (WButton *b, const char *text)
285 g_free (b->text);
286 b->text = g_strdup (text);
287 b->widget.cols = button_len (text, b->flags);
288 button_scan_hotkey(b);
289 dlg_redraw (b->widget.parent);
293 /* Radio button widget */
294 static int radio_event (Gpm_Event *event, void *);
296 static cb_ret_t
297 radio_callback (Widget *w, widget_msg_t msg, int parm)
299 WRadio *r = (WRadio *) w;
300 int i;
301 Dlg_head *h = r->widget.parent;
303 switch (msg) {
304 case WIDGET_HOTKEY:
306 int i, lp = tolower (parm);
307 const char *cp;
309 for (i = 0; i < r->count; i++) {
310 cp = strchr (r->texts[i], '&');
311 if (cp != NULL && cp[1] != '\0') {
312 int c = tolower ((unsigned char) cp[1]);
314 if (c != lp)
315 continue;
316 r->pos = i;
318 /* Take action */
319 radio_callback (w, WIDGET_KEY, ' ');
320 return MSG_HANDLED;
324 return MSG_NOT_HANDLED;
326 case WIDGET_KEY:
327 switch (parm) {
328 case ' ':
329 r->sel = r->pos;
330 (*h->callback) (h, DLG_ACTION, 0);
331 radio_callback (w, WIDGET_FOCUS, ' ');
332 return MSG_HANDLED;
334 case KEY_UP:
335 case KEY_LEFT:
336 if (r->pos > 0) {
337 r->pos--;
338 return MSG_HANDLED;
340 return MSG_NOT_HANDLED;
342 case KEY_DOWN:
343 case KEY_RIGHT:
344 if (r->count - 1 > r->pos) {
345 r->pos++;
346 return MSG_HANDLED;
349 return MSG_NOT_HANDLED;
351 case WIDGET_CURSOR:
352 (*h->callback) (h, DLG_ACTION, 0);
353 radio_callback (w, WIDGET_FOCUS, ' ');
354 widget_move (&r->widget, r->pos, 1);
355 return MSG_HANDLED;
357 case WIDGET_UNFOCUS:
358 case WIDGET_FOCUS:
359 case WIDGET_DRAW:
360 for (i = 0; i < r->count; i++) {
361 register const char *cp;
362 const bool focused = (i == r->pos && msg == WIDGET_FOCUS);
363 widget_selectcolor (w, focused, FALSE);
364 widget_move (&r->widget, i, 0);
366 tty_printf ("(%c) ", (r->sel == i) ? '*' : ' ');
367 for (cp = r->texts[i]; *cp; cp++) {
368 if (*cp == '&') {
369 widget_selectcolor (w, focused, TRUE);
370 addch (*++cp);
371 widget_selectcolor (w, focused, FALSE);
372 } else
373 addch (*cp);
376 return MSG_HANDLED;
378 default:
379 return default_proc (msg, parm);
383 static int
384 radio_event (Gpm_Event *event, void *data)
386 WRadio *r = data;
387 Widget *w = data;
389 if (event->type & (GPM_DOWN|GPM_UP)){
390 Dlg_head *h = r->widget.parent;
392 r->pos = event->y - 1;
393 dlg_select_widget (r);
394 if (event->type & GPM_UP){
395 radio_callback (w, WIDGET_KEY, ' ');
396 radio_callback (w, WIDGET_FOCUS, 0);
397 (*h->callback) (h, DLG_POST_KEY, ' ');
398 return MOU_NORMAL;
401 return MOU_NORMAL;
404 WRadio *
405 radio_new (int y, int x, int count, const char **texts)
407 WRadio *r = g_new (WRadio, 1);
408 int i, max, m;
410 /* Compute the longest string */
411 max = 0;
412 for (i = 0; i < count; i++){
413 m = strlen (texts [i]);
414 if (m > max)
415 max = m;
418 init_widget (&r->widget, y, x, count, max, radio_callback, radio_event);
419 r->state = 1;
420 r->pos = 0;
421 r->sel = 0;
422 r->count = count;
423 r->texts = texts;
424 widget_want_hotkey (r->widget, 1);
426 return r;
430 /* Checkbutton widget */
432 static int check_event (Gpm_Event *event, void *);
434 static cb_ret_t
435 check_callback (Widget *w, widget_msg_t msg, int parm)
437 WCheck *c = (WCheck *) w;
438 Dlg_head *h = c->widget.parent;
440 switch (msg) {
441 case WIDGET_HOTKEY:
442 if (c->hotkey == parm
443 || (c->hotkey >= 'a' && c->hotkey <= 'z'
444 && c->hotkey - 32 == parm)) {
445 check_callback (w, WIDGET_KEY, ' '); /* make action */
446 return MSG_HANDLED;
448 return MSG_NOT_HANDLED;
450 case WIDGET_KEY:
451 if (parm != ' ')
452 return MSG_NOT_HANDLED;
453 c->state ^= C_BOOL;
454 c->state ^= C_CHANGE;
455 (*h->callback) (h, DLG_ACTION, 0);
456 check_callback (w, WIDGET_FOCUS, ' ');
457 return MSG_HANDLED;
459 case WIDGET_CURSOR:
460 widget_move (&c->widget, 0, 1);
461 return MSG_HANDLED;
463 case WIDGET_FOCUS:
464 case WIDGET_UNFOCUS:
465 case WIDGET_DRAW:
466 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
467 widget_move (&c->widget, 0, 0);
468 tty_printf ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
470 if (c->hotpos >= 0) {
471 widget_selectcolor (w, msg == WIDGET_FOCUS, TRUE);
472 widget_move (&c->widget, 0, +c->hotpos + 4);
473 addch ((unsigned char) c->text[c->hotpos]);
475 return MSG_HANDLED;
477 case WIDGET_DESTROY:
478 g_free (c->text);
479 return MSG_HANDLED;
481 default:
482 return default_proc (msg, parm);
486 static int
487 check_event (Gpm_Event *event, void *data)
489 WCheck *c = data;
490 Widget *w = data;
492 if (event->type & (GPM_DOWN|GPM_UP)){
493 Dlg_head *h = c->widget.parent;
495 dlg_select_widget (c);
496 if (event->type & GPM_UP){
497 check_callback (w, WIDGET_KEY, ' ');
498 check_callback (w, WIDGET_FOCUS, 0);
499 (*h->callback) (h, DLG_POST_KEY, ' ');
500 return MOU_NORMAL;
503 return MOU_NORMAL;
506 WCheck *
507 check_new (int y, int x, int state, const char *text)
509 WCheck *c = g_new (WCheck, 1);
510 const char *s;
511 char *t;
513 init_widget (&c->widget, y, x, 1, strlen (text),
514 check_callback, check_event);
515 c->state = state ? C_BOOL : 0;
516 c->text = g_strdup (text);
517 c->hotkey = 0;
518 c->hotpos = -1;
519 widget_want_hotkey (c->widget, 1);
521 /* Scan for the hotkey */
522 for (s = text, t = c->text; *s; s++, t++){
523 if (*s != '&'){
524 *t = *s;
525 continue;
527 s++;
528 if (*s){
529 c->hotkey = tolower ((unsigned char) *s);
530 c->hotpos = t - c->text;
532 *t = *s;
534 *t = 0;
535 return c;
539 /* Label widget */
541 static cb_ret_t
542 label_callback (Widget *w, widget_msg_t msg, int parm)
544 WLabel *l = (WLabel *) w;
545 Dlg_head *h = l->widget.parent;
547 switch (msg) {
548 case WIDGET_INIT:
549 return MSG_HANDLED;
551 /* We don't want to get the focus */
552 case WIDGET_FOCUS:
553 return MSG_NOT_HANDLED;
555 case WIDGET_DRAW:
557 char *p = l->text, *q, c = 0;
558 int y = 0;
560 if (!l->text)
561 return MSG_HANDLED;
563 if (l->transparent)
564 attrset (DEFAULT_COLOR);
565 else
566 attrset (DLG_NORMALC (h));
567 for (;;) {
568 int xlen;
570 q = strchr (p, '\n');
571 if (q) {
572 c = *q;
573 *q = 0;
575 widget_move (&l->widget, y, 0);
576 tty_printf ("%s", p);
577 xlen = l->widget.cols - strlen (p);
578 if (xlen > 0)
579 tty_printf ("%*s", xlen, " ");
580 if (!q)
581 break;
582 *q = c;
583 p = q + 1;
584 y++;
586 return MSG_HANDLED;
589 case WIDGET_DESTROY:
590 g_free (l->text);
591 return MSG_HANDLED;
593 default:
594 return default_proc (msg, parm);
598 void
599 label_set_text (WLabel *label, const char *text)
601 int newcols = label->widget.cols;
603 if (label->text && text && !strcmp (label->text, text))
604 return; /* Flickering is not nice */
606 g_free (label->text);
608 if (text){
609 label->text = g_strdup (text);
610 if (label->auto_adjust_cols) {
611 newcols = strlen (text);
612 if (newcols > label->widget.cols)
613 label->widget.cols = newcols;
615 } else
616 label->text = 0;
618 if (label->widget.parent)
619 label_callback ((Widget *) label, WIDGET_DRAW, 0);
621 if (newcols < label->widget.cols)
622 label->widget.cols = newcols;
625 WLabel *
626 label_new (int y, int x, const char *text)
628 WLabel *l;
629 int width;
631 /* Multiline labels are immutable - no need to compute their sizes */
632 if (!text || strchr(text, '\n'))
633 width = 1;
634 else
635 width = strlen (text);
637 l = g_new (WLabel, 1);
638 init_widget (&l->widget, y, x, 1, width, label_callback, NULL);
639 l->text = text ? g_strdup (text) : 0;
640 l->auto_adjust_cols = 1;
641 l->transparent = 0;
642 widget_want_cursor (l->widget, 0);
643 return l;
647 /* Gauge widget (progress indicator) */
648 /* Currently width is hardcoded here for text mode */
649 #define gauge_len 47
651 static cb_ret_t
652 gauge_callback (Widget *w, widget_msg_t msg, int parm)
654 WGauge *g = (WGauge *) w;
655 Dlg_head *h = g->widget.parent;
657 if (msg == WIDGET_INIT)
658 return MSG_HANDLED;
660 /* We don't want to get the focus */
661 if (msg == WIDGET_FOCUS)
662 return MSG_NOT_HANDLED;
664 if (msg == WIDGET_DRAW){
665 widget_move (&g->widget, 0, 0);
666 attrset (DLG_NORMALC (h));
667 if (!g->shown)
668 tty_printf ("%*s", gauge_len, "");
669 else {
670 int percentage, columns;
671 long total = g->max, done = g->current;
673 if (total <= 0 || done < 0) {
674 done = 0;
675 total = 100;
677 if (done > total)
678 done = total;
679 while (total > 65535) {
680 total /= 256;
681 done /= 256;
683 percentage = (200 * done / total + 1) / 2;
684 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
685 addch ('[');
686 attrset (GAUGE_COLOR);
687 tty_printf ("%*s", (int) columns, "");
688 attrset (DLG_NORMALC (h));
689 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
691 return MSG_HANDLED;
693 return default_proc (msg, parm);
696 void
697 gauge_set_value (WGauge *g, int max, int current)
699 if (g->current == current && g->max == max)
700 return; /* Do not flicker */
701 if (max == 0)
702 max = 1; /* I do not like division by zero :) */
704 g->current = current;
705 g->max = max;
706 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
709 void
710 gauge_show (WGauge *g, int shown)
712 if (g->shown == shown)
713 return;
714 g->shown = shown;
715 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
718 WGauge *
719 gauge_new (int y, int x, int shown, int max, int current)
721 WGauge *g = g_new (WGauge, 1);
723 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
724 g->shown = shown;
725 if (max == 0)
726 max = 1; /* I do not like division by zero :) */
727 g->max = max;
728 g->current = current;
729 widget_want_cursor (g->widget, 0);
730 return g;
734 /* Input widget */
736 /* {{{ history button */
738 #define LARGE_HISTORY_BUTTON 1
740 #ifdef LARGE_HISTORY_BUTTON
741 # define HISTORY_BUTTON_WIDTH 3
742 #else
743 # define HISTORY_BUTTON_WIDTH 1
744 #endif
746 #define should_show_history_button(in) \
747 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
749 static void draw_history_button (WInput * in)
751 char c;
752 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
753 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
754 #ifdef LARGE_HISTORY_BUTTON
756 Dlg_head *h;
757 h = in->widget.parent;
758 #if 0
759 attrset (NORMALC); /* button has the same color as other buttons */
760 addstr ("[ ]");
761 attrset (HOT_NORMALC);
762 #else
763 attrset (NORMAL_COLOR);
764 addstr ("[ ]");
765 /* Too distracting: attrset (MARKED_COLOR); */
766 #endif
767 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
768 addch (c);
770 #else
771 attrset (MARKED_COLOR);
772 addch (c);
773 #endif
776 /* }}} history button */
779 /* Input widgets now have a global kill ring */
780 /* Pointer to killed data */
781 static char *kill_buffer = 0;
783 void
784 update_input (WInput *in, int clear_first)
786 int has_history = 0;
787 int i, j;
788 unsigned char c;
789 int buf_len = strlen (in->buffer);
791 if (should_show_history_button (in))
792 has_history = HISTORY_BUTTON_WIDTH;
794 if (in->disable_update)
795 return;
797 /* Make the point visible */
798 if ((in->point < in->first_shown) ||
799 (in->point >= in->first_shown+in->field_len - has_history)){
800 in->first_shown = in->point - (in->field_len / 3);
801 if (in->first_shown < 0)
802 in->first_shown = 0;
805 /* Adjust the mark */
806 if (in->mark > buf_len)
807 in->mark = buf_len;
809 if (has_history)
810 draw_history_button (in);
812 attrset (in->color);
814 widget_move (&in->widget, 0, 0);
815 for (i = 0; i < in->field_len - has_history; i++)
816 addch (' ');
817 widget_move (&in->widget, 0, 0);
819 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
820 c = in->buffer [j++];
821 c = is_printable (c) ? c : '.';
822 if (in->is_password)
823 c = '*';
824 addch (c);
826 widget_move (&in->widget, 0, in->point - in->first_shown);
828 if (clear_first)
829 in->first = 0;
832 void
833 winput_set_origin (WInput *in, int x, int field_len)
835 in->widget.x = x;
836 in->field_len = in->widget.cols = field_len;
837 update_input (in, 0);
840 /* {{{ history saving and loading */
842 int num_history_items_recorded = 60;
845 This loads and saves the history of an input line to and from the
846 widget. It is called with the widgets history name on creation of the
847 widget, and returns the GList list. It stores histories in the file
848 ~/.mc/history in using the profile code.
850 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
851 function) then input_new assigns the default text to be the last text
852 entered, or "" if not found.
855 GList *
856 history_get (const char *input_name)
858 int i;
859 GList *hist;
860 char *profile;
862 hist = NULL;
864 if (!num_history_items_recorded) /* this is how to disable */
865 return NULL;
866 if (!input_name)
867 return NULL;
868 if (!*input_name)
869 return NULL;
870 profile = mhl_str_dir_plus_file (home_dir, HISTORY_FILE_NAME);
871 for (i = 0;; i++) {
872 char key_name[BUF_TINY];
873 char this_entry[BUF_LARGE];
874 g_snprintf (key_name, sizeof (key_name), "%d", i);
875 GetPrivateProfileString (input_name, key_name, "", this_entry,
876 sizeof (this_entry), profile);
877 if (!*this_entry)
878 break;
880 hist = list_append_unique (hist, g_strdup (this_entry));
882 g_free (profile);
884 /* return pointer to the last entry in the list */
885 hist = g_list_last (hist);
887 return hist;
890 void
891 history_put (const char *input_name, GList *h)
893 int i;
894 char *profile;
896 if (!input_name)
897 return;
899 if (!*input_name)
900 return;
902 if (!h)
903 return;
905 if (!num_history_items_recorded) /* this is how to disable */
906 return;
908 profile = mhl_str_dir_plus_file (home_dir, HISTORY_FILE_NAME);
910 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
911 close (i);
913 /* Make sure the history is only readable by the user */
914 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
915 g_free (profile);
916 return;
919 /* go to end of list */
920 h = g_list_last (h);
922 /* go back 60 places */
923 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
924 h = g_list_previous (h);
926 if (input_name)
927 profile_clean_section (input_name, profile);
929 /* dump histories into profile */
930 for (i = 0; h; h = g_list_next (h)) {
931 char *text;
933 text = (char *) h->data;
935 /* We shouldn't have null entries, but let's be sure */
936 if (text && *text) {
937 char key_name[BUF_TINY];
938 g_snprintf (key_name, sizeof (key_name), "%d", i++);
939 WritePrivateProfileString (input_name, key_name, text,
940 profile);
944 g_free (profile);
947 /* }}} history saving and loading */
950 /* {{{ history display */
952 static const char *
953 i18n_htitle (void)
955 static const char *history_title = NULL;
957 if (history_title == NULL)
958 history_title = _(" History ");
959 return history_title;
962 static WLEntry *listbox_select_pos (WListbox *l, WLEntry *base, int
963 pos);
965 static inline cb_ret_t
966 listbox_fwd (WListbox *l)
968 if (l->current != l->list->prev){
969 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
970 return MSG_HANDLED;
972 return MSG_NOT_HANDLED;
975 char *
976 show_hist (GList *history, int widget_x, int widget_y)
978 GList *hi, *z;
979 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
980 int x, y, w, h;
981 char *q, *r = 0;
982 Dlg_head *query_dlg;
983 WListbox *query_list;
985 z = history;
986 if (!z)
987 return NULL;
989 z = g_list_first (history);
990 hi = z;
991 while (hi) {
992 if ((i = strlen ((char *) hi->data)) > maxlen)
993 maxlen = i;
994 count++;
995 hi = g_list_next (hi);
998 y = widget_y;
999 h = count + 2;
1000 if (h <= y || y > LINES - 6) {
1001 h = min (h, y - 1);
1002 y -= h;
1003 } else {
1004 y++;
1005 h = min (h, LINES - y);
1008 if (widget_x > 2)
1009 x = widget_x - 2;
1010 else
1011 x = 0;
1012 if ((w = maxlen + 4) + x > COLS) {
1013 w = min (w, COLS);
1014 x = COLS - w;
1017 query_dlg =
1018 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
1019 i18n_htitle (), DLG_COMPACT);
1020 query_list = listbox_new (1, 1, w - 2, h - 2, 0);
1021 add_widget (query_dlg, query_list);
1022 hi = z;
1023 if (y < widget_y) {
1024 /* traverse */
1025 while (hi) {
1026 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1027 hi = g_list_next (hi);
1029 while (listbox_fwd (query_list));
1030 } else {
1031 /* traverse backwards */
1032 hi = g_list_last (history);
1033 while (hi) {
1034 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1035 hi = g_list_previous (hi);
1038 run_dlg (query_dlg);
1039 q = NULL;
1040 if (query_dlg->ret_value != B_CANCEL) {
1041 listbox_get_current (query_list, &q, NULL);
1042 if (q)
1043 r = g_strdup (q);
1045 destroy_dlg (query_dlg);
1046 return r;
1049 static void do_show_hist (WInput * in)
1051 char *r;
1052 r = show_hist (in->history, in->widget.x, in->widget.y);
1053 if (r) {
1054 assign_text (in, r);
1055 g_free (r);
1059 /* }}} history display */
1061 static void
1062 input_destroy (WInput *in)
1064 if (!in){
1065 fprintf (stderr, "Internal error: null Input *\n");
1066 exit (1);
1069 new_input (in);
1071 if (in->history){
1072 if (!in->is_password) /* don't save passwords ;-) */
1073 history_put (in->history_name, in->history);
1075 in->history = g_list_first (in->history);
1076 g_list_foreach (in->history, (GFunc) g_free, NULL);
1077 g_list_free (in->history);
1080 g_free (in->buffer);
1081 free_completions (in);
1082 g_free (in->history_name);
1085 void
1086 input_disable_update (WInput *in)
1088 in->disable_update++;
1091 void
1092 input_enable_update (WInput *in)
1094 in->disable_update--;
1095 update_input (in, 0);
1098 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1101 push_history (WInput *in, const char *text)
1103 static int i18n;
1104 /* input widget where urls with passwords are entered without any
1105 vfs prefix */
1106 static const char *password_input_fields[] = {
1107 N_(" Link to a remote machine "),
1108 N_(" FTP to machine "),
1109 N_(" SMB link to machine ")
1111 char *t;
1112 const char *p;
1113 size_t i;
1115 if (!i18n) {
1116 i18n = 1;
1117 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1118 password_input_fields[i] = _(password_input_fields[i]);
1121 for (p = text; *p == ' ' || *p == '\t'; p++);
1122 if (!*p)
1123 return 0;
1125 if (in->history) {
1126 /* Avoid duplicated entries */
1127 in->history = g_list_last (in->history);
1128 if (!strcmp ((char *) in->history->data, text))
1129 return 1;
1132 t = g_strdup (text);
1134 if (in->history_name) {
1135 p = in->history_name + 3;
1136 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1137 if (strcmp (p, password_input_fields[i]) == 0)
1138 break;
1139 if (i < ELEMENTS (password_input_fields))
1140 strip_password (t, 0);
1141 else
1142 strip_password (t, 1);
1145 in->history = list_append_unique (in->history, t);
1146 in->need_push = 0;
1148 return 2;
1151 #undef ELEMENTS
1153 /* Cleans the input line and adds the current text to the history */
1154 void
1155 new_input (WInput *in)
1157 if (in->buffer)
1158 push_history (in, in->buffer);
1159 in->need_push = 1;
1160 in->buffer [0] = 0;
1161 in->point = 0;
1162 in->mark = 0;
1163 free_completions (in);
1164 update_input (in, 0);
1167 static cb_ret_t
1168 insert_char (WInput *in, int c_code)
1170 size_t i;
1172 if (c_code == -1)
1173 return MSG_NOT_HANDLED;
1175 in->need_push = 1;
1176 if (strlen (in->buffer)+1 == (size_t) in->current_max_len){
1177 /* Expand the buffer */
1178 char *narea = g_realloc (in->buffer, in->current_max_len + in->field_len);
1179 if (narea){
1180 in->buffer = narea;
1181 in->current_max_len += in->field_len;
1184 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
1185 size_t l = strlen (&in->buffer [in->point]);
1186 for (i = l+1; i > 0; i--)
1187 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1188 in->buffer [in->point] = c_code;
1189 in->point++;
1191 return MSG_HANDLED;
1194 static void
1195 beginning_of_line (WInput *in)
1197 in->point = 0;
1200 static void
1201 end_of_line (WInput *in)
1203 in->point = strlen (in->buffer);
1206 static void
1207 backward_char (WInput *in)
1209 if (in->point)
1210 in->point--;
1213 static void
1214 forward_char (WInput *in)
1216 if (in->buffer [in->point])
1217 in->point++;
1220 static void
1221 forward_word (WInput * in)
1223 char *p = in->buffer + in->point;
1225 while (*p
1226 && (isspace ((unsigned char) *p)
1227 || ispunct ((unsigned char) *p)))
1228 p++;
1229 while (*p && isalnum ((unsigned char) *p))
1230 p++;
1231 in->point = p - in->buffer;
1234 static void
1235 backward_word (WInput *in)
1237 char *p = in->buffer + in->point;
1239 while (p - 1 > in->buffer - 1 && (isspace ((unsigned char) *(p - 1))
1240 || ispunct ((unsigned char)
1241 *(p - 1))))
1242 p--;
1243 while (p - 1 > in->buffer - 1 && isalnum ((unsigned char) *(p - 1)))
1244 p--;
1245 in->point = p - in->buffer;
1248 static void
1249 key_left (WInput *in)
1251 backward_char (in);
1254 static void
1255 key_ctrl_left (WInput *in)
1257 backward_word (in);
1260 static void
1261 key_right (WInput *in)
1263 forward_char (in);
1266 static void
1267 key_ctrl_right (WInput *in)
1269 forward_word (in);
1271 static void
1272 backward_delete (WInput *in)
1274 int i;
1276 if (!in->point)
1277 return;
1278 for (i = in->point; in->buffer [i-1]; i++)
1279 in->buffer [i-1] = in->buffer [i];
1280 in->need_push = 1;
1281 in->point--;
1284 static void
1285 delete_char (WInput *in)
1287 int i;
1289 for (i = in->point; in->buffer [i]; i++)
1290 in->buffer [i] = in->buffer [i+1];
1291 in->need_push = 1;
1294 static void
1295 copy_region (WInput *in, int x_first, int x_last)
1297 int first = min (x_first, x_last);
1298 int last = max (x_first, x_last);
1300 if (last == first)
1301 return;
1303 g_free (kill_buffer);
1305 kill_buffer = g_strndup(in->buffer+first,last-first);
1308 static void
1309 delete_region (WInput *in, int x_first, int x_last)
1311 int first = min (x_first, x_last);
1312 int last = max (x_first, x_last);
1313 size_t len = strlen (&in->buffer [last]) + 1;
1315 in->point = first;
1316 in->mark = first;
1317 memmove (&in->buffer [first], &in->buffer [last], len);
1318 in->need_push = 1;
1321 static void
1322 kill_word (WInput *in)
1324 int old_point = in->point;
1325 int new_point;
1327 forward_word (in);
1328 new_point = in->point;
1329 in->point = old_point;
1331 copy_region (in, old_point, new_point);
1332 delete_region (in, old_point, new_point);
1333 in->need_push = 1;
1336 static void
1337 back_kill_word (WInput *in)
1339 int old_point = in->point;
1340 int new_point;
1342 backward_word (in);
1343 new_point = in->point;
1344 in->point = old_point;
1346 copy_region (in, old_point, new_point);
1347 delete_region (in, old_point, new_point);
1348 in->need_push = 1;
1351 static void
1352 set_mark (WInput *in)
1354 in->mark = in->point;
1357 static void
1358 kill_save (WInput *in)
1360 copy_region (in, in->mark, in->point);
1363 static void
1364 kill_region (WInput *in)
1366 kill_save (in);
1367 delete_region (in, in->point, in->mark);
1370 static void
1371 yank (WInput *in)
1373 char *p;
1375 if (!kill_buffer)
1376 return;
1377 for (p = kill_buffer; *p; p++)
1378 insert_char (in, *p);
1381 static void
1382 kill_line (WInput *in)
1384 g_free (kill_buffer);
1385 kill_buffer = g_strdup (&in->buffer [in->point]);
1386 in->buffer [in->point] = 0;
1389 void
1390 assign_text (WInput *in, const char *text)
1392 free_completions (in);
1393 g_free (in->buffer);
1394 in->buffer = g_strdup (text); /* was in->buffer->text */
1395 in->current_max_len = strlen (in->buffer) + 1;
1396 in->point = strlen (in->buffer);
1397 in->mark = 0;
1398 in->need_push = 1;
1401 static void
1402 hist_prev (WInput *in)
1404 if (!in->history)
1405 return;
1407 if (in->need_push) {
1408 switch (push_history (in, in->buffer)) {
1409 case 2:
1410 in->history = g_list_previous (in->history);
1411 break;
1412 case 1:
1413 if (in->history->prev)
1414 in->history = g_list_previous (in->history);
1415 break;
1416 case 0:
1417 break;
1419 } else if (in->history->prev)
1420 in->history = g_list_previous (in->history);
1421 else
1422 return;
1423 assign_text (in, (char *) in->history->data);
1424 in->need_push = 0;
1427 static void
1428 hist_next (WInput *in)
1430 if (in->need_push) {
1431 switch (push_history (in, in->buffer)) {
1432 case 2:
1433 assign_text (in, "");
1434 return;
1435 case 0:
1436 return;
1440 if (!in->history)
1441 return;
1443 if (!in->history->next) {
1444 assign_text (in, "");
1445 return;
1448 in->history = g_list_next (in->history);
1449 assign_text (in, (char *) in->history->data);
1450 in->need_push = 0;
1453 static const struct {
1454 int key_code;
1455 void (*fn)(WInput *in);
1456 } input_map [] = {
1457 /* Motion */
1458 { XCTRL('a'), beginning_of_line },
1459 { KEY_HOME, beginning_of_line },
1460 { KEY_A1, beginning_of_line },
1461 { ALT ('<'), beginning_of_line },
1462 { XCTRL('e'), end_of_line },
1463 { KEY_END, end_of_line },
1464 { KEY_C1, end_of_line },
1465 { ALT ('>'), end_of_line },
1466 { KEY_LEFT, key_left },
1467 { KEY_LEFT | KEY_M_CTRL, key_ctrl_left },
1468 { XCTRL('b'), backward_char },
1469 { ALT('b'), backward_word },
1470 { KEY_RIGHT, key_right },
1471 { KEY_RIGHT | KEY_M_CTRL, key_ctrl_right },
1472 { XCTRL('f'), forward_char },
1473 { ALT('f'), forward_word },
1475 /* Editing */
1476 { KEY_BACKSPACE, backward_delete },
1477 { KEY_DC, delete_char },
1478 { ALT('d'), kill_word },
1479 { ALT(KEY_BACKSPACE), back_kill_word },
1481 /* Region manipulation */
1482 { 0, set_mark },
1483 { XCTRL('w'), kill_region },
1484 { ALT('w'), kill_save },
1485 { XCTRL('y'), yank },
1486 { XCTRL('k'), kill_line },
1488 /* History */
1489 { ALT('p'), hist_prev },
1490 { ALT('n'), hist_next },
1491 { ALT('h'), do_show_hist },
1493 /* Completion */
1494 { ALT('\t'), complete },
1496 { 0, 0 }
1499 /* This function is a test for a special input key used in complete.c */
1500 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1501 and 2 if it is a complete key */
1503 is_in_input_map (WInput *in, int c_code)
1505 int i;
1507 (void) in;
1509 for (i = 0; input_map [i].fn; i++)
1510 if (c_code == input_map [i].key_code) {
1511 if (input_map [i].fn == complete)
1512 return 2;
1513 else
1514 return 1;
1516 return 0;
1519 static void
1520 port_region_marked_for_delete (WInput *in)
1522 *in->buffer = 0;
1523 in->point = 0;
1524 in->first = 0;
1527 cb_ret_t
1528 handle_char (WInput *in, int c_code)
1530 cb_ret_t v;
1531 int i;
1533 v = MSG_NOT_HANDLED;
1535 if (quote){
1536 free_completions (in);
1537 v = insert_char (in, c_code);
1538 update_input (in, 1);
1539 quote = 0;
1540 return v;
1543 for (i = 0; input_map [i].fn; i++){
1544 if (c_code == input_map [i].key_code){
1545 if (input_map [i].fn != complete)
1546 free_completions (in);
1547 (*input_map [i].fn)(in);
1548 v = MSG_HANDLED;
1549 break;
1552 if (!input_map [i].fn){
1553 if (c_code > 255 || !is_printable (c_code))
1554 return MSG_NOT_HANDLED;
1555 if (in->first){
1556 port_region_marked_for_delete (in);
1558 free_completions (in);
1559 v = insert_char (in, c_code);
1561 update_input (in, 1);
1562 return v;
1565 /* Inserts text in input line */
1566 void
1567 stuff (WInput *in, const char *text, int insert_extra_space)
1569 input_disable_update (in);
1570 while (*text)
1571 handle_char (in, *text++);
1572 if (insert_extra_space)
1573 handle_char (in, ' ');
1574 input_enable_update (in);
1575 update_input (in, 1);
1578 void
1579 input_set_point (WInput *in, int pos)
1581 if (pos > in->current_max_len)
1582 pos = in->current_max_len;
1583 if (pos != in->point)
1584 free_completions (in);
1585 in->point = pos;
1586 update_input (in, 1);
1589 cb_ret_t
1590 input_callback (Widget *w, widget_msg_t msg, int parm)
1592 WInput *in = (WInput *) w;
1593 cb_ret_t v;
1595 switch (msg) {
1596 case WIDGET_KEY:
1597 if (parm == XCTRL ('q')) {
1598 quote = 1;
1599 v = handle_char (in, ascii_alpha_to_cntrl (mi_getch ()));
1600 quote = 0;
1601 return v;
1604 /* Keys we want others to handle */
1605 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1606 || parm == KEY_F (10) || parm == XCTRL ('g') || parm == '\n')
1607 return MSG_NOT_HANDLED;
1609 /* When pasting multiline text, insert literal Enter */
1610 if ((parm & ~KEY_M_MASK) == '\n') {
1611 quote = 1;
1612 v = handle_char (in, '\n');
1613 quote = 0;
1614 return v;
1617 return handle_char (in, parm);
1619 case WIDGET_FOCUS:
1620 case WIDGET_UNFOCUS:
1621 case WIDGET_DRAW:
1622 update_input (in, 0);
1623 return MSG_HANDLED;
1625 case WIDGET_CURSOR:
1626 widget_move (&in->widget, 0, in->point - in->first_shown);
1627 return MSG_HANDLED;
1629 case WIDGET_DESTROY:
1630 input_destroy (in);
1631 return MSG_HANDLED;
1633 default:
1634 return default_proc (msg, parm);
1638 static int
1639 input_event (Gpm_Event * event, void *data)
1641 WInput *in = data;
1643 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1644 dlg_select_widget (in);
1646 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1647 && should_show_history_button (in)) {
1648 do_show_hist (in);
1649 } else {
1650 in->point = strlen (in->buffer);
1651 if (event->x - in->first_shown - 1 < in->point)
1652 in->point = event->x - in->first_shown - 1;
1653 if (in->point < 0)
1654 in->point = 0;
1656 update_input (in, 1);
1658 return MOU_NORMAL;
1661 WInput *
1662 input_new (int y, int x, int color, int len, const char *def_text,
1663 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
1665 WInput *in = g_new (WInput, 1);
1666 int initial_buffer_len;
1668 init_widget (&in->widget, y, x, 1, len, input_callback, input_event);
1670 /* history setup */
1671 in->history = NULL;
1672 in->history_name = 0;
1673 if (histname) {
1674 if (*histname) {
1675 in->history_name = g_strdup (histname);
1676 in->history = history_get (histname);
1680 if (!def_text)
1681 def_text = "";
1683 if (def_text == INPUT_LAST_TEXT) {
1684 def_text = "";
1685 if (in->history)
1686 if (in->history->data)
1687 def_text = (char *) in->history->data;
1689 initial_buffer_len = 1 + max ((size_t) len, strlen (def_text));
1690 in->widget.options |= W_IS_INPUT;
1691 in->completions = NULL;
1692 in->completion_flags = completion_flags;
1693 in->current_max_len = initial_buffer_len;
1694 in->buffer = g_malloc (initial_buffer_len);
1695 in->color = color;
1696 in->field_len = len;
1697 in->first = 1;
1698 in->first_shown = 0;
1699 in->disable_update = 0;
1700 in->mark = 0;
1701 in->need_push = 1;
1702 in->is_password = 0;
1704 strcpy (in->buffer, def_text);
1705 in->point = strlen (in->buffer);
1706 return in;
1710 /* Listbox widget */
1712 /* Should draw the scrollbar, but currently draws only
1713 * indications that there is more information
1715 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1717 static void
1718 listbox_drawscroll (WListbox *l)
1720 int line;
1721 int i, top;
1722 int max_line = l->height-1;
1724 /* Are we at the top? */
1725 widget_move (&l->widget, 0, l->width);
1726 if (l->list == l->top)
1727 one_vline ();
1728 else
1729 addch ('^');
1731 /* Are we at the bottom? */
1732 widget_move (&l->widget, max_line, l->width);
1733 top = listbox_cdiff (l->list, l->top);
1734 if ((top + l->height == l->count) || l->height >= l->count)
1735 one_vline ();
1736 else
1737 addch ('v');
1739 /* Now draw the nice relative pointer */
1740 if (l->count)
1741 line = 1+ ((l->pos * (l->height-2)) / l->count);
1742 else
1743 line = 0;
1745 for (i = 1; i < max_line; i++){
1746 widget_move (&l->widget, i, l->width);
1747 if (i != line)
1748 one_vline ();
1749 else
1750 addch ('*');
1754 static void
1755 listbox_draw (WListbox *l, int focused)
1757 WLEntry *e;
1758 int i;
1759 int sel_line;
1760 Dlg_head *h = l->widget.parent;
1761 int normalc = DLG_NORMALC (h);
1762 int selc;
1763 const char *text;
1765 if (focused){
1766 selc = DLG_FOCUSC (h);
1767 } else {
1768 selc = DLG_HOT_FOCUSC (h);
1770 sel_line = -1;
1772 for (e = l->top, i = 0; (i < l->height); i++){
1774 /* Display the entry */
1775 if (e == l->current && sel_line == -1){
1776 sel_line = i;
1777 attrset (selc);
1778 } else
1779 attrset (normalc);
1781 widget_move (&l->widget, i, 0);
1783 if ((i > 0 && e == l->list) || !l->list)
1784 text = "";
1785 else {
1786 text = e->text;
1787 e = e->next;
1789 tty_printf (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1791 l->cursor_y = sel_line;
1792 if (!l->scrollbar)
1793 return;
1794 attrset (normalc);
1795 listbox_drawscroll (l);
1798 /* Returns the number of items between s and e,
1799 must be on the same linked list */
1800 static int
1801 listbox_cdiff (WLEntry *s, WLEntry *e)
1803 int count;
1805 for (count = 0; s != e; count++)
1806 s = s->next;
1807 return count;
1810 static WLEntry *
1811 listbox_check_hotkey (WListbox *l, int key)
1813 int i;
1814 WLEntry *e;
1816 i = 0;
1817 e = l->list;
1818 if (!e)
1819 return NULL;
1821 while (1) {
1823 /* If we didn't find anything, return */
1824 if (i && e == l->list)
1825 return NULL;
1827 if (e->hotkey == key)
1828 return e;
1830 i++;
1831 e = e->next;
1835 /* Used only for display updating, for avoiding line at a time scroll */
1836 void
1837 listbox_select_last (WListbox *l, int set_top)
1839 if (l->list){
1840 l->current = l->list->prev;
1841 l->pos = l->count - 1;
1842 if (set_top)
1843 l->top = l->list->prev;
1847 void
1848 listbox_remove_list (WListbox *l)
1850 WLEntry *p, *q;
1852 if (!l->count)
1853 return;
1855 p = l->list;
1857 while (l->count--) {
1858 q = p->next;
1859 g_free (p->text);
1860 g_free (p);
1861 p = q;
1863 l->pos = l->count = 0;
1864 l->list = l->top = l->current = 0;
1868 * bor 30.10.96: added force flag to remove *last* entry as well
1869 * bor 30.10.96: corrected selection bug if last entry was removed
1872 void
1873 listbox_remove_current (WListbox *l, int force)
1875 WLEntry *p;
1877 /* Ok, note: this won't allow for emtpy lists */
1878 if (!force && (!l->count || l->count == 1))
1879 return;
1881 l->count--;
1882 p = l->current;
1884 if (l->count) {
1885 l->current->next->prev = l->current->prev;
1886 l->current->prev->next = l->current->next;
1887 if (p->next == l->list) {
1888 l->current = p->prev;
1889 l->pos--;
1891 else
1892 l->current = p->next;
1894 if (p == l->list)
1895 l->list = l->top = p->next;
1896 } else {
1897 l->pos = 0;
1898 l->list = l->top = l->current = 0;
1901 g_free (p->text);
1902 g_free (p);
1905 /* Makes *e the selected entry (sets current and pos) */
1906 void
1907 listbox_select_entry (WListbox *l, WLEntry *dest)
1909 WLEntry *e;
1910 int pos;
1911 int top_seen;
1913 top_seen = 0;
1915 /* Special case */
1916 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1918 if (e == l->top)
1919 top_seen = 1;
1921 if (e == dest){
1922 l->current = e;
1923 if (top_seen){
1924 while (listbox_cdiff (l->top, l->current) >= l->height)
1925 l->top = l->top->next;
1926 } else {
1927 l->top = l->current;
1929 l->pos = pos;
1930 return;
1933 /* If we are unable to find it, set decent values */
1934 l->current = l->top = l->list;
1935 l->pos = 0;
1938 /* Selects from base the pos element */
1939 static WLEntry *
1940 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1942 WLEntry *last = l->list->prev;
1944 if (base == last)
1945 return last;
1946 while (pos--){
1947 base = base->next;
1948 if (base == last)
1949 break;
1951 return base;
1954 static inline cb_ret_t
1955 listbox_back (WListbox *l)
1957 if (l->pos){
1958 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1959 return MSG_HANDLED;
1961 return MSG_NOT_HANDLED;
1964 /* Return MSG_HANDLED if we want a redraw */
1965 static cb_ret_t
1966 listbox_key (WListbox *l, int key)
1968 int i;
1969 int j = 0;
1971 if (!l->list)
1972 return MSG_NOT_HANDLED;
1974 switch (key){
1975 case KEY_HOME:
1976 case KEY_A1:
1977 case ALT ('<'):
1978 l->current = l->top = l->list;
1979 l->pos = 0;
1980 return MSG_HANDLED;
1982 case KEY_END:
1983 case KEY_C1:
1984 case ALT ('>'):
1985 l->current = l->top = l->list->prev;
1986 for (i = min (l->height - 1, l->count - 1); i; i--)
1987 l->top = l->top->prev;
1988 l->pos = l->count - 1;
1989 return MSG_HANDLED;
1991 case XCTRL('p'):
1992 case KEY_UP:
1993 listbox_back (l);
1994 return MSG_HANDLED;
1996 case XCTRL('n'):
1997 case KEY_DOWN:
1998 listbox_fwd (l);
1999 return MSG_HANDLED;
2001 case KEY_NPAGE:
2002 case XCTRL('v'):
2003 for (i = 0; i < l->height-1; i++)
2004 j |= listbox_fwd (l);
2005 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2007 case KEY_PPAGE:
2008 case ALT('v'):
2009 for (i = 0; i < l->height-1; i++)
2010 j |= listbox_back (l);
2011 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2013 return MSG_NOT_HANDLED;
2016 static void
2017 listbox_destroy (WListbox *l)
2019 WLEntry *n, *p = l->list;
2020 int i;
2022 for (i = 0; i < l->count; i++){
2023 n = p->next;
2024 g_free (p->text);
2025 g_free (p);
2026 p = n;
2030 static cb_ret_t
2031 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2033 WListbox *l = (WListbox *) w;
2034 cb_ret_t ret_code;
2035 WLEntry *e;
2036 Dlg_head *h = l->widget.parent;
2038 switch (msg) {
2039 case WIDGET_INIT:
2040 return MSG_HANDLED;
2042 case WIDGET_HOTKEY:
2043 if ((e = listbox_check_hotkey (l, parm)) != NULL) {
2044 int action;
2046 listbox_select_entry (l, e);
2048 if (l->cback)
2049 action = (*l->cback) (l);
2050 else
2051 action = LISTBOX_DONE;
2053 if (action == LISTBOX_DONE) {
2054 h->ret_value = B_ENTER;
2055 dlg_stop (h);
2057 return MSG_HANDLED;
2058 } else
2059 return MSG_NOT_HANDLED;
2061 case WIDGET_KEY:
2062 if ((ret_code = listbox_key (l, parm)))
2063 listbox_draw (l, 1);
2064 return ret_code;
2066 case WIDGET_CURSOR:
2067 widget_move (&l->widget, l->cursor_y, 0);
2068 return MSG_HANDLED;
2070 case WIDGET_FOCUS:
2071 case WIDGET_UNFOCUS:
2072 case WIDGET_DRAW:
2073 listbox_draw (l, msg != WIDGET_UNFOCUS);
2074 return MSG_HANDLED;
2076 case WIDGET_DESTROY:
2077 listbox_destroy (l);
2078 return MSG_HANDLED;
2080 default:
2081 return default_proc (msg, parm);
2085 static int
2086 listbox_event (Gpm_Event *event, void *data)
2088 WListbox *l = data;
2089 Widget *w = data;
2090 int i;
2092 Dlg_head *h = l->widget.parent;
2094 /* Single click */
2095 if (event->type & GPM_DOWN)
2096 dlg_select_widget (l);
2097 if (!l->list)
2098 return MOU_NORMAL;
2099 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2100 if (event->x < 0 || event->x >= l->width)
2101 return MOU_REPEAT;
2102 if (event->y < 1)
2103 for (i = -event->y; i >= 0; i--)
2104 listbox_back (l);
2105 else if (event->y > l->height)
2106 for (i = event->y - l->height; i > 0; i--)
2107 listbox_fwd (l);
2108 else
2109 listbox_select_entry (l,
2110 listbox_select_pos (l, l->top,
2111 event->y - 1));
2113 /* We need to refresh ourselves since the dialog manager doesn't */
2114 /* know about this event */
2115 listbox_callback (w, WIDGET_DRAW, 0);
2116 mc_refresh ();
2117 return MOU_REPEAT;
2120 /* Double click */
2121 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2122 int action;
2124 if (event->x < 0 || event->x >= l->width)
2125 return MOU_NORMAL;
2126 if (event->y < 1 || event->y > l->height)
2127 return MOU_NORMAL;
2129 dlg_select_widget (l);
2130 listbox_select_entry (l,
2131 listbox_select_pos (l, l->top,
2132 event->y - 1));
2134 if (l->cback)
2135 action = (*l->cback) (l);
2136 else
2137 action = LISTBOX_DONE;
2139 if (action == LISTBOX_DONE) {
2140 h->ret_value = B_ENTER;
2141 dlg_stop (h);
2142 return MOU_NORMAL;
2145 return MOU_NORMAL;
2148 WListbox *
2149 listbox_new (int y, int x, int width, int height, lcback callback)
2151 WListbox *l = g_new (WListbox, 1);
2153 init_widget (&l->widget, y, x, height, width,
2154 listbox_callback, listbox_event);
2156 l->list = l->top = l->current = 0;
2157 l->pos = 0;
2158 l->width = width;
2159 if (height <= 0)
2160 l->height = 1;
2161 else
2162 l->height = height;
2163 l->count = 0;
2164 l->cback = callback;
2165 l->allow_duplicates = 1;
2166 l->scrollbar = slow_terminal ? 0 : 1;
2167 widget_want_hotkey (l->widget, 1);
2169 return l;
2172 /* Listbox item adding function. They still lack a lot of functionality */
2173 /* any takers? */
2174 /* 1.11.96 bor: added pos argument to control placement of new entry */
2175 static void
2176 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2178 if (!l->list){
2179 l->list = e;
2180 l->top = e;
2181 l->current = e;
2182 e->next = l->list;
2183 e->prev = l->list;
2184 } else if (pos == LISTBOX_APPEND_AT_END) {
2185 e->next = l->list;
2186 e->prev = l->list->prev;
2187 l->list->prev->next = e;
2188 l->list->prev = e;
2189 } else if (pos == LISTBOX_APPEND_BEFORE){
2190 e->next = l->current;
2191 e->prev = l->current->prev;
2192 l->current->prev->next = e;
2193 l->current->prev = e;
2194 if (l->list == l->current) { /* move list one position down */
2195 l->list = e;
2196 l->top = e;
2198 } else if (pos == LISTBOX_APPEND_AFTER) {
2199 e->prev = l->current;
2200 e->next = l->current->next;
2201 l->current->next->prev = e;
2202 l->current->next = e;
2203 } else if (pos == LISTBOX_APPEND_SORTED) {
2204 WLEntry *w = l->list;
2206 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2207 w = w->next;
2208 if (w->next == l->list) {
2209 e->prev = w;
2210 e->next = l->list;
2211 w->next = e;
2212 l->list->prev = e;
2213 } else {
2214 e->next = w;
2215 e->prev = w->prev;
2216 w->prev->next = e;
2217 w->prev = e;
2220 l->count++;
2223 char *
2224 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2225 const char *text, void *data)
2227 WLEntry *entry;
2229 if (!l)
2230 return NULL;
2232 if (!l->allow_duplicates)
2233 if (listbox_search_text (l, text))
2234 return NULL;
2236 entry = g_new (WLEntry, 1);
2237 entry->text = g_strdup (text);
2238 entry->data = data;
2239 entry->hotkey = hotkey;
2241 listbox_append_item (l, entry, pos);
2243 return entry->text;
2246 /* Selects the nth entry in the listbox */
2247 void
2248 listbox_select_by_number (WListbox *l, int n)
2250 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2253 WLEntry *
2254 listbox_search_text (WListbox *l, const char *text)
2256 WLEntry *e;
2258 e = l->list;
2259 if (!e)
2260 return NULL;
2262 do {
2263 if(!strcmp (e->text, text))
2264 return e;
2265 e = e->next;
2266 } while (e!=l->list);
2268 return NULL;
2271 /* Returns the current string text as well as the associated extra data */
2272 void
2273 listbox_get_current (WListbox *l, char **string, char **extra)
2275 if (!l->current){
2276 *string = 0;
2277 *extra = 0;
2279 if (string && l->current)
2280 *string = l->current->text;
2281 if (extra && l->current)
2282 *extra = l->current->data;
2285 /* returns TRUE if a function has been called, FALSE otherwise. */
2286 static bool
2287 buttonbar_call (WButtonBar *bb, int i)
2289 switch (bb->labels[i].tag) {
2290 case BBFUNC_NONE:
2291 break;
2292 case BBFUNC_VOID:
2293 bb->labels[i].u.fn_void ();
2294 return TRUE;
2295 case BBFUNC_PTR:
2296 bb->labels[i].u.fn_ptr (bb->labels[i].data);
2297 return TRUE;
2299 return FALSE;
2303 static cb_ret_t
2304 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2306 WButtonBar *bb = (WButtonBar *) w;
2307 int i;
2309 switch (msg) {
2310 case WIDGET_FOCUS:
2311 return MSG_NOT_HANDLED;
2313 case WIDGET_HOTKEY:
2314 for (i = 0; i < 10; i++) {
2315 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2316 return MSG_HANDLED;
2318 return MSG_NOT_HANDLED;
2320 case WIDGET_DRAW:
2321 if (!bb->visible)
2322 return MSG_HANDLED;
2323 widget_move (&bb->widget, 0, 0);
2324 attrset (DEFAULT_COLOR);
2325 tty_printf ("%-*s", bb->widget.cols, "");
2326 for (i = 0; i < COLS / 8 && i < 10; i++) {
2327 widget_move (&bb->widget, 0, i * 8);
2328 attrset (DEFAULT_COLOR);
2329 tty_printf ("%d", i + 1);
2330 attrset (SELECTED_COLOR);
2331 tty_printf ("%-*s", ((i + 1) * 8 == COLS ? 5 : 6),
2332 bb->labels[i].text ? bb->labels[i].text : "");
2333 attrset (DEFAULT_COLOR);
2335 attrset (SELECTED_COLOR);
2336 return MSG_HANDLED;
2338 case WIDGET_DESTROY:
2339 for (i = 0; i < 10; i++)
2340 g_free (bb->labels[i].text);
2341 return MSG_HANDLED;
2343 default:
2344 return default_proc (msg, parm);
2348 static int
2349 buttonbar_event (Gpm_Event *event, void *data)
2351 WButtonBar *bb = data;
2352 int button;
2354 if (!(event->type & GPM_UP))
2355 return MOU_NORMAL;
2356 if (event->y == 2)
2357 return MOU_NORMAL;
2358 button = event->x / 8;
2359 if (button < 10)
2360 buttonbar_call (bb, button);
2361 return MOU_NORMAL;
2364 WButtonBar *
2365 buttonbar_new (int visible)
2367 int i;
2368 WButtonBar *bb = g_new (WButtonBar, 1);
2370 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2371 buttonbar_callback, buttonbar_event);
2373 bb->visible = visible;
2374 for (i = 0; i < 10; i++){
2375 bb->labels[i].text = NULL;
2376 bb->labels[i].tag = BBFUNC_NONE;
2378 widget_want_hotkey (bb->widget, 1);
2379 widget_want_cursor (bb->widget, 0);
2381 return bb;
2384 static void
2385 set_label_text (WButtonBar * bb, int index, const char *text)
2387 g_free (bb->labels[index - 1].text);
2389 bb->labels[index - 1].text = g_strdup (text);
2392 /* Find ButtonBar widget in the dialog */
2393 WButtonBar *
2394 find_buttonbar (Dlg_head *h)
2396 WButtonBar *bb;
2398 bb = (WButtonBar *) find_widget_type (h, buttonbar_callback);
2399 return bb;
2402 void
2403 buttonbar_clear_label (Dlg_head *h, int idx)
2405 WButtonBar *bb = find_buttonbar (h);
2407 if (!bb)
2408 return;
2410 set_label_text (bb, idx, "");
2411 bb->labels[idx - 1].tag = BBFUNC_NONE;
2414 void
2415 buttonbar_set_label_data (Dlg_head *h, int idx, const char *text, buttonbarfn cback,
2416 void *data)
2418 WButtonBar *bb = find_buttonbar (h);
2420 if (!bb)
2421 return;
2423 assert (cback != (buttonbarfn) 0);
2424 set_label_text (bb, idx, text);
2425 bb->labels[idx - 1].tag = BBFUNC_PTR;
2426 bb->labels[idx - 1].u.fn_ptr = cback;
2427 bb->labels[idx - 1].data = data;
2430 void
2431 buttonbar_set_label (Dlg_head *h, int idx, const char *text, voidfn cback)
2433 WButtonBar *bb = find_buttonbar (h);
2435 if (!bb)
2436 return;
2438 assert (cback != (voidfn) 0);
2439 set_label_text (bb, idx, text);
2440 bb->labels[idx - 1].tag = BBFUNC_VOID;
2441 bb->labels[idx - 1].u.fn_void = cback;
2444 void
2445 buttonbar_set_visible (WButtonBar *bb, bool visible)
2447 bb->visible = visible;
2450 void
2451 buttonbar_redraw (Dlg_head *h)
2453 WButtonBar *bb = find_buttonbar (h);
2455 if (!bb)
2456 return;
2458 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2461 static cb_ret_t
2462 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2464 WGroupbox *g = (WGroupbox *) w;
2466 switch (msg) {
2467 case WIDGET_INIT:
2468 return MSG_HANDLED;
2470 case WIDGET_FOCUS:
2471 return MSG_NOT_HANDLED;
2473 case WIDGET_DRAW:
2474 attrset (COLOR_NORMAL);
2475 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2476 g->widget.x - g->widget.parent->x, g->widget.lines,
2477 g->widget.cols);
2479 attrset (COLOR_HOT_NORMAL);
2480 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2481 g->widget.x - g->widget.parent->x + 1);
2482 addstr (g->title);
2483 return MSG_HANDLED;
2485 case WIDGET_DESTROY:
2486 g_free (g->title);
2487 return MSG_HANDLED;
2489 default:
2490 return default_proc (msg, parm);
2494 WGroupbox *
2495 groupbox_new (int x, int y, int width, int height, const char *title)
2497 WGroupbox *g = g_new (WGroupbox, 1);
2499 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2501 g->widget.options &= ~W_WANT_CURSOR;
2502 widget_want_hotkey (g->widget, 0);
2504 /* Strip existing spaces, add one space before and after the title */
2505 if (title) {
2506 char *t;
2507 t = g_strstrip (g_strdup (title));
2508 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2509 g_free (t);
2512 return g;