Completely removed MHL stuff
[midnight-commander.git] / src / widget.c
blobf0eb95bbe4bf9242a99e22f50e2f3db3de812745
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 "global.h"
38 #include "tty.h"
39 #include "color.h"
40 #include "mouse.h"
41 #include "dialog.h"
42 #include "widget.h"
43 #include "win.h"
44 #include "key.h" /* XCTRL and ALT macros */
45 #include "profile.h" /* for history loading and saving */
46 #include "wtools.h" /* For common_dialog_repaint() */
47 #include "main.h" /* for `slow_terminal' */
49 #define HISTORY_FILE_NAME ".mc/history"
51 struct WButtonBar {
52 Widget widget;
53 int visible; /* Is it visible? */
54 struct {
55 char *text;
56 enum { BBFUNC_NONE, BBFUNC_VOID, BBFUNC_PTR } tag;
57 union {
58 voidfn fn_void;
59 buttonbarfn fn_ptr;
60 } u;
61 void *data;
62 } labels [10];
65 static int button_event (Gpm_Event *event, void *);
67 int quote = 0;
69 static void
70 widget_selectcolor (Widget *w, gboolean focused, gboolean hotkey)
72 Dlg_head *h = w->parent;
74 attrset (hotkey
75 ? (focused
76 ? DLG_HOT_FOCUSC (h)
77 : DLG_HOT_NORMALC (h))
78 : (focused
79 ? DLG_FOCUSC (h)
80 : DLG_NORMALC (h)));
83 static cb_ret_t
84 button_callback (Widget *w, widget_msg_t msg, int parm)
86 WButton *b = (WButton *) w;
87 char buf[BUF_SMALL];
88 int stop = 0;
89 int off = 0;
90 Dlg_head *h = b->widget.parent;
92 switch (msg) {
93 case WIDGET_HOTKEY:
95 * Don't let the default button steal Enter from the current
96 * button. This is a workaround for the flawed event model
97 * when hotkeys are sent to all widgets before the key is
98 * handled by the current widget.
100 if (parm == '\n' && h->current == &b->widget) {
101 button_callback (w, WIDGET_KEY, ' ');
102 return MSG_HANDLED;
105 if (parm == '\n' && b->flags == DEFPUSH_BUTTON) {
106 button_callback (w, WIDGET_KEY, ' ');
107 return MSG_HANDLED;
110 if (b->hotkey == tolower (parm)) {
111 button_callback (w, WIDGET_KEY, ' ');
112 return MSG_HANDLED;
115 return MSG_NOT_HANDLED;
117 case WIDGET_KEY:
118 if (parm != ' ' && parm != '\n')
119 return MSG_NOT_HANDLED;
121 if (b->callback)
122 stop = (*b->callback) (b->action);
123 if (!b->callback || stop) {
124 h->ret_value = b->action;
125 dlg_stop (h);
127 return MSG_HANDLED;
129 case WIDGET_CURSOR:
130 switch (b->flags) {
131 case DEFPUSH_BUTTON:
132 off = 3;
133 break;
134 case NORMAL_BUTTON:
135 off = 2;
136 break;
137 case NARROW_BUTTON:
138 off = 1;
139 break;
140 case HIDDEN_BUTTON:
141 default:
142 off = 0;
143 break;
145 widget_move (&b->widget, 0, b->hotpos + off);
146 return MSG_HANDLED;
148 case WIDGET_UNFOCUS:
149 case WIDGET_FOCUS:
150 case WIDGET_DRAW:
151 if (msg == WIDGET_UNFOCUS)
152 b->selected = 0;
153 else if (msg == WIDGET_FOCUS)
154 b->selected = 1;
156 switch (b->flags) {
157 case DEFPUSH_BUTTON:
158 g_snprintf (buf, sizeof (buf), "[< %s >]", b->text);
159 off = 3;
160 break;
161 case NORMAL_BUTTON:
162 g_snprintf (buf, sizeof (buf), "[ %s ]", b->text);
163 off = 2;
164 break;
165 case NARROW_BUTTON:
166 g_snprintf (buf, sizeof (buf), "[%s]", b->text);
167 off = 1;
168 break;
169 case HIDDEN_BUTTON:
170 default:
171 buf[0] = '\0';
172 off = 0;
173 break;
176 widget_selectcolor (w, b->selected, FALSE);
177 widget_move (w, 0, 0);
179 addstr (buf);
181 if (b->hotpos >= 0) {
182 widget_selectcolor (w, b->selected, TRUE);
183 widget_move (w, 0, b->hotpos + off);
184 addch ((unsigned char) b->text[b->hotpos]);
186 return MSG_HANDLED;
188 case WIDGET_DESTROY:
189 g_free (b->text);
190 return MSG_HANDLED;
192 default:
193 return default_proc (msg, parm);
197 static int
198 button_event (Gpm_Event *event, void *data)
200 WButton *b = data;
202 if (event->type & (GPM_DOWN|GPM_UP)){
203 Dlg_head *h=b->widget.parent;
204 dlg_select_widget (b);
205 if (event->type & GPM_UP){
206 button_callback ((Widget *) data, WIDGET_KEY, ' ');
207 (*h->callback) (h, DLG_POST_KEY, ' ');
208 return MOU_NORMAL;
211 return MOU_NORMAL;
214 static int
215 button_len (const char *text, unsigned int flags)
217 int ret = strlen (text);
218 switch (flags){
219 case DEFPUSH_BUTTON:
220 ret += 6;
221 break;
222 case NORMAL_BUTTON:
223 ret += 4;
224 break;
225 case NARROW_BUTTON:
226 ret += 2;
227 break;
228 case HIDDEN_BUTTON:
229 default:
230 return 0;
232 return ret;
236 * Locate the hotkey and remove it from the button text. Assuming that
237 * the button text is g_malloc()ed, we can safely change and shorten it.
239 static void
240 button_scan_hotkey (WButton *b)
242 char *cp = strchr (b->text, '&');
244 if (cp != NULL && cp[1] != '\0') {
245 g_strlcpy (cp, cp + 1, strlen (cp));
246 b->hotkey = tolower ((unsigned char) *cp);
247 b->hotpos = cp - b->text;
251 WButton *
252 button_new (int y, int x, int action, int flags, const char *text,
253 bcback callback)
255 WButton *b = g_new (WButton, 1);
257 init_widget (&b->widget, y, x, 1, button_len (text, flags),
258 button_callback, button_event);
260 b->action = action;
261 b->flags = flags;
262 b->selected = 0;
263 b->text = g_strdup (text);
264 b->callback = callback;
265 widget_want_hotkey (b->widget, 1);
266 b->hotkey = 0;
267 b->hotpos = -1;
269 button_scan_hotkey(b);
270 return b;
273 const char *
274 button_get_text (WButton *b)
276 return b->text;
279 void
280 button_set_text (WButton *b, const char *text)
282 g_free (b->text);
283 b->text = g_strdup (text);
284 b->widget.cols = button_len (text, b->flags);
285 button_scan_hotkey(b);
286 dlg_redraw (b->widget.parent);
290 /* Radio button widget */
291 static int radio_event (Gpm_Event *event, void *);
293 static cb_ret_t
294 radio_callback (Widget *w, widget_msg_t msg, int parm)
296 WRadio *r = (WRadio *) w;
297 int i;
298 Dlg_head *h = r->widget.parent;
300 switch (msg) {
301 case WIDGET_HOTKEY:
303 int i, lp = tolower (parm);
304 const char *cp;
306 for (i = 0; i < r->count; i++) {
307 cp = strchr (r->texts[i], '&');
308 if (cp != NULL && cp[1] != '\0') {
309 int c = tolower ((unsigned char) cp[1]);
311 if (c != lp)
312 continue;
313 r->pos = i;
315 /* Take action */
316 radio_callback (w, WIDGET_KEY, ' ');
317 return MSG_HANDLED;
321 return MSG_NOT_HANDLED;
323 case WIDGET_KEY:
324 switch (parm) {
325 case ' ':
326 r->sel = r->pos;
327 (*h->callback) (h, DLG_ACTION, 0);
328 radio_callback (w, WIDGET_FOCUS, ' ');
329 return MSG_HANDLED;
331 case KEY_UP:
332 case KEY_LEFT:
333 if (r->pos > 0) {
334 r->pos--;
335 return MSG_HANDLED;
337 return MSG_NOT_HANDLED;
339 case KEY_DOWN:
340 case KEY_RIGHT:
341 if (r->count - 1 > r->pos) {
342 r->pos++;
343 return MSG_HANDLED;
346 return MSG_NOT_HANDLED;
348 case WIDGET_CURSOR:
349 (*h->callback) (h, DLG_ACTION, 0);
350 radio_callback (w, WIDGET_FOCUS, ' ');
351 widget_move (&r->widget, r->pos, 1);
352 return MSG_HANDLED;
354 case WIDGET_UNFOCUS:
355 case WIDGET_FOCUS:
356 case WIDGET_DRAW:
357 for (i = 0; i < r->count; i++) {
358 register const char *cp;
359 const gboolean focused = (i == r->pos && msg == WIDGET_FOCUS);
360 widget_selectcolor (w, focused, FALSE);
361 widget_move (&r->widget, i, 0);
363 tty_printf ("(%c) ", (r->sel == i) ? '*' : ' ');
364 for (cp = r->texts[i]; *cp; cp++) {
365 if (*cp == '&') {
366 widget_selectcolor (w, focused, TRUE);
367 addch (*++cp);
368 widget_selectcolor (w, focused, FALSE);
369 } else
370 addch (*cp);
373 return MSG_HANDLED;
375 default:
376 return default_proc (msg, parm);
380 static int
381 radio_event (Gpm_Event *event, void *data)
383 WRadio *r = data;
384 Widget *w = data;
386 if (event->type & (GPM_DOWN|GPM_UP)){
387 Dlg_head *h = r->widget.parent;
389 r->pos = event->y - 1;
390 dlg_select_widget (r);
391 if (event->type & GPM_UP){
392 radio_callback (w, WIDGET_KEY, ' ');
393 radio_callback (w, WIDGET_FOCUS, 0);
394 (*h->callback) (h, DLG_POST_KEY, ' ');
395 return MOU_NORMAL;
398 return MOU_NORMAL;
401 WRadio *
402 radio_new (int y, int x, int count, const char **texts)
404 WRadio *r = g_new (WRadio, 1);
405 int i, max, m;
407 /* Compute the longest string */
408 max = 0;
409 for (i = 0; i < count; i++){
410 m = strlen (texts [i]);
411 if (m > max)
412 max = m;
415 init_widget (&r->widget, y, x, count, max, radio_callback, radio_event);
416 r->state = 1;
417 r->pos = 0;
418 r->sel = 0;
419 r->count = count;
420 r->texts = texts;
421 widget_want_hotkey (r->widget, 1);
423 return r;
427 /* Checkbutton widget */
429 static int check_event (Gpm_Event *event, void *);
431 static cb_ret_t
432 check_callback (Widget *w, widget_msg_t msg, int parm)
434 WCheck *c = (WCheck *) w;
435 Dlg_head *h = c->widget.parent;
437 switch (msg) {
438 case WIDGET_HOTKEY:
439 if (c->hotkey == parm
440 || (c->hotkey >= 'a' && c->hotkey <= 'z'
441 && c->hotkey - 32 == parm)) {
442 check_callback (w, WIDGET_KEY, ' '); /* make action */
443 return MSG_HANDLED;
445 return MSG_NOT_HANDLED;
447 case WIDGET_KEY:
448 if (parm != ' ')
449 return MSG_NOT_HANDLED;
450 c->state ^= C_BOOL;
451 c->state ^= C_CHANGE;
452 (*h->callback) (h, DLG_ACTION, 0);
453 check_callback (w, WIDGET_FOCUS, ' ');
454 return MSG_HANDLED;
456 case WIDGET_CURSOR:
457 widget_move (&c->widget, 0, 1);
458 return MSG_HANDLED;
460 case WIDGET_FOCUS:
461 case WIDGET_UNFOCUS:
462 case WIDGET_DRAW:
463 widget_selectcolor (w, msg == WIDGET_FOCUS, FALSE);
464 widget_move (&c->widget, 0, 0);
465 tty_printf ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
467 if (c->hotpos >= 0) {
468 widget_selectcolor (w, msg == WIDGET_FOCUS, TRUE);
469 widget_move (&c->widget, 0, +c->hotpos + 4);
470 addch ((unsigned char) c->text[c->hotpos]);
472 return MSG_HANDLED;
474 case WIDGET_DESTROY:
475 g_free (c->text);
476 return MSG_HANDLED;
478 default:
479 return default_proc (msg, parm);
483 static int
484 check_event (Gpm_Event *event, void *data)
486 WCheck *c = data;
487 Widget *w = data;
489 if (event->type & (GPM_DOWN|GPM_UP)){
490 Dlg_head *h = c->widget.parent;
492 dlg_select_widget (c);
493 if (event->type & GPM_UP){
494 check_callback (w, WIDGET_KEY, ' ');
495 check_callback (w, WIDGET_FOCUS, 0);
496 (*h->callback) (h, DLG_POST_KEY, ' ');
497 return MOU_NORMAL;
500 return MOU_NORMAL;
503 WCheck *
504 check_new (int y, int x, int state, const char *text)
506 WCheck *c = g_new (WCheck, 1);
507 const char *s;
508 char *t;
510 init_widget (&c->widget, y, x, 1, strlen (text),
511 check_callback, check_event);
512 c->state = state ? C_BOOL : 0;
513 c->text = g_strdup (text);
514 c->hotkey = 0;
515 c->hotpos = -1;
516 widget_want_hotkey (c->widget, 1);
518 /* Scan for the hotkey */
519 for (s = text, t = c->text; *s; s++, t++){
520 if (*s != '&'){
521 *t = *s;
522 continue;
524 s++;
525 if (*s){
526 c->hotkey = tolower ((unsigned char) *s);
527 c->hotpos = t - c->text;
529 *t = *s;
531 *t = 0;
532 return c;
536 /* Label widget */
538 static cb_ret_t
539 label_callback (Widget *w, widget_msg_t msg, int parm)
541 WLabel *l = (WLabel *) w;
542 Dlg_head *h = l->widget.parent;
544 switch (msg) {
545 case WIDGET_INIT:
546 return MSG_HANDLED;
548 /* We don't want to get the focus */
549 case WIDGET_FOCUS:
550 return MSG_NOT_HANDLED;
552 case WIDGET_DRAW:
554 char *p = l->text, *q, c = 0;
555 int y = 0;
557 if (!l->text)
558 return MSG_HANDLED;
560 if (l->transparent)
561 attrset (DEFAULT_COLOR);
562 else
563 attrset (DLG_NORMALC (h));
564 for (;;) {
565 int xlen;
567 q = strchr (p, '\n');
568 if (q) {
569 c = *q;
570 *q = 0;
572 widget_move (&l->widget, y, 0);
573 tty_printf ("%s", p);
574 xlen = l->widget.cols - strlen (p);
575 if (xlen > 0)
576 tty_printf ("%*s", xlen, " ");
577 if (!q)
578 break;
579 *q = c;
580 p = q + 1;
581 y++;
583 return MSG_HANDLED;
586 case WIDGET_DESTROY:
587 g_free (l->text);
588 return MSG_HANDLED;
590 default:
591 return default_proc (msg, parm);
595 void
596 label_set_text (WLabel *label, const char *text)
598 int newcols = label->widget.cols;
600 if (label->text && text && !strcmp (label->text, text))
601 return; /* Flickering is not nice */
603 g_free (label->text);
605 if (text){
606 label->text = g_strdup (text);
607 if (label->auto_adjust_cols) {
608 newcols = strlen (text);
609 if (newcols > label->widget.cols)
610 label->widget.cols = newcols;
612 } else
613 label->text = 0;
615 if (label->widget.parent)
616 label_callback ((Widget *) label, WIDGET_DRAW, 0);
618 if (newcols < label->widget.cols)
619 label->widget.cols = newcols;
622 WLabel *
623 label_new (int y, int x, const char *text)
625 WLabel *l;
626 int width;
628 /* Multiline labels are immutable - no need to compute their sizes */
629 if (!text || strchr(text, '\n'))
630 width = 1;
631 else
632 width = strlen (text);
634 l = g_new (WLabel, 1);
635 init_widget (&l->widget, y, x, 1, width, label_callback, NULL);
636 l->text = text ? g_strdup (text) : 0;
637 l->auto_adjust_cols = 1;
638 l->transparent = 0;
639 widget_want_cursor (l->widget, 0);
640 return l;
644 /* Gauge widget (progress indicator) */
645 /* Currently width is hardcoded here for text mode */
646 #define gauge_len 47
648 static cb_ret_t
649 gauge_callback (Widget *w, widget_msg_t msg, int parm)
651 WGauge *g = (WGauge *) w;
652 Dlg_head *h = g->widget.parent;
654 if (msg == WIDGET_INIT)
655 return MSG_HANDLED;
657 /* We don't want to get the focus */
658 if (msg == WIDGET_FOCUS)
659 return MSG_NOT_HANDLED;
661 if (msg == WIDGET_DRAW){
662 widget_move (&g->widget, 0, 0);
663 attrset (DLG_NORMALC (h));
664 if (!g->shown)
665 tty_printf ("%*s", gauge_len, "");
666 else {
667 int percentage, columns;
668 long total = g->max, done = g->current;
670 if (total <= 0 || done < 0) {
671 done = 0;
672 total = 100;
674 if (done > total)
675 done = total;
676 while (total > 65535) {
677 total /= 256;
678 done /= 256;
680 percentage = (200 * done / total + 1) / 2;
681 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
682 addch ('[');
683 attrset (GAUGE_COLOR);
684 tty_printf ("%*s", (int) columns, "");
685 attrset (DLG_NORMALC (h));
686 tty_printf ("%*s] %3d%%", (int)(gauge_len - 7 - columns), "", (int) percentage);
688 return MSG_HANDLED;
690 return default_proc (msg, parm);
693 void
694 gauge_set_value (WGauge *g, int max, int current)
696 if (g->current == current && g->max == max)
697 return; /* Do not flicker */
698 if (max == 0)
699 max = 1; /* I do not like division by zero :) */
701 g->current = current;
702 g->max = max;
703 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
706 void
707 gauge_show (WGauge *g, int shown)
709 if (g->shown == shown)
710 return;
711 g->shown = shown;
712 gauge_callback ((Widget *) g, WIDGET_DRAW, 0);
715 WGauge *
716 gauge_new (int y, int x, int shown, int max, int current)
718 WGauge *g = g_new (WGauge, 1);
720 init_widget (&g->widget, y, x, 1, gauge_len, gauge_callback, NULL);
721 g->shown = shown;
722 if (max == 0)
723 max = 1; /* I do not like division by zero :) */
724 g->max = max;
725 g->current = current;
726 widget_want_cursor (g->widget, 0);
727 return g;
731 /* Input widget */
733 /* {{{ history button */
735 #define LARGE_HISTORY_BUTTON 1
737 #ifdef LARGE_HISTORY_BUTTON
738 # define HISTORY_BUTTON_WIDTH 3
739 #else
740 # define HISTORY_BUTTON_WIDTH 1
741 #endif
743 #define should_show_history_button(in) \
744 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
746 static void draw_history_button (WInput * in)
748 char c;
749 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
750 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
751 #ifdef LARGE_HISTORY_BUTTON
753 Dlg_head *h;
754 h = in->widget.parent;
755 #if 0
756 attrset (NORMALC); /* button has the same color as other buttons */
757 addstr ("[ ]");
758 attrset (HOT_NORMALC);
759 #else
760 attrset (NORMAL_COLOR);
761 addstr ("[ ]");
762 /* Too distracting: attrset (MARKED_COLOR); */
763 #endif
764 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
765 addch (c);
767 #else
768 attrset (MARKED_COLOR);
769 addch (c);
770 #endif
773 /* }}} history button */
776 /* Input widgets now have a global kill ring */
777 /* Pointer to killed data */
778 static char *kill_buffer = 0;
780 void
781 update_input (WInput *in, int clear_first)
783 int has_history = 0;
784 int i, j;
785 unsigned char c;
786 int buf_len = strlen (in->buffer);
788 if (should_show_history_button (in))
789 has_history = HISTORY_BUTTON_WIDTH;
791 if (in->disable_update)
792 return;
794 /* Make the point visible */
795 if ((in->point < in->first_shown) ||
796 (in->point >= in->first_shown+in->field_len - has_history)){
797 in->first_shown = in->point - (in->field_len / 3);
798 if (in->first_shown < 0)
799 in->first_shown = 0;
802 /* Adjust the mark */
803 if (in->mark > buf_len)
804 in->mark = buf_len;
806 if (has_history)
807 draw_history_button (in);
809 attrset (in->color);
811 widget_move (&in->widget, 0, 0);
812 for (i = 0; i < in->field_len - has_history; i++)
813 addch (' ');
814 widget_move (&in->widget, 0, 0);
816 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
817 c = in->buffer [j++];
818 c = is_printable (c) ? c : '.';
819 if (in->is_password)
820 c = '*';
821 addch (c);
823 widget_move (&in->widget, 0, in->point - in->first_shown);
825 if (clear_first)
826 in->first = 0;
829 void
830 winput_set_origin (WInput *in, int x, int field_len)
832 in->widget.x = x;
833 in->field_len = in->widget.cols = field_len;
834 update_input (in, 0);
837 /* {{{ history saving and loading */
839 int num_history_items_recorded = 60;
842 This loads and saves the history of an input line to and from the
843 widget. It is called with the widgets history name on creation of the
844 widget, and returns the GList list. It stores histories in the file
845 ~/.mc/history in using the profile code.
847 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
848 function) then input_new assigns the default text to be the last text
849 entered, or "" if not found.
852 GList *
853 history_get (const char *input_name)
855 int i;
856 GList *hist;
857 char *profile;
859 hist = NULL;
861 if (!num_history_items_recorded) /* this is how to disable */
862 return NULL;
863 if (!input_name)
864 return NULL;
865 if (!*input_name)
866 return NULL;
867 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
868 for (i = 0;; i++) {
869 char key_name[BUF_TINY];
870 char this_entry[BUF_LARGE];
871 g_snprintf (key_name, sizeof (key_name), "%d", i);
872 GetPrivateProfileString (input_name, key_name, "", this_entry,
873 sizeof (this_entry), profile);
874 if (!*this_entry)
875 break;
877 hist = list_append_unique (hist, g_strdup (this_entry));
879 g_free (profile);
881 /* return pointer to the last entry in the list */
882 hist = g_list_last (hist);
884 return hist;
887 void
888 history_put (const char *input_name, GList *h)
890 int i;
891 char *profile;
893 if (!input_name)
894 return;
896 if (!*input_name)
897 return;
899 if (!h)
900 return;
902 if (!num_history_items_recorded) /* this is how to disable */
903 return;
905 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
907 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
908 close (i);
910 /* Make sure the history is only readable by the user */
911 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
912 g_free (profile);
913 return;
916 /* go to end of list */
917 h = g_list_last (h);
919 /* go back 60 places */
920 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
921 h = g_list_previous (h);
923 if (input_name)
924 profile_clean_section (input_name, profile);
926 /* dump histories into profile */
927 for (i = 0; h; h = g_list_next (h)) {
928 char *text;
930 text = (char *) h->data;
932 /* We shouldn't have null entries, but let's be sure */
933 if (text && *text) {
934 char key_name[BUF_TINY];
935 g_snprintf (key_name, sizeof (key_name), "%d", i++);
936 WritePrivateProfileString (input_name, key_name, text,
937 profile);
941 g_free (profile);
944 /* }}} history saving and loading */
947 /* {{{ history display */
949 static const char *
950 i18n_htitle (void)
952 static const char *history_title = NULL;
954 if (history_title == NULL)
955 history_title = _(" History ");
956 return history_title;
959 static WLEntry *listbox_select_pos (WListbox *l, WLEntry *base, int
960 pos);
962 static inline cb_ret_t
963 listbox_fwd (WListbox *l)
965 if (l->current != l->list->prev){
966 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
967 return MSG_HANDLED;
969 return MSG_NOT_HANDLED;
972 char *
973 show_hist (GList *history, int widget_x, int widget_y)
975 GList *hi, *z;
976 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
977 int x, y, w, h;
978 char *q, *r = 0;
979 Dlg_head *query_dlg;
980 WListbox *query_list;
982 z = history;
983 if (!z)
984 return NULL;
986 z = g_list_first (history);
987 hi = z;
988 while (hi) {
989 if ((i = strlen ((char *) hi->data)) > maxlen)
990 maxlen = i;
991 count++;
992 hi = g_list_next (hi);
995 y = widget_y;
996 h = count + 2;
997 if (h <= y || y > LINES - 6) {
998 h = min (h, y - 1);
999 y -= h;
1000 } else {
1001 y++;
1002 h = min (h, LINES - y);
1005 if (widget_x > 2)
1006 x = widget_x - 2;
1007 else
1008 x = 0;
1009 if ((w = maxlen + 4) + x > COLS) {
1010 w = min (w, COLS);
1011 x = COLS - w;
1014 query_dlg =
1015 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
1016 i18n_htitle (), DLG_COMPACT);
1017 query_list = listbox_new (1, 1, w - 2, h - 2, 0);
1018 add_widget (query_dlg, query_list);
1019 hi = z;
1020 if (y < widget_y) {
1021 /* traverse */
1022 while (hi) {
1023 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1024 hi = g_list_next (hi);
1026 while (listbox_fwd (query_list));
1027 } else {
1028 /* traverse backwards */
1029 hi = g_list_last (history);
1030 while (hi) {
1031 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
1032 hi = g_list_previous (hi);
1035 run_dlg (query_dlg);
1036 q = NULL;
1037 if (query_dlg->ret_value != B_CANCEL) {
1038 listbox_get_current (query_list, &q, NULL);
1039 if (q)
1040 r = g_strdup (q);
1042 destroy_dlg (query_dlg);
1043 return r;
1046 static void do_show_hist (WInput * in)
1048 char *r;
1049 r = show_hist (in->history, in->widget.x, in->widget.y);
1050 if (r) {
1051 assign_text (in, r);
1052 g_free (r);
1056 /* }}} history display */
1058 static void
1059 input_destroy (WInput *in)
1061 if (!in){
1062 fprintf (stderr, "Internal error: null Input *\n");
1063 exit (1);
1066 new_input (in);
1068 if (in->history){
1069 if (!in->is_password) /* don't save passwords ;-) */
1070 history_put (in->history_name, in->history);
1072 in->history = g_list_first (in->history);
1073 g_list_foreach (in->history, (GFunc) g_free, NULL);
1074 g_list_free (in->history);
1077 g_free (in->buffer);
1078 free_completions (in);
1079 g_free (in->history_name);
1082 void
1083 input_disable_update (WInput *in)
1085 in->disable_update++;
1088 void
1089 input_enable_update (WInput *in)
1091 in->disable_update--;
1092 update_input (in, 0);
1095 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1098 push_history (WInput *in, const char *text)
1100 static int i18n;
1101 /* input widget where urls with passwords are entered without any
1102 vfs prefix */
1103 static const char *password_input_fields[] = {
1104 N_(" Link to a remote machine "),
1105 N_(" FTP to machine "),
1106 N_(" SMB link to machine ")
1108 char *t;
1109 const char *p;
1110 size_t i;
1112 if (!i18n) {
1113 i18n = 1;
1114 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1115 password_input_fields[i] = _(password_input_fields[i]);
1118 for (p = text; *p == ' ' || *p == '\t'; p++);
1119 if (!*p)
1120 return 0;
1122 if (in->history) {
1123 /* Avoid duplicated entries */
1124 in->history = g_list_last (in->history);
1125 if (!strcmp ((char *) in->history->data, text))
1126 return 1;
1129 t = g_strdup (text);
1131 if (in->history_name) {
1132 p = in->history_name + 3;
1133 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1134 if (strcmp (p, password_input_fields[i]) == 0)
1135 break;
1136 if (i < ELEMENTS (password_input_fields))
1137 strip_password (t, 0);
1138 else
1139 strip_password (t, 1);
1142 in->history = list_append_unique (in->history, t);
1143 in->need_push = 0;
1145 return 2;
1148 #undef ELEMENTS
1150 /* Cleans the input line and adds the current text to the history */
1151 void
1152 new_input (WInput *in)
1154 if (in->buffer)
1155 push_history (in, in->buffer);
1156 in->need_push = 1;
1157 in->buffer [0] = 0;
1158 in->point = 0;
1159 in->mark = 0;
1160 free_completions (in);
1161 update_input (in, 0);
1164 static cb_ret_t
1165 insert_char (WInput *in, int c_code)
1167 size_t i;
1169 if (c_code == -1)
1170 return MSG_NOT_HANDLED;
1172 in->need_push = 1;
1173 if (strlen (in->buffer)+1 == (size_t) in->current_max_len){
1174 /* Expand the buffer */
1175 char *narea = g_realloc (in->buffer, in->current_max_len + in->field_len);
1176 if (narea){
1177 in->buffer = narea;
1178 in->current_max_len += in->field_len;
1181 if (strlen (in->buffer)+1 < (size_t) in->current_max_len){
1182 size_t l = strlen (&in->buffer [in->point]);
1183 for (i = l+1; i > 0; i--)
1184 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1185 in->buffer [in->point] = c_code;
1186 in->point++;
1188 return MSG_HANDLED;
1191 static void
1192 beginning_of_line (WInput *in)
1194 in->point = 0;
1197 static void
1198 end_of_line (WInput *in)
1200 in->point = strlen (in->buffer);
1203 static void
1204 backward_char (WInput *in)
1206 if (in->point)
1207 in->point--;
1210 static void
1211 forward_char (WInput *in)
1213 if (in->buffer [in->point])
1214 in->point++;
1217 static void
1218 forward_word (WInput * in)
1220 char *p = in->buffer + in->point;
1222 while (*p
1223 && (isspace ((unsigned char) *p)
1224 || ispunct ((unsigned char) *p)))
1225 p++;
1226 while (*p && isalnum ((unsigned char) *p))
1227 p++;
1228 in->point = p - in->buffer;
1231 static void
1232 backward_word (WInput *in)
1234 char *p = in->buffer + in->point;
1236 while (p - 1 > in->buffer - 1 && (isspace ((unsigned char) *(p - 1))
1237 || ispunct ((unsigned char)
1238 *(p - 1))))
1239 p--;
1240 while (p - 1 > in->buffer - 1 && isalnum ((unsigned char) *(p - 1)))
1241 p--;
1242 in->point = p - in->buffer;
1245 static void
1246 key_left (WInput *in)
1248 backward_char (in);
1251 static void
1252 key_ctrl_left (WInput *in)
1254 backward_word (in);
1257 static void
1258 key_right (WInput *in)
1260 forward_char (in);
1263 static void
1264 key_ctrl_right (WInput *in)
1266 forward_word (in);
1268 static void
1269 backward_delete (WInput *in)
1271 int i;
1273 if (!in->point)
1274 return;
1275 for (i = in->point; in->buffer [i-1]; i++)
1276 in->buffer [i-1] = in->buffer [i];
1277 in->need_push = 1;
1278 in->point--;
1281 static void
1282 delete_char (WInput *in)
1284 int i;
1286 for (i = in->point; in->buffer [i]; i++)
1287 in->buffer [i] = in->buffer [i+1];
1288 in->need_push = 1;
1291 static void
1292 copy_region (WInput *in, int x_first, int x_last)
1294 int first = min (x_first, x_last);
1295 int last = max (x_first, x_last);
1297 if (last == first)
1298 return;
1300 g_free (kill_buffer);
1302 kill_buffer = g_strndup(in->buffer+first,last-first);
1305 static void
1306 delete_region (WInput *in, int x_first, int x_last)
1308 int first = min (x_first, x_last);
1309 int last = max (x_first, x_last);
1310 size_t len = strlen (&in->buffer [last]) + 1;
1312 in->point = first;
1313 in->mark = first;
1314 memmove (&in->buffer [first], &in->buffer [last], len);
1315 in->need_push = 1;
1318 static void
1319 kill_word (WInput *in)
1321 int old_point = in->point;
1322 int new_point;
1324 forward_word (in);
1325 new_point = in->point;
1326 in->point = old_point;
1328 copy_region (in, old_point, new_point);
1329 delete_region (in, old_point, new_point);
1330 in->need_push = 1;
1333 static void
1334 back_kill_word (WInput *in)
1336 int old_point = in->point;
1337 int new_point;
1339 backward_word (in);
1340 new_point = in->point;
1341 in->point = old_point;
1343 copy_region (in, old_point, new_point);
1344 delete_region (in, old_point, new_point);
1345 in->need_push = 1;
1348 static void
1349 set_mark (WInput *in)
1351 in->mark = in->point;
1354 static void
1355 kill_save (WInput *in)
1357 copy_region (in, in->mark, in->point);
1360 static void
1361 kill_region (WInput *in)
1363 kill_save (in);
1364 delete_region (in, in->point, in->mark);
1367 static void
1368 yank (WInput *in)
1370 char *p;
1372 if (!kill_buffer)
1373 return;
1374 for (p = kill_buffer; *p; p++)
1375 insert_char (in, *p);
1378 static void
1379 kill_line (WInput *in)
1381 g_free (kill_buffer);
1382 kill_buffer = g_strdup (&in->buffer [in->point]);
1383 in->buffer [in->point] = 0;
1386 void
1387 assign_text (WInput *in, const char *text)
1389 free_completions (in);
1390 g_free (in->buffer);
1391 in->buffer = g_strdup (text); /* was in->buffer->text */
1392 in->current_max_len = strlen (in->buffer) + 1;
1393 in->point = strlen (in->buffer);
1394 in->mark = 0;
1395 in->need_push = 1;
1398 static void
1399 hist_prev (WInput *in)
1401 if (!in->history)
1402 return;
1404 if (in->need_push) {
1405 switch (push_history (in, in->buffer)) {
1406 case 2:
1407 in->history = g_list_previous (in->history);
1408 break;
1409 case 1:
1410 if (in->history->prev)
1411 in->history = g_list_previous (in->history);
1412 break;
1413 case 0:
1414 break;
1416 } else if (in->history->prev)
1417 in->history = g_list_previous (in->history);
1418 else
1419 return;
1420 assign_text (in, (char *) in->history->data);
1421 in->need_push = 0;
1424 static void
1425 hist_next (WInput *in)
1427 if (in->need_push) {
1428 switch (push_history (in, in->buffer)) {
1429 case 2:
1430 assign_text (in, "");
1431 return;
1432 case 0:
1433 return;
1437 if (!in->history)
1438 return;
1440 if (!in->history->next) {
1441 assign_text (in, "");
1442 return;
1445 in->history = g_list_next (in->history);
1446 assign_text (in, (char *) in->history->data);
1447 in->need_push = 0;
1450 static const struct {
1451 int key_code;
1452 void (*fn)(WInput *in);
1453 } input_map [] = {
1454 /* Motion */
1455 { XCTRL('a'), beginning_of_line },
1456 { KEY_HOME, beginning_of_line },
1457 { KEY_A1, beginning_of_line },
1458 { ALT ('<'), beginning_of_line },
1459 { XCTRL('e'), end_of_line },
1460 { KEY_END, end_of_line },
1461 { KEY_C1, end_of_line },
1462 { ALT ('>'), end_of_line },
1463 { KEY_LEFT, key_left },
1464 { KEY_LEFT | KEY_M_CTRL, key_ctrl_left },
1465 { XCTRL('b'), backward_char },
1466 { ALT('b'), backward_word },
1467 { KEY_RIGHT, key_right },
1468 { KEY_RIGHT | KEY_M_CTRL, key_ctrl_right },
1469 { XCTRL('f'), forward_char },
1470 { ALT('f'), forward_word },
1472 /* Editing */
1473 { KEY_BACKSPACE, backward_delete },
1474 { KEY_DC, delete_char },
1475 { ALT('d'), kill_word },
1476 { ALT(KEY_BACKSPACE), back_kill_word },
1478 /* Region manipulation */
1479 { 0, set_mark },
1480 { XCTRL('w'), kill_region },
1481 { ALT('w'), kill_save },
1482 { XCTRL('y'), yank },
1483 { XCTRL('k'), kill_line },
1485 /* History */
1486 { ALT('p'), hist_prev },
1487 { ALT('n'), hist_next },
1488 { ALT('h'), do_show_hist },
1490 /* Completion */
1491 { ALT('\t'), complete },
1493 { 0, 0 }
1496 /* This function is a test for a special input key used in complete.c */
1497 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1498 and 2 if it is a complete key */
1500 is_in_input_map (WInput *in, int c_code)
1502 int i;
1504 (void) in;
1506 for (i = 0; input_map [i].fn; i++)
1507 if (c_code == input_map [i].key_code) {
1508 if (input_map [i].fn == complete)
1509 return 2;
1510 else
1511 return 1;
1513 return 0;
1516 static void
1517 port_region_marked_for_delete (WInput *in)
1519 *in->buffer = 0;
1520 in->point = 0;
1521 in->first = 0;
1524 cb_ret_t
1525 handle_char (WInput *in, int c_code)
1527 cb_ret_t v;
1528 int i;
1530 v = MSG_NOT_HANDLED;
1532 if (quote){
1533 free_completions (in);
1534 v = insert_char (in, c_code);
1535 update_input (in, 1);
1536 quote = 0;
1537 return v;
1540 for (i = 0; input_map [i].fn; i++){
1541 if (c_code == input_map [i].key_code){
1542 if (input_map [i].fn != complete)
1543 free_completions (in);
1544 (*input_map [i].fn)(in);
1545 v = MSG_HANDLED;
1546 break;
1549 if (!input_map [i].fn){
1550 if (c_code > 255 || !is_printable (c_code))
1551 return MSG_NOT_HANDLED;
1552 if (in->first){
1553 port_region_marked_for_delete (in);
1555 free_completions (in);
1556 v = insert_char (in, c_code);
1558 update_input (in, 1);
1559 return v;
1562 /* Inserts text in input line */
1563 void
1564 stuff (WInput *in, const char *text, int insert_extra_space)
1566 input_disable_update (in);
1567 while (*text)
1568 handle_char (in, *text++);
1569 if (insert_extra_space)
1570 handle_char (in, ' ');
1571 input_enable_update (in);
1572 update_input (in, 1);
1575 void
1576 input_set_point (WInput *in, int pos)
1578 if (pos > in->current_max_len)
1579 pos = in->current_max_len;
1580 if (pos != in->point)
1581 free_completions (in);
1582 in->point = pos;
1583 update_input (in, 1);
1586 cb_ret_t
1587 input_callback (Widget *w, widget_msg_t msg, int parm)
1589 WInput *in = (WInput *) w;
1590 cb_ret_t v;
1592 switch (msg) {
1593 case WIDGET_KEY:
1594 if (parm == XCTRL ('q')) {
1595 quote = 1;
1596 v = handle_char (in, ascii_alpha_to_cntrl (mi_getch ()));
1597 quote = 0;
1598 return v;
1601 /* Keys we want others to handle */
1602 if (parm == KEY_UP || parm == KEY_DOWN || parm == ESC_CHAR
1603 || parm == KEY_F (10) || parm == XCTRL ('g') || parm == '\n')
1604 return MSG_NOT_HANDLED;
1606 /* When pasting multiline text, insert literal Enter */
1607 if ((parm & ~KEY_M_MASK) == '\n') {
1608 quote = 1;
1609 v = handle_char (in, '\n');
1610 quote = 0;
1611 return v;
1614 return handle_char (in, parm);
1616 case WIDGET_FOCUS:
1617 case WIDGET_UNFOCUS:
1618 case WIDGET_DRAW:
1619 update_input (in, 0);
1620 return MSG_HANDLED;
1622 case WIDGET_CURSOR:
1623 widget_move (&in->widget, 0, in->point - in->first_shown);
1624 return MSG_HANDLED;
1626 case WIDGET_DESTROY:
1627 input_destroy (in);
1628 return MSG_HANDLED;
1630 default:
1631 return default_proc (msg, parm);
1635 static int
1636 input_event (Gpm_Event * event, void *data)
1638 WInput *in = data;
1640 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1641 dlg_select_widget (in);
1643 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1644 && should_show_history_button (in)) {
1645 do_show_hist (in);
1646 } else {
1647 in->point = strlen (in->buffer);
1648 if (event->x - in->first_shown - 1 < in->point)
1649 in->point = event->x - in->first_shown - 1;
1650 if (in->point < 0)
1651 in->point = 0;
1653 update_input (in, 1);
1655 return MOU_NORMAL;
1658 WInput *
1659 input_new (int y, int x, int color, int len, const char *def_text,
1660 const char *histname, INPUT_COMPLETE_FLAGS completion_flags)
1662 WInput *in = g_new (WInput, 1);
1663 int initial_buffer_len;
1665 init_widget (&in->widget, y, x, 1, len, input_callback, input_event);
1667 /* history setup */
1668 in->history = NULL;
1669 in->history_name = 0;
1670 if (histname) {
1671 if (*histname) {
1672 in->history_name = g_strdup (histname);
1673 in->history = history_get (histname);
1677 if (!def_text)
1678 def_text = "";
1680 if (def_text == INPUT_LAST_TEXT) {
1681 def_text = "";
1682 if (in->history)
1683 if (in->history->data)
1684 def_text = (char *) in->history->data;
1686 initial_buffer_len = 1 + max ((size_t) len, strlen (def_text));
1687 in->widget.options |= W_IS_INPUT;
1688 in->completions = NULL;
1689 in->completion_flags = completion_flags;
1690 in->current_max_len = initial_buffer_len;
1691 in->buffer = g_malloc (initial_buffer_len);
1692 in->color = color;
1693 in->field_len = len;
1694 in->first = 1;
1695 in->first_shown = 0;
1696 in->disable_update = 0;
1697 in->mark = 0;
1698 in->need_push = 1;
1699 in->is_password = 0;
1701 strcpy (in->buffer, def_text);
1702 in->point = strlen (in->buffer);
1703 return in;
1707 /* Listbox widget */
1709 /* Should draw the scrollbar, but currently draws only
1710 * indications that there is more information
1712 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1714 static void
1715 listbox_drawscroll (WListbox *l)
1717 int line;
1718 int i, top;
1719 int max_line = l->height-1;
1721 /* Are we at the top? */
1722 widget_move (&l->widget, 0, l->width);
1723 if (l->list == l->top)
1724 one_vline ();
1725 else
1726 addch ('^');
1728 /* Are we at the bottom? */
1729 widget_move (&l->widget, max_line, l->width);
1730 top = listbox_cdiff (l->list, l->top);
1731 if ((top + l->height == l->count) || l->height >= l->count)
1732 one_vline ();
1733 else
1734 addch ('v');
1736 /* Now draw the nice relative pointer */
1737 if (l->count)
1738 line = 1+ ((l->pos * (l->height-2)) / l->count);
1739 else
1740 line = 0;
1742 for (i = 1; i < max_line; i++){
1743 widget_move (&l->widget, i, l->width);
1744 if (i != line)
1745 one_vline ();
1746 else
1747 addch ('*');
1751 static void
1752 listbox_draw (WListbox *l, int focused)
1754 WLEntry *e;
1755 int i;
1756 int sel_line;
1757 Dlg_head *h = l->widget.parent;
1758 int normalc = DLG_NORMALC (h);
1759 int selc;
1760 const char *text;
1762 if (focused){
1763 selc = DLG_FOCUSC (h);
1764 } else {
1765 selc = DLG_HOT_FOCUSC (h);
1767 sel_line = -1;
1769 for (e = l->top, i = 0; (i < l->height); i++){
1771 /* Display the entry */
1772 if (e == l->current && sel_line == -1){
1773 sel_line = i;
1774 attrset (selc);
1775 } else
1776 attrset (normalc);
1778 widget_move (&l->widget, i, 0);
1780 if ((i > 0 && e == l->list) || !l->list)
1781 text = "";
1782 else {
1783 text = e->text;
1784 e = e->next;
1786 tty_printf (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1788 l->cursor_y = sel_line;
1789 if (!l->scrollbar)
1790 return;
1791 attrset (normalc);
1792 listbox_drawscroll (l);
1795 /* Returns the number of items between s and e,
1796 must be on the same linked list */
1797 static int
1798 listbox_cdiff (WLEntry *s, WLEntry *e)
1800 int count;
1802 for (count = 0; s != e; count++)
1803 s = s->next;
1804 return count;
1807 static WLEntry *
1808 listbox_check_hotkey (WListbox *l, int key)
1810 int i;
1811 WLEntry *e;
1813 i = 0;
1814 e = l->list;
1815 if (!e)
1816 return NULL;
1818 while (1) {
1820 /* If we didn't find anything, return */
1821 if (i && e == l->list)
1822 return NULL;
1824 if (e->hotkey == key)
1825 return e;
1827 i++;
1828 e = e->next;
1832 /* Used only for display updating, for avoiding line at a time scroll */
1833 void
1834 listbox_select_last (WListbox *l, int set_top)
1836 if (l->list){
1837 l->current = l->list->prev;
1838 l->pos = l->count - 1;
1839 if (set_top)
1840 l->top = l->list->prev;
1844 void
1845 listbox_remove_list (WListbox *l)
1847 WLEntry *p, *q;
1849 if (!l->count)
1850 return;
1852 p = l->list;
1854 while (l->count--) {
1855 q = p->next;
1856 g_free (p->text);
1857 g_free (p);
1858 p = q;
1860 l->pos = l->count = 0;
1861 l->list = l->top = l->current = 0;
1865 * bor 30.10.96: added force flag to remove *last* entry as well
1866 * bor 30.10.96: corrected selection bug if last entry was removed
1869 void
1870 listbox_remove_current (WListbox *l, int force)
1872 WLEntry *p;
1874 /* Ok, note: this won't allow for emtpy lists */
1875 if (!force && (!l->count || l->count == 1))
1876 return;
1878 l->count--;
1879 p = l->current;
1881 if (l->count) {
1882 l->current->next->prev = l->current->prev;
1883 l->current->prev->next = l->current->next;
1884 if (p->next == l->list) {
1885 l->current = p->prev;
1886 l->pos--;
1888 else
1889 l->current = p->next;
1891 if (p == l->list)
1892 l->list = l->top = p->next;
1893 } else {
1894 l->pos = 0;
1895 l->list = l->top = l->current = 0;
1898 g_free (p->text);
1899 g_free (p);
1902 /* Makes *e the selected entry (sets current and pos) */
1903 void
1904 listbox_select_entry (WListbox *l, WLEntry *dest)
1906 WLEntry *e;
1907 int pos;
1908 int top_seen;
1910 top_seen = 0;
1912 /* Special case */
1913 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1915 if (e == l->top)
1916 top_seen = 1;
1918 if (e == dest){
1919 l->current = e;
1920 if (top_seen){
1921 while (listbox_cdiff (l->top, l->current) >= l->height)
1922 l->top = l->top->next;
1923 } else {
1924 l->top = l->current;
1926 l->pos = pos;
1927 return;
1930 /* If we are unable to find it, set decent values */
1931 l->current = l->top = l->list;
1932 l->pos = 0;
1935 /* Selects from base the pos element */
1936 static WLEntry *
1937 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1939 WLEntry *last = l->list->prev;
1941 if (base == last)
1942 return last;
1943 while (pos--){
1944 base = base->next;
1945 if (base == last)
1946 break;
1948 return base;
1951 static inline cb_ret_t
1952 listbox_back (WListbox *l)
1954 if (l->pos){
1955 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1956 return MSG_HANDLED;
1958 return MSG_NOT_HANDLED;
1961 /* Return MSG_HANDLED if we want a redraw */
1962 static cb_ret_t
1963 listbox_key (WListbox *l, int key)
1965 int i;
1966 int j = 0;
1968 if (!l->list)
1969 return MSG_NOT_HANDLED;
1971 switch (key){
1972 case KEY_HOME:
1973 case KEY_A1:
1974 case ALT ('<'):
1975 l->current = l->top = l->list;
1976 l->pos = 0;
1977 return MSG_HANDLED;
1979 case KEY_END:
1980 case KEY_C1:
1981 case ALT ('>'):
1982 l->current = l->top = l->list->prev;
1983 for (i = min (l->height - 1, l->count - 1); i; i--)
1984 l->top = l->top->prev;
1985 l->pos = l->count - 1;
1986 return MSG_HANDLED;
1988 case XCTRL('p'):
1989 case KEY_UP:
1990 listbox_back (l);
1991 return MSG_HANDLED;
1993 case XCTRL('n'):
1994 case KEY_DOWN:
1995 listbox_fwd (l);
1996 return MSG_HANDLED;
1998 case KEY_NPAGE:
1999 case XCTRL('v'):
2000 for (i = 0; i < l->height-1; i++)
2001 j |= listbox_fwd (l);
2002 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2004 case KEY_PPAGE:
2005 case ALT('v'):
2006 for (i = 0; i < l->height-1; i++)
2007 j |= listbox_back (l);
2008 return (j > 0) ? MSG_HANDLED : MSG_NOT_HANDLED;
2010 return MSG_NOT_HANDLED;
2013 static void
2014 listbox_destroy (WListbox *l)
2016 WLEntry *n, *p = l->list;
2017 int i;
2019 for (i = 0; i < l->count; i++){
2020 n = p->next;
2021 g_free (p->text);
2022 g_free (p);
2023 p = n;
2027 static cb_ret_t
2028 listbox_callback (Widget *w, widget_msg_t msg, int parm)
2030 WListbox *l = (WListbox *) w;
2031 cb_ret_t ret_code;
2032 WLEntry *e;
2033 Dlg_head *h = l->widget.parent;
2035 switch (msg) {
2036 case WIDGET_INIT:
2037 return MSG_HANDLED;
2039 case WIDGET_HOTKEY:
2040 if ((e = listbox_check_hotkey (l, parm)) != NULL) {
2041 int action;
2043 listbox_select_entry (l, e);
2045 if (l->cback)
2046 action = (*l->cback) (l);
2047 else
2048 action = LISTBOX_DONE;
2050 if (action == LISTBOX_DONE) {
2051 h->ret_value = B_ENTER;
2052 dlg_stop (h);
2054 return MSG_HANDLED;
2055 } else
2056 return MSG_NOT_HANDLED;
2058 case WIDGET_KEY:
2059 if ((ret_code = listbox_key (l, parm)))
2060 listbox_draw (l, 1);
2061 return ret_code;
2063 case WIDGET_CURSOR:
2064 widget_move (&l->widget, l->cursor_y, 0);
2065 return MSG_HANDLED;
2067 case WIDGET_FOCUS:
2068 case WIDGET_UNFOCUS:
2069 case WIDGET_DRAW:
2070 listbox_draw (l, msg != WIDGET_UNFOCUS);
2071 return MSG_HANDLED;
2073 case WIDGET_DESTROY:
2074 listbox_destroy (l);
2075 return MSG_HANDLED;
2077 default:
2078 return default_proc (msg, parm);
2082 static int
2083 listbox_event (Gpm_Event *event, void *data)
2085 WListbox *l = data;
2086 Widget *w = data;
2087 int i;
2089 Dlg_head *h = l->widget.parent;
2091 /* Single click */
2092 if (event->type & GPM_DOWN)
2093 dlg_select_widget (l);
2094 if (!l->list)
2095 return MOU_NORMAL;
2096 if (event->type & (GPM_DOWN | GPM_DRAG)) {
2097 if (event->x < 0 || event->x >= l->width)
2098 return MOU_REPEAT;
2099 if (event->y < 1)
2100 for (i = -event->y; i >= 0; i--)
2101 listbox_back (l);
2102 else if (event->y > l->height)
2103 for (i = event->y - l->height; i > 0; i--)
2104 listbox_fwd (l);
2105 else
2106 listbox_select_entry (l,
2107 listbox_select_pos (l, l->top,
2108 event->y - 1));
2110 /* We need to refresh ourselves since the dialog manager doesn't */
2111 /* know about this event */
2112 listbox_callback (w, WIDGET_DRAW, 0);
2113 mc_refresh ();
2114 return MOU_REPEAT;
2117 /* Double click */
2118 if ((event->type & (GPM_DOUBLE | GPM_UP)) == (GPM_UP | GPM_DOUBLE)) {
2119 int action;
2121 if (event->x < 0 || event->x >= l->width)
2122 return MOU_NORMAL;
2123 if (event->y < 1 || event->y > l->height)
2124 return MOU_NORMAL;
2126 dlg_select_widget (l);
2127 listbox_select_entry (l,
2128 listbox_select_pos (l, l->top,
2129 event->y - 1));
2131 if (l->cback)
2132 action = (*l->cback) (l);
2133 else
2134 action = LISTBOX_DONE;
2136 if (action == LISTBOX_DONE) {
2137 h->ret_value = B_ENTER;
2138 dlg_stop (h);
2139 return MOU_NORMAL;
2142 return MOU_NORMAL;
2145 WListbox *
2146 listbox_new (int y, int x, int width, int height, lcback callback)
2148 WListbox *l = g_new (WListbox, 1);
2150 init_widget (&l->widget, y, x, height, width,
2151 listbox_callback, listbox_event);
2153 l->list = l->top = l->current = 0;
2154 l->pos = 0;
2155 l->width = width;
2156 if (height <= 0)
2157 l->height = 1;
2158 else
2159 l->height = height;
2160 l->count = 0;
2161 l->cback = callback;
2162 l->allow_duplicates = 1;
2163 l->scrollbar = slow_terminal ? 0 : 1;
2164 widget_want_hotkey (l->widget, 1);
2166 return l;
2169 /* Listbox item adding function. They still lack a lot of functionality */
2170 /* any takers? */
2171 /* 1.11.96 bor: added pos argument to control placement of new entry */
2172 static void
2173 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2175 if (!l->list){
2176 l->list = e;
2177 l->top = e;
2178 l->current = e;
2179 e->next = l->list;
2180 e->prev = l->list;
2181 } else if (pos == LISTBOX_APPEND_AT_END) {
2182 e->next = l->list;
2183 e->prev = l->list->prev;
2184 l->list->prev->next = e;
2185 l->list->prev = e;
2186 } else if (pos == LISTBOX_APPEND_BEFORE){
2187 e->next = l->current;
2188 e->prev = l->current->prev;
2189 l->current->prev->next = e;
2190 l->current->prev = e;
2191 if (l->list == l->current) { /* move list one position down */
2192 l->list = e;
2193 l->top = e;
2195 } else if (pos == LISTBOX_APPEND_AFTER) {
2196 e->prev = l->current;
2197 e->next = l->current->next;
2198 l->current->next->prev = e;
2199 l->current->next = e;
2200 } else if (pos == LISTBOX_APPEND_SORTED) {
2201 WLEntry *w = l->list;
2203 while (w->next != l->list && strcmp (e->text, w->text) > 0)
2204 w = w->next;
2205 if (w->next == l->list) {
2206 e->prev = w;
2207 e->next = l->list;
2208 w->next = e;
2209 l->list->prev = e;
2210 } else {
2211 e->next = w;
2212 e->prev = w->prev;
2213 w->prev->next = e;
2214 w->prev = e;
2217 l->count++;
2220 char *
2221 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey,
2222 const char *text, void *data)
2224 WLEntry *entry;
2226 if (!l)
2227 return NULL;
2229 if (!l->allow_duplicates)
2230 if (listbox_search_text (l, text))
2231 return NULL;
2233 entry = g_new (WLEntry, 1);
2234 entry->text = g_strdup (text);
2235 entry->data = data;
2236 entry->hotkey = hotkey;
2238 listbox_append_item (l, entry, pos);
2240 return entry->text;
2243 /* Selects the nth entry in the listbox */
2244 void
2245 listbox_select_by_number (WListbox *l, int n)
2247 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2250 WLEntry *
2251 listbox_search_text (WListbox *l, const char *text)
2253 WLEntry *e;
2255 e = l->list;
2256 if (!e)
2257 return NULL;
2259 do {
2260 if(!strcmp (e->text, text))
2261 return e;
2262 e = e->next;
2263 } while (e!=l->list);
2265 return NULL;
2268 /* Returns the current string text as well as the associated extra data */
2269 void
2270 listbox_get_current (WListbox *l, char **string, char **extra)
2272 if (!l->current){
2273 *string = 0;
2274 *extra = 0;
2276 if (string && l->current)
2277 *string = l->current->text;
2278 if (extra && l->current)
2279 *extra = l->current->data;
2282 /* returns TRUE if a function has been called, FALSE otherwise. */
2283 static gboolean
2284 buttonbar_call (WButtonBar *bb, int i)
2286 switch (bb->labels[i].tag) {
2287 case BBFUNC_NONE:
2288 break;
2289 case BBFUNC_VOID:
2290 bb->labels[i].u.fn_void ();
2291 return TRUE;
2292 case BBFUNC_PTR:
2293 bb->labels[i].u.fn_ptr (bb->labels[i].data);
2294 return TRUE;
2296 return FALSE;
2300 static cb_ret_t
2301 buttonbar_callback (Widget *w, widget_msg_t msg, int parm)
2303 WButtonBar *bb = (WButtonBar *) w;
2304 int i;
2306 switch (msg) {
2307 case WIDGET_FOCUS:
2308 return MSG_NOT_HANDLED;
2310 case WIDGET_HOTKEY:
2311 for (i = 0; i < 10; i++) {
2312 if (parm == KEY_F (i + 1) && buttonbar_call (bb, i))
2313 return MSG_HANDLED;
2315 return MSG_NOT_HANDLED;
2317 case WIDGET_DRAW:
2318 if (!bb->visible)
2319 return MSG_HANDLED;
2320 widget_move (&bb->widget, 0, 0);
2321 attrset (DEFAULT_COLOR);
2322 tty_printf ("%-*s", bb->widget.cols, "");
2323 for (i = 0; i < COLS / 8 && i < 10; i++) {
2324 widget_move (&bb->widget, 0, i * 8);
2325 attrset (DEFAULT_COLOR);
2326 tty_printf ("%d", i + 1);
2327 attrset (SELECTED_COLOR);
2328 tty_printf ("%-*s", ((i + 1) * 8 == COLS ? 5 : 6),
2329 bb->labels[i].text ? bb->labels[i].text : "");
2330 attrset (DEFAULT_COLOR);
2332 attrset (SELECTED_COLOR);
2333 return MSG_HANDLED;
2335 case WIDGET_DESTROY:
2336 for (i = 0; i < 10; i++)
2337 g_free (bb->labels[i].text);
2338 return MSG_HANDLED;
2340 default:
2341 return default_proc (msg, parm);
2345 static int
2346 buttonbar_event (Gpm_Event *event, void *data)
2348 WButtonBar *bb = data;
2349 int button;
2351 if (!(event->type & GPM_UP))
2352 return MOU_NORMAL;
2353 if (event->y == 2)
2354 return MOU_NORMAL;
2355 button = event->x / 8;
2356 if (button < 10)
2357 buttonbar_call (bb, button);
2358 return MOU_NORMAL;
2361 WButtonBar *
2362 buttonbar_new (int visible)
2364 int i;
2365 WButtonBar *bb = g_new (WButtonBar, 1);
2367 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2368 buttonbar_callback, buttonbar_event);
2370 bb->visible = visible;
2371 for (i = 0; i < 10; i++){
2372 bb->labels[i].text = NULL;
2373 bb->labels[i].tag = BBFUNC_NONE;
2375 widget_want_hotkey (bb->widget, 1);
2376 widget_want_cursor (bb->widget, 0);
2378 return bb;
2381 static void
2382 set_label_text (WButtonBar * bb, int index, const char *text)
2384 g_free (bb->labels[index - 1].text);
2386 bb->labels[index - 1].text = g_strdup (text);
2389 /* Find ButtonBar widget in the dialog */
2390 WButtonBar *
2391 find_buttonbar (Dlg_head *h)
2393 WButtonBar *bb;
2395 bb = (WButtonBar *) find_widget_type (h, buttonbar_callback);
2396 return bb;
2399 void
2400 buttonbar_clear_label (Dlg_head *h, int idx)
2402 WButtonBar *bb = find_buttonbar (h);
2404 if (!bb)
2405 return;
2407 set_label_text (bb, idx, "");
2408 bb->labels[idx - 1].tag = BBFUNC_NONE;
2411 void
2412 buttonbar_set_label_data (Dlg_head *h, int idx, const char *text, buttonbarfn cback,
2413 void *data)
2415 WButtonBar *bb = find_buttonbar (h);
2417 if (!bb)
2418 return;
2420 assert (cback != (buttonbarfn) 0);
2421 set_label_text (bb, idx, text);
2422 bb->labels[idx - 1].tag = BBFUNC_PTR;
2423 bb->labels[idx - 1].u.fn_ptr = cback;
2424 bb->labels[idx - 1].data = data;
2427 void
2428 buttonbar_set_label (Dlg_head *h, int idx, const char *text, voidfn cback)
2430 WButtonBar *bb = find_buttonbar (h);
2432 if (!bb)
2433 return;
2435 assert (cback != (voidfn) 0);
2436 set_label_text (bb, idx, text);
2437 bb->labels[idx - 1].tag = BBFUNC_VOID;
2438 bb->labels[idx - 1].u.fn_void = cback;
2441 void
2442 buttonbar_set_visible (WButtonBar *bb, gboolean visible)
2444 bb->visible = visible;
2447 void
2448 buttonbar_redraw (Dlg_head *h)
2450 WButtonBar *bb = find_buttonbar (h);
2452 if (!bb)
2453 return;
2455 send_message ((Widget *) bb, WIDGET_DRAW, 0);
2458 static cb_ret_t
2459 groupbox_callback (Widget *w, widget_msg_t msg, int parm)
2461 WGroupbox *g = (WGroupbox *) w;
2463 switch (msg) {
2464 case WIDGET_INIT:
2465 return MSG_HANDLED;
2467 case WIDGET_FOCUS:
2468 return MSG_NOT_HANDLED;
2470 case WIDGET_DRAW:
2471 attrset (COLOR_NORMAL);
2472 draw_box (g->widget.parent, g->widget.y - g->widget.parent->y,
2473 g->widget.x - g->widget.parent->x, g->widget.lines,
2474 g->widget.cols);
2476 attrset (COLOR_HOT_NORMAL);
2477 dlg_move (g->widget.parent, g->widget.y - g->widget.parent->y,
2478 g->widget.x - g->widget.parent->x + 1);
2479 addstr (g->title);
2480 return MSG_HANDLED;
2482 case WIDGET_DESTROY:
2483 g_free (g->title);
2484 return MSG_HANDLED;
2486 default:
2487 return default_proc (msg, parm);
2491 WGroupbox *
2492 groupbox_new (int x, int y, int width, int height, const char *title)
2494 WGroupbox *g = g_new (WGroupbox, 1);
2496 init_widget (&g->widget, y, x, height, width, groupbox_callback, NULL);
2498 g->widget.options &= ~W_WANT_CURSOR;
2499 widget_want_hotkey (g->widget, 0);
2501 /* Strip existing spaces, add one space before and after the title */
2502 if (title) {
2503 char *t;
2504 t = g_strstrip (g_strdup (title));
2505 g->title = g_strconcat (" ", t, " ", (char *) NULL);
2506 g_free (t);
2509 return g;