Just a little correction at the it.po file.
[midnight-commander.git] / src / widget.c
blob138b54e6f72db501502445051801e9508c5e3255
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996 the Free Software Foundation
5 Authors: 1994, 1995 Radek Doulik
6 1994, 1995 Miguel de Icaza
7 1995 Jakub Jelinek
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <config.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <ctype.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "color.h"
37 #include "mouse.h"
38 #include "dlg.h"
39 #include "widget.h"
40 #include "win.h"
41 #include "complete.h"
42 #include "key.h" /* XCTRL and ALT macros */
43 #include "profile.h" /* for history loading and saving */
44 #include "wtools.h" /* For common_dialog_repaint() */
46 static int button_event (Gpm_Event *event, WButton *b);
48 int quote = 0;
50 static int
51 button_callback (WButton *b, int Msg, int Par)
53 char buf[BUF_SMALL];
54 int stop = 0;
55 int off = 0;
56 Dlg_head *h = b->widget.parent;
58 switch (Msg){
59 case WIDGET_INIT:
60 return 1;
62 case WIDGET_HOTKEY:
63 if (b->hotkey == Par || toupper(b->hotkey) == Par){
64 button_callback (b, WIDGET_KEY, ' '); /* to make action */
65 return 1;
66 } else
67 return 0;
69 case WIDGET_KEY:
70 if (Par != ' ' && Par != '\n')
71 break;
73 if (b->callback)
74 stop = (*b->callback)(b->action, b->callback_data);
75 if (!b->callback || stop){
76 h->ret_value = b->action;
77 dlg_stop (h);
79 return 1;
81 case WIDGET_CURSOR:
82 switch (b->flags) {
83 case DEFPUSH_BUTTON:
84 off = 3;
85 break;
86 case NORMAL_BUTTON:
87 off = 2;
88 break;
89 case NARROW_BUTTON:
90 off = 1;
91 break;
92 case HIDDEN_BUTTON:
93 default:
94 off = 0;
95 break;
97 widget_move (&b->widget, 0, b->hotpos + off);
98 return 1;
100 case WIDGET_UNFOCUS:
101 case WIDGET_FOCUS:
102 case WIDGET_DRAW:
103 if (Msg==WIDGET_UNFOCUS)
104 b->selected = 0;
105 else if (Msg==WIDGET_FOCUS)
106 b->selected = 1;
108 switch (b->flags){
109 case DEFPUSH_BUTTON:
110 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
111 off = 3;
112 break;
113 case NORMAL_BUTTON:
114 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
115 off = 2;
116 break;
117 case NARROW_BUTTON:
118 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
119 off = 1;
120 break;
121 case HIDDEN_BUTTON:
122 default:
123 buf[0] = '\0';
124 off = 0;
125 break;
128 attrset ((b->selected) ? FOCUSC : NORMALC);
129 widget_move (&b->widget, 0, 0);
131 addstr (buf);
133 if (b->hotpos >= 0){
134 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
135 widget_move (&b->widget, 0, b->hotpos+off);
136 addch ((unsigned char)b->text [b->hotpos]);
138 if (Msg == WIDGET_FOCUS)
139 break;
140 else
141 return 1;
142 break;
144 return default_proc (Msg, Par);
147 static int
148 button_event (Gpm_Event *event, WButton *b)
150 if (event->type & (GPM_DOWN|GPM_UP)){
151 Dlg_head *h=b->widget.parent;
152 dlg_select_widget (h, b);
153 if (event->type & GPM_UP){
154 button_callback (b, WIDGET_KEY, ' ');
155 (*h->callback) (h, ' ', DLG_POST_KEY);
156 return MOU_NORMAL;
159 return MOU_NORMAL;
162 static void
163 button_destroy (WButton *b)
165 g_free (b->text);
168 static int
169 button_len (const char *text, unsigned int flags)
171 int ret = strlen (text);
172 switch (flags){
173 case DEFPUSH_BUTTON:
174 ret += 6;
175 break;
176 case NORMAL_BUTTON:
177 ret += 4;
178 break;
179 case NARROW_BUTTON:
180 ret += 2;
181 break;
182 case HIDDEN_BUTTON:
183 default:
184 return 0;
186 return ret;
190 * Locate the hotkey and remove it from the button text. Assuming that
191 * the button text is g_malloc()ed, we can safely change and shorten it.
193 static void
194 button_scan_hotkey(WButton* b)
196 char* cp = strchr (b->text, '&');
198 if (cp != NULL && cp[1] != '\0'){
199 strcpy (cp, cp+1);
200 b->hotkey = tolower (*cp);
201 b->hotpos = cp - b->text;
205 WButton *
206 button_new (int y, int x, int action, int flags, char *text,
207 int (*callback)(int, void *), void *callback_data, char *tkname)
209 WButton *b = g_new (WButton, 1);
211 init_widget (&b->widget, y, x, 1, button_len (text, flags),
212 (callback_fn) button_callback,
213 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
215 b->action = action;
216 b->flags = flags;
217 b->selected = 0;
218 b->text = g_strdup (text);
219 b->callback = callback;
220 b->callback_data = callback_data;
221 widget_want_hotkey (b->widget, 1);
222 b->hotkey = 0;
223 b->hotpos = -1;
225 button_scan_hotkey(b);
226 return b;
229 void
230 button_set_text (WButton *b, char *text)
232 g_free (b->text);
233 b->text = g_strdup (text);
234 b->widget.cols = button_len (text, b->flags);
235 button_scan_hotkey(b);
236 dlg_redraw (b->widget.parent);
240 /* Radio button widget */
241 static int radio_event (Gpm_Event *event, WRadio *r);
243 static int
244 radio_callback (WRadio *r, int Msg, int Par)
246 int i;
247 Dlg_head *h = r->widget.parent;
249 switch (Msg) {
250 case WIDGET_INIT:
251 return 1;
253 case WIDGET_HOTKEY:
255 int i, lp = tolower(Par);
256 char *cp;
258 for (i = 0; i < r->count; i++){
259 cp = strchr (r->texts [i], '&');
260 if (cp != NULL && cp[1] != '\0'){
261 int c = tolower (cp [1]);
263 if (c != lp)
264 continue;
265 r->pos = i;
266 radio_callback (r, WIDGET_KEY, ' '); /* Take action */
267 return 1;
271 return 0;
273 case WIDGET_KEY:
274 switch (Par){
275 case ' ':
276 r->sel = r->pos;
277 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
278 radio_callback (r, WIDGET_FOCUS, ' ');
279 return 1;
281 case KEY_UP:
282 case KEY_LEFT:
283 if (r->pos > 0){
284 r->pos--;
285 return 1;
287 return 0;
289 case KEY_DOWN:
290 case KEY_RIGHT:
291 if (r->count - 1 > r->pos) {
292 r->pos++;
293 return 1;
296 return 0;
298 case WIDGET_CURSOR:
299 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
300 radio_callback (r, WIDGET_FOCUS, ' ');
301 widget_move (&r->widget, r->pos, 1);
302 break;
304 case WIDGET_UNFOCUS:
305 case WIDGET_FOCUS:
306 case WIDGET_DRAW:
307 for (i = 0; i < r->count; i++){
308 register unsigned char* cp;
309 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
310 widget_move (&r->widget, i, 0);
312 printw("(%c) ", (r->sel == i) ? '*' : ' ');
313 for (cp = r->texts[i]; *cp; cp++)
315 if (*cp == '&')
317 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
318 ? HOT_FOCUSC : HOT_NORMALC);
319 addch(*++cp);
320 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
322 else
323 addch(*cp);
326 return 1;
327 break;
329 return default_proc (Msg, Par);
332 static int
333 radio_event (Gpm_Event *event, WRadio *r)
335 if (event->type & (GPM_DOWN|GPM_UP)){
336 Dlg_head *h = r->widget.parent;
338 r->pos = event->y - 1;
339 dlg_select_widget (h, r);
340 if (event->type & GPM_UP){
341 radio_callback (r, WIDGET_KEY, ' ');
342 radio_callback (r, WIDGET_FOCUS, 0);
343 (*h->callback) (h, ' ', DLG_POST_KEY);
344 return MOU_NORMAL;
347 return MOU_NORMAL;
350 WRadio *
351 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
353 WRadio *r = g_new (WRadio, 1);
354 int i, max, m;
356 /* Compute the longest string */
357 max = 0;
358 for (i = 0; i < count; i++){
359 m = strlen (texts [i]);
360 if (m > max)
361 max = m;
364 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
365 0, (mouse_h) radio_event, tkname);
366 r->state = 1;
367 r->pos = 0;
368 r->sel = 0;
369 r->count = count;
370 r->texts = texts;
371 r->upper_letter_is_hotkey = use_hotkey;
372 widget_want_hotkey (r->widget, 1);
374 return r;
378 /* Checkbutton widget */
380 static int check_event (Gpm_Event *event, WCheck *b);
382 static int
383 check_callback (WCheck *c, int Msg, int Par)
385 Dlg_head *h = c->widget.parent;
387 switch (Msg) {
388 case WIDGET_INIT:
389 return 1;
391 case WIDGET_HOTKEY:
392 if (c->hotkey==Par ||
393 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
394 check_callback (c, WIDGET_KEY, ' '); /* make action */
395 return 1;
396 } else
397 return 0;
399 case WIDGET_KEY:
400 if (Par != ' ')
401 break;
402 c->state ^= C_BOOL;
403 c->state ^= C_CHANGE;
404 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
405 check_callback (c, WIDGET_FOCUS, ' ');
406 return 1;
408 case WIDGET_CURSOR:
409 widget_move (&c->widget, 0, 1);
410 break;
412 case WIDGET_FOCUS:
413 case WIDGET_UNFOCUS:
414 case WIDGET_DRAW:
415 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
416 widget_move (&c->widget, 0, 0);
417 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
419 if (c->hotpos >= 0){
420 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
421 widget_move (&c->widget, 0, + c->hotpos+4);
422 addch ((unsigned char)c->text [c->hotpos]);
424 return 1;
426 return default_proc (Msg, Par);
429 static int
430 check_event (Gpm_Event *event, WCheck *c)
432 if (event->type & (GPM_DOWN|GPM_UP)){
433 Dlg_head *h = c->widget.parent;
435 dlg_select_widget (h, c);
436 if (event->type & GPM_UP){
437 check_callback (c, WIDGET_KEY, ' ');
438 check_callback (c, WIDGET_FOCUS, 0);
439 (*h->callback) (h, ' ', DLG_POST_KEY);
440 return MOU_NORMAL;
443 return MOU_NORMAL;
446 static void
447 check_destroy (WCheck *c)
449 g_free (c->text);
452 WCheck *
453 check_new (int y, int x, int state, char *text, char *tkname)
455 WCheck *c = g_new (WCheck, 1);
456 char *s, *t;
458 init_widget (&c->widget, y, x, 1, strlen (text),
459 (callback_fn)check_callback,
460 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
461 c->state = state ? C_BOOL : 0;
462 c->text = g_strdup (text);
463 c->hotkey = 0;
464 c->hotpos = -1;
465 widget_want_hotkey (c->widget, 1);
467 /* Scan for the hotkey */
468 for (s = text, t = c->text; *s; s++, t++){
469 if (*s != '&'){
470 *t = *s;
471 continue;
473 s++;
474 if (*s){
475 c->hotkey = tolower (*s);
476 c->hotpos = t - c->text;
478 *t = *s;
480 *t = 0;
481 return c;
485 /* Label widget */
487 static int
488 label_callback (WLabel *l, int Msg, int Par)
490 Dlg_head *h = l->widget.parent;
492 if (Msg == WIDGET_INIT)
493 return 1;
495 /* We don't want to get the focus */
496 if (Msg == WIDGET_FOCUS)
497 return 0;
498 if (Msg == WIDGET_DRAW && l->text){
499 char *p = l->text, *q, c = 0;
500 int y = 0;
501 if (l->transparent)
502 attrset (DEFAULT_COLOR);
503 else
504 attrset (NORMALC);
505 for (;;){
506 int xlen;
508 q = strchr (p, '\n');
509 if (q){
510 c = *q;
511 *q = 0;
513 widget_move (&l->widget, y, 0);
514 printw ("%s", p);
515 xlen = l->widget.cols - strlen (p);
516 if (xlen > 0)
517 printw ("%*s", xlen, " ");
518 if (!q)
519 break;
520 *q = c;
521 p = q + 1;
522 y++;
524 return 1;
526 return default_proc (Msg, Par);
529 void
530 label_set_text (WLabel *label, char *text)
532 int newcols = label->widget.cols;
534 if (label->text && text && !strcmp (label->text, text))
535 return; /* Flickering is not nice */
537 if (label->text){
538 g_free (label->text);
540 if (text){
541 label->text = g_strdup (text);
542 if (label->auto_adjust_cols) {
543 newcols = strlen (text);
544 if (newcols > label->widget.cols)
545 label->widget.cols = newcols;
547 } else
548 label->text = 0;
550 if (label->widget.parent)
551 label_callback (label, WIDGET_DRAW, 0);
553 if (newcols < label->widget.cols)
554 label->widget.cols = newcols;
557 static void
558 label_destroy (WLabel *l)
560 if (l->text)
561 g_free (l->text);
564 WLabel *
565 label_new (int y, int x, const char *text, char *tkname)
567 WLabel *l;
568 int width;
570 /* Multiline labels are immutable - no need to compute their sizes */
571 if (!text || strchr(text, '\n'))
572 width = 1;
573 else
574 width = strlen (text);
576 l = g_new (WLabel, 1);
577 init_widget (&l->widget, y, x, 1, width,
578 (callback_fn) label_callback,
579 (destroy_fn) label_destroy, NULL, tkname);
580 l->text = text ? g_strdup (text) : 0;
581 l->auto_adjust_cols = 1;
582 l->transparent = 0;
583 widget_want_cursor (l->widget, 0);
584 return l;
588 /* Gauge widget (progress indicator) */
589 /* Currently width is hardcoded here for text mode */
590 #define gauge_len 47
592 static int
593 gauge_callback (WGauge *g, int Msg, int Par)
595 Dlg_head *h = g->widget.parent;
597 if (Msg == WIDGET_INIT)
598 return 1;
600 /* We don't want to get the focus */
601 if (Msg == WIDGET_FOCUS)
602 return 0;
604 if (Msg == WIDGET_DRAW){
605 widget_move (&g->widget, 0, 0);
606 attrset (NORMALC);
607 if (!g->shown)
608 printw ("%*s", gauge_len, "");
609 else {
610 long percentage, columns;
611 long total = g->max, done = g->current;
613 if (total <= 0 || done < 0) {
614 done = 0;
615 total = 100;
617 if (done > total)
618 done = total;
619 while (total > 65535) {
620 total /= 256;
621 done /= 256;
623 percentage = (200 * done / total + 1) / 2;
624 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
625 addch ('[');
626 attrset (GAUGE_COLOR);
627 printw ("%*s", columns, "");
628 attrset (NORMALC);
629 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
631 return 1;
633 return default_proc (Msg, Par);
636 void
637 gauge_set_value (WGauge *g, int max, int current)
639 if (g->current == current && g->max == max)
640 return; /* Do not flicker */
641 if (max == 0)
642 max = 1; /* I do not like division by zero :) */
644 g->current = current;
645 g->max = max;
646 gauge_callback (g, WIDGET_DRAW, 0);
649 void
650 gauge_show (WGauge *g, int shown)
652 if (g->shown == shown)
653 return;
654 g->shown = shown;
655 gauge_callback (g, WIDGET_DRAW, 0);
658 static void
659 gauge_destroy (WGauge *g)
661 /* nothing */
664 WGauge *
665 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
667 WGauge *g = g_new (WGauge, 1);
669 init_widget (&g->widget, y, x, 1, gauge_len,
670 (callback_fn) gauge_callback,
671 (destroy_fn) gauge_destroy, NULL, tkname);
672 g->shown = shown;
673 if (max == 0)
674 max = 1; /* I do not like division by zero :) */
675 g->max = max;
676 g->current = current;
677 widget_want_cursor (g->widget, 0);
678 return g;
682 /* Input widget */
684 /* {{{ history button */
686 #define LARGE_HISTORY_BUTTON 1
688 #ifdef LARGE_HISTORY_BUTTON
689 # define HISTORY_BUTTON_WIDTH 3
690 #else
691 # define HISTORY_BUTTON_WIDTH 1
692 #endif
694 #define should_show_history_button(in) \
695 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
697 static void draw_history_button (WInput * in)
699 char c;
700 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
701 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
702 #ifdef LARGE_HISTORY_BUTTON
704 Dlg_head *h;
705 h = in->widget.parent;
706 #if 0
707 attrset (NORMALC); /* button has the same colour as other buttons */
708 addstr ("[ ]");
709 attrset (HOT_NORMALC);
710 #else
711 attrset (NORMAL_COLOR);
712 addstr ("[ ]");
713 /* Too distracting: attrset (MARKED_COLOR); */
714 #endif
715 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
716 addch (c);
718 #else
719 attrset (MARKED_COLOR);
720 addch (c);
721 #endif
724 /* }}} history button */
727 /* Input widgets now have a global kill ring */
728 /* Pointer to killed data */
729 static char *kill_buffer = 0;
731 void
732 update_input (WInput *in, int clear_first)
734 int has_history = 0;
735 int i, j;
736 unsigned char c;
737 int buf_len = strlen (in->buffer);
739 if (should_show_history_button (in))
740 has_history = HISTORY_BUTTON_WIDTH;
742 if (in->disable_update)
743 return;
745 /* Make the point visible */
746 if ((in->point < in->first_shown) ||
747 (in->point >= in->first_shown+in->field_len - has_history)){
748 in->first_shown = in->point - (in->field_len / 3);
749 if (in->first_shown < 0)
750 in->first_shown = 0;
753 /* Adjust the mark */
754 if (in->mark > buf_len)
755 in->mark = buf_len;
757 if (has_history)
758 draw_history_button (in);
760 attrset (in->color);
762 widget_move (&in->widget, 0, 0);
763 for (i = 0; i < in->field_len - has_history; i++)
764 addch (' ');
765 widget_move (&in->widget, 0, 0);
767 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
768 c = in->buffer [j++];
769 c = is_printable (c) ? c : '.';
770 if (in->is_password)
771 c = '*';
772 addch (c);
774 widget_move (&in->widget, 0, in->point - in->first_shown);
776 if (clear_first)
777 in->first = 0;
780 void
781 winput_set_origin (WInput *in, int x, int field_len)
783 in->widget.x = x;
784 in->field_len = in->widget.cols = field_len;
785 update_input (in, 0);
788 /* {{{ history saving and loading */
790 int num_history_items_recorded = 60;
793 This loads and saves the history of an input line to and from the
794 widget. It is called with the widgets tk name on creation of the
795 widget, and returns the GList list. It stores histories in the file
796 ~/.mc/history in using the profile code.
798 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
799 function) then input_new assigns the default text to be the last text
800 entered, or "" if not found.
803 GList *
804 history_get (char *input_name)
806 int i;
807 GList *hist;
808 char *profile;
810 hist = NULL;
812 if (!num_history_items_recorded) /* this is how to disable */
813 return 0;
814 if (!input_name)
815 return 0;
816 if (!*input_name)
817 return 0;
818 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
819 for (i = 0;; i++) {
820 char key_name[BUF_TINY];
821 char this_entry[BUF_LARGE];
822 g_snprintf (key_name, sizeof (key_name), "%d", i);
823 GetPrivateProfileString (input_name, key_name, "", this_entry,
824 sizeof (this_entry), profile);
825 if (!*this_entry)
826 break;
828 hist = list_append_unique (hist, g_strdup (this_entry));
830 g_free (profile);
832 /* return pointer to the last entry in the list */
833 hist = g_list_last (hist);
835 return hist;
838 void
839 history_put (char *input_name, GList *h)
841 int i;
842 char *profile;
844 if (!input_name)
845 return;
847 if (!*input_name)
848 return;
850 if (!h)
851 return;
853 if (!num_history_items_recorded) /* this is how to disable */
854 return;
856 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
858 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
859 close (i);
861 /* Make sure the history is only readable by the user */
862 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT) {
863 g_free (profile);
864 return;
867 /* go to end of list */
868 h = g_list_last (h);
870 /* go back 60 places */
871 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
872 h = g_list_previous (h);
874 if (input_name)
875 profile_clean_section (input_name, profile);
877 /* dump histories into profile */
878 for (i = 0; h; h = g_list_next (h)) {
879 char *text;
881 text = (char *) h->data;
883 /* We shouldn't have null entries, but let's be sure */
884 if (text && *text) {
885 char key_name[BUF_TINY];
886 g_snprintf (key_name, sizeof (key_name), "%d", i++);
887 WritePrivateProfileString (input_name, key_name, text,
888 profile);
892 g_free (profile);
895 /* }}} history saving and loading */
898 /* {{{ history display */
900 static char *
901 i18n_htitle (void)
903 static char *history_title = NULL;
905 if (history_title == NULL)
906 history_title = _(" History ");
907 return history_title;
910 static inline int listbox_fwd (WListbox *l);
912 char *
913 show_hist (GList *history, int widget_x, int widget_y)
915 GList *hi, *z;
916 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
917 int x, y, w, h;
918 char *q, *r = 0;
919 Dlg_head *query_dlg;
920 WListbox *query_list;
922 z = history;
923 if (!z)
924 return 0;
926 z = g_list_first (history);
927 hi = z;
928 while (hi) {
929 if ((i = strlen ((char *) hi->data)) > maxlen)
930 maxlen = i;
931 count++;
932 hi = g_list_next (hi);
935 y = widget_y;
936 h = count + 2;
937 if (h <= y || y > LINES - 6) {
938 h = min (h, y - 1);
939 y -= h;
940 } else {
941 y++;
942 h = min (h, LINES - y);
945 if (widget_x > 2)
946 x = widget_x - 2;
947 else
948 x = 0;
949 if ((w = maxlen + 4) + x > COLS) {
950 w = min (w, COLS);
951 x = COLS - w;
954 query_dlg =
955 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
956 i18n_htitle (), DLG_COMPACT);
957 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
958 add_widget (query_dlg, query_list);
959 hi = z;
960 if (y < widget_y) {
961 /* traverse */
962 while (hi) {
963 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
964 hi = g_list_next (hi);
966 while (listbox_fwd (query_list));
967 } else {
968 /* traverse backwards */
969 hi = g_list_last (history);
970 while (hi) {
971 listbox_add_item (query_list, 0, 0, (char *) hi->data, NULL);
972 hi = g_list_previous (hi);
975 run_dlg (query_dlg);
976 q = NULL;
977 if (query_dlg->ret_value != B_CANCEL) {
978 listbox_get_current (query_list, &q, NULL);
979 if (q)
980 r = g_strdup (q);
982 destroy_dlg (query_dlg);
983 return r;
986 static void do_show_hist (WInput * in)
988 char *r;
989 r = show_hist (in->history, in->widget.x, in->widget.y);
990 if (r) {
991 assign_text (in, r);
992 g_free (r);
996 /* }}} history display */
998 static void
999 input_destroy (WInput *in)
1001 if (!in){
1002 fprintf (stderr, "Internal error: null Input *\n");
1003 exit (1);
1006 new_input (in);
1008 if (in->history){
1009 if (!in->is_password) /* don't save passwords ;-) */
1010 history_put (in->history_name, in->history);
1012 g_list_foreach (in->history, (GFunc) g_free, NULL);
1013 g_list_free (in->history);
1016 g_free (in->buffer);
1017 free_completions (in);
1018 if (in->history_name)
1019 g_free (in->history_name);
1022 static char disable_update = 0;
1024 void
1025 input_disable_update (WInput *in)
1027 in->disable_update++;
1030 void
1031 input_enable_update (WInput *in)
1033 in->disable_update--;
1034 update_input (in, 0);
1037 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1040 push_history (WInput *in, char *text)
1042 static int i18n;
1043 /* input widget where urls with passwords are entered without any
1044 vfs prefix */
1045 static const char *password_input_fields[] = {
1046 N_(" Link to a remote machine "),
1047 N_(" FTP to machine "),
1048 N_(" SMB link to machine ")
1050 char *t;
1051 char *p;
1052 int i;
1054 if (!i18n) {
1055 i18n = 1;
1056 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1057 password_input_fields[i] = _(password_input_fields[i]);
1060 for (p = text; *p == ' ' || *p == '\t'; p++);
1061 if (!*p)
1062 return 0;
1064 if (in->history) {
1065 /* Avoid duplicated entries */
1066 in->history = g_list_last (in->history);
1067 if (!strcmp ((char *) in->history->data, text))
1068 return 1;
1071 t = g_strdup (text);
1073 if (in->history_name) {
1074 p = in->history_name + 3;
1075 for (i = 0; i < ELEMENTS (password_input_fields); i++)
1076 if (strcmp (p, password_input_fields[i]) == 0)
1077 break;
1078 if (i < ELEMENTS (password_input_fields))
1079 strip_password (t, 0);
1080 else
1081 strip_password (t, 1);
1084 in->history = list_append_unique (in->history, t);
1085 in->need_push = 0;
1087 return 2;
1090 #undef ELEMENTS
1092 /* Cleans the input line and adds the current text to the history */
1093 void
1094 new_input (WInput *in)
1096 if (in->buffer)
1097 push_history (in, in->buffer);
1098 in->need_push = 1;
1099 in->buffer [0] = 0;
1100 in->point = 0;
1101 in->mark = 0;
1102 free_completions (in);
1103 update_input (in, 0);
1106 static int
1107 insert_char (WInput *in, int c_code)
1109 int i;
1111 if (c_code == -1)
1112 return 0;
1114 in->need_push = 1;
1115 if (strlen (in->buffer)+1 == in->current_max_len){
1116 /* Expand the buffer */
1117 char *narea = g_malloc (in->current_max_len + in->field_len);
1118 if (narea){
1119 char *p = in->buffer;
1121 strcpy (narea, in->buffer);
1122 in->buffer = narea;
1123 in->current_max_len += in->field_len;
1124 g_free (p);
1127 if (strlen (in->buffer)+1 < in->current_max_len){
1128 int l = strlen (&in->buffer [in->point]);
1129 for (i = l+1; i > 0; i--)
1130 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1131 in->buffer [in->point] = c_code;
1132 in->point++;
1134 return 1;
1137 static void
1138 beginning_of_line (WInput *in)
1140 in->point = 0;
1143 static void
1144 end_of_line (WInput *in)
1146 in->point = strlen (in->buffer);
1149 static void
1150 backward_char (WInput *in)
1152 if (in->point)
1153 in->point--;
1156 static void
1157 forward_char (WInput *in)
1159 if (in->buffer [in->point])
1160 in->point++;
1163 static void
1164 forward_word (WInput *in)
1166 unsigned char *p = in->buffer+in->point;
1168 while (*p && (isspace (*p) || ispunct (*p)))
1169 p++;
1170 while (*p && isalnum (*p))
1171 p++;
1172 in->point = p - in->buffer;
1175 static void
1176 backward_word (WInput *in)
1178 unsigned char *p = in->buffer+in->point;
1180 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1181 p--;
1182 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1183 p--;
1184 in->point = p - in->buffer;
1187 static void
1188 key_left (WInput *in)
1190 backward_char (in);
1193 static void
1194 key_ctrl_left (WInput *in)
1196 backward_word (in);
1199 static void
1200 key_right (WInput *in)
1202 forward_char (in);
1205 static void
1206 key_ctrl_right (WInput *in)
1208 forward_word (in);
1210 static void
1211 backward_delete (WInput *in)
1213 int i;
1215 if (!in->point)
1216 return;
1217 for (i = in->point; in->buffer [i-1]; i++)
1218 in->buffer [i-1] = in->buffer [i];
1219 in->need_push = 1;
1220 in->point--;
1223 static void
1224 delete_char (WInput *in)
1226 int i;
1228 for (i = in->point; in->buffer [i]; i++)
1229 in->buffer [i] = in->buffer [i+1];
1230 in->need_push = 1;
1233 static void
1234 copy_region (WInput *in, int x_first, int x_last)
1236 int first = min (x_first, x_last);
1237 int last = max (x_first, x_last);
1239 if (last == first)
1240 return;
1242 if (kill_buffer)
1243 g_free (kill_buffer);
1245 kill_buffer = g_malloc (last-first + 1);
1246 strncpy (kill_buffer, in->buffer+first, last-first);
1247 kill_buffer [last-first] = 0;
1250 static void
1251 delete_region (WInput *in, int x_first, int x_last)
1253 int first = min (x_first, x_last);
1254 int last = max (x_first, x_last);
1256 in->point = first;
1257 in->mark = first;
1258 strcpy (&in->buffer [first], &in->buffer [last]);
1259 in->need_push = 1;
1262 static void
1263 kill_word (WInput *in)
1265 int old_point = in->point;
1266 int new_point;
1268 forward_word (in);
1269 new_point = in->point;
1270 in->point = old_point;
1272 copy_region (in, old_point, new_point);
1273 delete_region (in, old_point, new_point);
1274 in->need_push = 1;
1277 static void
1278 back_kill_word (WInput *in)
1280 int old_point = in->point;
1281 int new_point;
1283 backward_word (in);
1284 new_point = in->point;
1285 in->point = old_point;
1287 copy_region (in, old_point, new_point);
1288 delete_region (in, old_point, new_point);
1289 in->need_push = 1;
1292 static void
1293 set_mark (WInput *in)
1295 in->mark = in->point;
1298 static void
1299 kill_save (WInput *in)
1301 copy_region (in, in->mark, in->point);
1304 static void
1305 kill_region (WInput *in)
1307 kill_save (in);
1308 delete_region (in, in->point, in->mark);
1311 static void
1312 yank (WInput *in)
1314 char *p;
1316 if (!kill_buffer)
1317 return;
1318 for (p = kill_buffer; *p; p++)
1319 insert_char (in, *p);
1322 static void
1323 kill_line (WInput *in)
1325 if (kill_buffer)
1326 g_free (kill_buffer);
1327 kill_buffer = g_strdup (&in->buffer [in->point]);
1328 in->buffer [in->point] = 0;
1331 void
1332 assign_text (WInput *in, char *text)
1334 free_completions (in);
1335 g_free (in->buffer);
1336 in->buffer = g_strdup (text); /* was in->buffer->text */
1337 in->current_max_len = strlen (in->buffer) + 1;
1338 in->point = strlen (in->buffer);
1339 in->mark = 0;
1340 in->need_push = 1;
1343 static void
1344 hist_prev (WInput *in)
1346 if (!in->history)
1347 return;
1349 if (in->need_push) {
1350 switch (push_history (in, in->buffer)) {
1351 case 2:
1352 in->history = g_list_previous (in->history);
1353 break;
1354 case 1:
1355 if (in->history->prev)
1356 in->history = g_list_previous (in->history);
1357 break;
1358 case 0:
1359 break;
1361 } else if (in->history->prev)
1362 in->history = g_list_previous (in->history);
1363 else
1364 return;
1365 assign_text (in, (char *) in->history->data);
1366 in->need_push = 0;
1369 static void
1370 hist_next (WInput *in)
1372 if (in->need_push) {
1373 switch (push_history (in, in->buffer)) {
1374 case 2:
1375 assign_text (in, "");
1376 return;
1377 case 0:
1378 return;
1382 if (!in->history)
1383 return;
1385 if (!in->history->next) {
1386 assign_text (in, "");
1387 return;
1390 in->history = g_list_next (in->history);
1391 assign_text (in, (char *) in->history->data);
1392 in->need_push = 0;
1395 static const struct {
1396 int key_code;
1397 void (*fn)(WInput *in);
1398 } input_map [] = {
1399 /* Motion */
1400 { XCTRL('a'), beginning_of_line },
1401 { KEY_HOME, beginning_of_line },
1402 { KEY_A1, beginning_of_line },
1403 { XCTRL('e'), end_of_line },
1404 { KEY_END, end_of_line },
1405 { KEY_C1, end_of_line },
1406 { KEY_LEFT, key_left },
1407 { KEY_LEFT | KEY_M_CTRL, key_ctrl_left },
1408 { XCTRL('b'), backward_char },
1409 { ALT('b'), backward_word },
1410 { KEY_RIGHT, key_right },
1411 { KEY_RIGHT | KEY_M_CTRL, key_ctrl_right },
1412 { XCTRL('f'), forward_char },
1413 { ALT('f'), forward_word },
1415 /* Editing */
1416 { KEY_BACKSPACE, backward_delete },
1417 { KEY_DC, delete_char },
1418 { ALT('d'), kill_word },
1419 { ALT(KEY_BACKSPACE), back_kill_word },
1421 /* Region manipulation */
1422 { 0, set_mark },
1423 { XCTRL('w'), kill_region },
1424 { ALT('w'), kill_save },
1425 { XCTRL('y'), yank },
1426 { XCTRL('k'), kill_line },
1428 /* History */
1429 { ALT('p'), hist_prev },
1430 { ALT('n'), hist_next },
1431 { ALT('h'), do_show_hist },
1433 /* Completion */
1434 { ALT('\t'), complete },
1436 { 0, 0 }
1439 /* This function is a test for a special input key used in complete.c */
1440 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1441 and 2 if it is a complete key */
1443 is_in_input_map (WInput *in, int c_code)
1445 int i;
1447 for (i = 0; input_map [i].fn; i++)
1448 if (c_code == input_map [i].key_code) {
1449 if (input_map [i].fn == complete)
1450 return 2;
1451 else
1452 return 1;
1454 return 0;
1457 static void
1458 port_region_marked_for_delete (WInput *in)
1460 *in->buffer = 0;
1461 in->point = 0;
1462 in->first = 0;
1466 handle_char (WInput *in, int c_code)
1468 int i;
1469 int v;
1471 v = 0;
1473 if (quote){
1474 free_completions (in);
1475 v = insert_char (in, c_code);
1476 update_input (in, 1);
1477 quote = 0;
1478 return v;
1481 for (i = 0; input_map [i].fn; i++){
1482 if (c_code == input_map [i].key_code){
1483 if (input_map [i].fn != complete)
1484 free_completions (in);
1485 (*input_map [i].fn)(in);
1486 v = 1;
1487 break;
1490 if (!input_map [i].fn){
1491 if (c_code > 255 || !is_printable (c_code))
1492 return 0;
1493 if (in->first){
1494 port_region_marked_for_delete (in);
1496 free_completions (in);
1497 v = insert_char (in, c_code);
1499 if (!disable_update)
1500 update_input (in, 1);
1501 return v;
1504 /* Inserts text in input line */
1505 void
1506 stuff (WInput *in, char *text, int insert_extra_space)
1508 input_disable_update (in);
1509 while (*text)
1510 handle_char (in, *text++);
1511 if (insert_extra_space)
1512 handle_char (in, ' ');
1513 input_enable_update (in);
1514 update_input (in, 1);
1517 void
1518 input_set_point (WInput *in, int pos)
1520 if (pos > in->current_max_len)
1521 pos = in->current_max_len;
1522 if (pos != in->point)
1523 free_completions (in);
1524 in->point = pos;
1525 update_input (in, 1);
1529 input_callback (WInput *in, int Msg, int Par)
1531 int v;
1532 Dlg_head *h = in->widget.parent;
1534 switch (Msg) {
1535 case WIDGET_INIT:
1536 return 1;
1538 case WIDGET_KEY:
1539 if (Par == XCTRL ('q')) {
1540 quote = 1;
1541 v = handle_char (in, mi_getch ());
1542 quote = 0;
1543 return v;
1545 if (Par == KEY_UP || Par == KEY_DOWN || Par == ESC_CHAR
1546 || Par == KEY_F (10) || Par == XCTRL ('g'))
1547 return 0; /* We don't handle up/down */
1549 if (Par == '\n') {
1550 dlg_one_down (h);
1551 return 1;
1554 /* When pasting multiline text, insert literal Enter */
1555 if ((Par & ~KEY_M_MASK) == '\n') {
1556 quote = 1;
1557 v = handle_char (in, '\n');
1558 quote = 0;
1559 return v;
1562 return handle_char (in, Par);
1564 case WIDGET_FOCUS:
1565 case WIDGET_UNFOCUS:
1566 case WIDGET_DRAW:
1567 update_input (in, 0);
1568 break;
1569 case WIDGET_CURSOR:
1570 widget_move (&in->widget, 0, in->point - in->first_shown);
1571 return 1;
1574 return default_proc (Msg, Par);
1577 static int
1578 input_event (Gpm_Event * event, WInput * in)
1580 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1581 dlg_select_widget (in->widget.parent, in);
1583 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1584 && should_show_history_button (in)) {
1585 do_show_hist (in);
1586 } else {
1587 in->point = strlen (in->buffer);
1588 if (event->x - in->first_shown - 1 < in->point)
1589 in->point = event->x - in->first_shown - 1;
1590 if (in->point < 0)
1591 in->point = 0;
1593 update_input (in, 1);
1595 return MOU_NORMAL;
1598 WInput *
1599 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1601 WInput *in = g_new (WInput, 1);
1602 int initial_buffer_len;
1604 init_widget (&in->widget, y, x, 1, len,
1605 (callback_fn) input_callback,
1606 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1608 /* history setup */
1609 in->history = NULL;
1610 in->history_name = 0;
1611 if (tkname) {
1612 if (*tkname) {
1613 in->history_name = g_strdup (tkname);
1614 in->history = history_get (tkname);
1618 if (!def_text)
1619 def_text = "";
1621 if (def_text == INPUT_LAST_TEXT) {
1622 def_text = "";
1623 if (in->history)
1624 if (in->history->data)
1625 def_text = (char *) in->history->data;
1627 initial_buffer_len = 1 + max (len, strlen (def_text));
1628 in->widget.options |= W_IS_INPUT;
1629 in->completions = NULL;
1630 in->completion_flags =
1631 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1632 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1633 in->current_max_len = initial_buffer_len;
1634 in->buffer = g_malloc (initial_buffer_len);
1635 in->color = color;
1636 in->field_len = len;
1637 in->first = 1;
1638 in->first_shown = 0;
1639 in->disable_update = 0;
1640 in->mark = 0;
1641 in->need_push = 1;
1642 in->is_password = 0;
1644 strcpy (in->buffer, def_text);
1645 in->point = strlen (in->buffer);
1646 return in;
1650 /* Listbox widget */
1652 /* Should draw the scrollbar, but currently draws only
1653 * indications that there is more information
1655 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1657 static void
1658 listbox_drawscroll (WListbox *l)
1660 extern int slow_terminal;
1661 int line;
1662 int i, top;
1663 int max_line = l->height-1;
1665 /* Are we at the top? */
1666 widget_move (&l->widget, 0, l->width);
1667 if (l->list == l->top)
1668 one_vline ();
1669 else
1670 addch ('^');
1672 /* Are we at the bottom? */
1673 widget_move (&l->widget, max_line, l->width);
1674 top = listbox_cdiff (l->list, l->top);
1675 if ((top + l->height == l->count) || l->height >= l->count)
1676 one_vline ();
1677 else
1678 addch ('v');
1680 /* Now draw the nice relative pointer */
1681 if (l->count)
1682 line = 1+ ((l->pos * (l->height-2)) / l->count);
1683 else
1684 line = 0;
1686 for (i = 1; i < max_line; i++){
1687 widget_move (&l->widget, i, l->width);
1688 if (i != line)
1689 one_vline ();
1690 else
1691 addch ('*');
1695 static void
1696 listbox_draw (WListbox *l, int focused)
1698 WLEntry *e;
1699 int i;
1700 int sel_line;
1701 Dlg_head *h = l->widget.parent;
1702 int normalc = NORMALC;
1703 int selc;
1704 char *text;
1706 if (focused){
1707 selc = FOCUSC;
1708 } else {
1709 selc = HOT_FOCUSC;
1711 sel_line = -1;
1713 for (e = l->top, i = 0; (i < l->height); i++){
1715 /* Display the entry */
1716 if (e == l->current && sel_line == -1){
1717 sel_line = i;
1718 attrset (selc);
1719 } else
1720 attrset (normalc);
1722 widget_move (&l->widget, i, 0);
1724 if ((i > 0 && e == l->list) || !l->list)
1725 text = "";
1726 else {
1727 text = e->text;
1728 e = e->next;
1730 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1732 l->cursor_y = sel_line;
1733 if (!l->scrollbar)
1734 return;
1735 attrset (normalc);
1736 listbox_drawscroll (l);
1739 /* Returns the number of items between s and e,
1740 must be on the same linked list */
1741 static int
1742 listbox_cdiff (WLEntry *s, WLEntry *e)
1744 int count;
1746 for (count = 0; s != e; count++)
1747 s = s->next;
1748 return count;
1751 static WLEntry *
1752 listbox_check_hotkey (WListbox *l, int key)
1754 int i;
1755 WLEntry *e;
1757 i = 0;
1758 e = l->list;
1759 if (!e)
1760 return 0;
1762 while (1){
1764 /* If we didn't find anything, return */
1765 if (i && e == l->list)
1766 return 0;
1768 if (e->hotkey == key)
1769 return e;
1771 i++;
1772 e = e->next;
1776 /* Used only for display updating, for avoiding line at a time scroll */
1777 void
1778 listbox_select_last (WListbox *l, int set_top)
1780 if (l->list){
1781 l->current = l->list->prev;
1782 l->pos = l->count - 1;
1783 if (set_top)
1784 l->top = l->list->prev;
1788 void
1789 listbox_remove_list (WListbox *l)
1791 WLEntry *p, *q;
1793 if (!l->count)
1794 return;
1796 p = l->list;
1798 while (l->count--) {
1799 q = p->next;
1800 g_free (p->text);
1801 g_free (p);
1802 p = q;
1804 l->pos = l->count = 0;
1805 l->list = l->top = l->current = 0;
1809 * bor 30.10.96: added force flag to remove *last* entry as well
1810 * bor 30.10.96: corrected selection bug if last entry was removed
1813 void
1814 listbox_remove_current (WListbox *l, int force)
1816 WLEntry *p;
1818 /* Ok, note: this won't allow for emtpy lists */
1819 if (!force && (!l->count || l->count == 1))
1820 return;
1822 l->count--;
1823 p = l->current;
1825 if (l->count) {
1826 l->current->next->prev = l->current->prev;
1827 l->current->prev->next = l->current->next;
1828 if (p->next == l->list) {
1829 l->current = p->prev;
1830 l->pos--;
1832 else
1833 l->current = p->next;
1835 if (p == l->list)
1836 l->list = l->top = p->next;
1837 } else {
1838 l->pos = 0;
1839 l->list = l->top = l->current = 0;
1842 g_free (p->text);
1843 g_free (p);
1846 /* Makes *e the selected entry (sets current and pos) */
1847 void
1848 listbox_select_entry (WListbox *l, WLEntry *dest)
1850 WLEntry *e;
1851 int pos;
1852 int top_seen;
1854 top_seen = 0;
1856 /* Special case */
1857 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1859 if (e == l->top)
1860 top_seen = 1;
1862 if (e == dest){
1863 l->current = e;
1864 if (top_seen){
1865 while (listbox_cdiff (l->top, l->current) >= l->height)
1866 l->top = l->top->next;
1867 } else {
1868 l->top = l->current;
1870 l->pos = pos;
1871 return;
1874 /* If we are unable to find it, set decent values */
1875 l->current = l->top = l->list;
1876 l->pos = 0;
1879 /* Selects from base the pos element */
1880 static WLEntry *
1881 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1883 WLEntry *last = l->list->prev;
1885 if (base == last)
1886 return last;
1887 while (pos--){
1888 base = base->next;
1889 if (base == last)
1890 break;
1892 return base;
1895 static inline int
1896 listbox_back (WListbox *l)
1898 if (l->pos){
1899 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1900 return 1;
1902 return 0;
1905 static inline int
1906 listbox_fwd (WListbox *l)
1908 if (l->current != l->list->prev){
1909 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1910 return 1;
1912 return 0;
1915 /* Returns 1 if we want a redraw */
1916 static int
1917 listbox_key (WListbox *l, int key)
1919 int i;
1920 int j = 0;
1922 if (!l->list)
1923 return 0;
1925 switch (key){
1926 case KEY_HOME:
1927 case KEY_A1:
1928 l->current = l->top = l->list;
1929 l->pos = 0;
1930 return 1;
1932 case KEY_END:
1933 case KEY_C1:
1934 l->current = l->top = l->list->prev;
1935 for (i = min (l->height - 1, l->count - 1); i; i--)
1936 l->top = l->top->prev;
1937 l->pos = l->count - 1;
1938 return 1;
1940 case XCTRL('p'):
1941 case KEY_UP:
1942 listbox_back (l);
1943 return 1;
1945 case XCTRL('n'):
1946 case KEY_DOWN:
1947 listbox_fwd (l);
1948 return 1;
1950 case KEY_NPAGE:
1951 case XCTRL('v'):
1952 for (i = 0; i < l->height-1; i++)
1953 j |= listbox_fwd (l);
1954 return j > 0;
1956 case KEY_PPAGE:
1957 case ALT('v'):
1958 for (i = 0; i < l->height-1; i++)
1959 j |= listbox_back (l);
1960 return j > 0;
1962 return 0;
1965 static int listbox_event (Gpm_Event *event, WListbox *l);
1966 static int
1967 listbox_callback (WListbox *l, int msg, int par)
1969 WLEntry *e;
1970 /* int selected_color; Never used */
1971 int ret_code;
1973 switch (msg){
1974 case WIDGET_INIT:
1975 return 1;
1977 case WIDGET_HOTKEY:
1978 if ((e = listbox_check_hotkey (l, par)) != NULL){
1979 listbox_select_entry (l, e);
1981 /* Take the appropriate action */
1982 if (l->action == listbox_finish){
1983 l->widget.parent->running = 0;
1984 l->widget.parent->ret_value = B_ENTER;
1985 } else if (l->action == listbox_cback){
1986 if ((*l->cback)(l) == listbox_finish){
1987 l->widget.parent->running = 0;
1988 l->widget.parent->ret_value = B_ENTER;
1991 return 1;
1992 } else
1993 return 0;
1995 case WIDGET_KEY:
1996 if ((ret_code = listbox_key (l, par)))
1997 listbox_draw (l, 1);
1998 return ret_code;
2000 case WIDGET_CURSOR:
2001 widget_move (&l->widget, l->cursor_y, 0);
2002 return 1;
2004 case WIDGET_FOCUS:
2005 case WIDGET_UNFOCUS:
2006 case WIDGET_DRAW:
2007 listbox_draw (l, msg != WIDGET_UNFOCUS);
2008 return 1;
2010 return default_proc (msg, par);
2013 static int
2014 listbox_event (Gpm_Event *event, WListbox *l)
2016 int i;
2018 Dlg_head *h = l->widget.parent;
2020 /* Single click */
2021 if (event->type & GPM_DOWN)
2022 dlg_select_widget (l->widget.parent, l);
2023 if (!l->list)
2024 return MOU_NORMAL;
2025 if (event->type & (GPM_DOWN|GPM_DRAG)){
2026 if (event->x < 0 || event->x >= l->width)
2027 return MOU_REPEAT;
2028 if (event->y < 1)
2029 for (i = -event->y; i >= 0; i--)
2030 listbox_back (l);
2031 else if (event->y > l->height)
2032 for (i = event->y - l->height; i > 0; i--)
2033 listbox_fwd (l);
2034 else
2035 listbox_select_entry (l, listbox_select_pos (l, l->top,
2036 event->y - 1));
2038 /* We need to refresh ourselves since the dialog manager doesn't */
2039 /* know about this event */
2040 listbox_callback (l, WIDGET_DRAW, 0);
2041 mc_refresh ();
2042 return MOU_REPEAT;
2045 /* Double click */
2046 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2047 if (event->x < 0 || event->x >= l->width)
2048 return MOU_NORMAL;
2049 if (event->y < 1 || event->y > l->height)
2050 return MOU_NORMAL;
2052 dlg_select_widget (l->widget.parent, l);
2053 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2055 switch (l->action){
2056 case listbox_nothing:
2057 break;
2059 case listbox_finish:
2060 h->ret_value = B_ENTER;
2061 dlg_stop (h);
2062 return MOU_NORMAL;
2064 case listbox_cback:
2065 if ((*l->cback)(l) == listbox_finish)
2066 return MOU_NORMAL;
2069 return MOU_NORMAL;
2072 static void
2073 listbox_destroy (WListbox *l)
2075 WLEntry *n, *p = l->list;
2076 int i;
2078 for (i = 0; i < l->count; i++){
2079 n = p->next;
2080 g_free (p->text);
2081 g_free (p);
2082 p = n;
2086 WListbox *
2087 listbox_new (int y, int x, int width, int height,
2088 int action, lcback callback, char *tkname)
2090 WListbox *l = g_new (WListbox, 1);
2091 extern int slow_terminal;
2093 init_widget (&l->widget, y, x, height, width,
2094 (callback_fn)listbox_callback,
2095 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2097 l->list = l->top = l->current = 0;
2098 l->pos = 0;
2099 l->width = width;
2100 if (height <= 0)
2101 l->height = 1;
2102 else
2103 l->height = height;
2104 l->count = 0;
2105 l->top = 0;
2106 l->current= 0;
2107 l->cback = callback;
2108 l->action = action;
2109 l->allow_duplicates = 1;
2110 l->scrollbar = slow_terminal ? 0 : 1;
2111 widget_want_hotkey (l->widget, 1);
2113 return l;
2116 /* Listbox item adding function. They still lack a lot of functionality */
2117 /* any takers? */
2118 /* 1.11.96 bor: added pos argument to control placement of new entry */
2119 static void
2120 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2122 if (!l->list){
2123 l->list = e;
2124 l->top = e;
2125 l->current = e;
2126 e->next = l->list;
2127 e->prev = l->list;
2128 } else if (pos == LISTBOX_APPEND_AT_END) {
2129 e->next = l->list;
2130 e->prev = l->list->prev;
2131 l->list->prev->next = e;
2132 l->list->prev = e;
2133 } else if (pos == LISTBOX_APPEND_BEFORE){
2134 e->next = l->current;
2135 e->prev = l->current->prev;
2136 l->current->prev->next = e;
2137 l->current->prev = e;
2138 if (l->list == l->current) { /* move list one position down */
2139 l->list = e;
2140 l->top = e;
2142 } else if (pos == LISTBOX_APPEND_AFTER) {
2143 e->prev = l->current;
2144 e->next = l->current->next;
2145 l->current->next->prev = e;
2146 l->current->next = e;
2148 l->count++;
2151 char *
2152 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2153 void *data)
2155 WLEntry *entry;
2157 if (!l)
2158 return 0;
2160 if (!l->allow_duplicates)
2161 if (listbox_search_text (l, text))
2162 return 0;
2164 entry = g_new (WLEntry, 1);
2165 entry->text = g_strdup (text);
2166 entry->data = data;
2167 entry->hotkey = hotkey;
2169 listbox_append_item (l, entry, pos);
2171 return entry->text;
2174 /* Selects the nth entry in the listbox */
2175 void
2176 listbox_select_by_number (WListbox *l, int n)
2178 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2181 WLEntry *
2182 listbox_search_text (WListbox *l, char *text)
2184 WLEntry *e;
2186 e = l->list;
2187 if (!e)
2188 return NULL;
2190 do {
2191 if(!strcmp (e->text, text))
2192 return e;
2193 e = e->next;
2194 } while (e!=l->list);
2196 return NULL;
2199 /* Returns the current string text as well as the associated extra data */
2200 void
2201 listbox_get_current (WListbox *l, char **string, char **extra)
2203 if (!l->current){
2204 *string = 0;
2205 *extra = 0;
2207 if (string && l->current)
2208 *string = l->current->text;
2209 if (extra && l->current)
2210 *extra = l->current->data;
2213 static int
2214 buttonbar_callback (WButtonBar *bb, int msg, int par)
2216 int i;
2218 switch (msg){
2219 case WIDGET_INIT:
2220 return 1;
2222 case WIDGET_FOCUS:
2223 return 0;
2225 case WIDGET_HOTKEY:
2226 for (i = 0; i < 10; i++){
2227 if (par == KEY_F(i+1) && bb->labels [i].function){
2228 (*bb->labels [i].function)(bb->labels [i].data);
2229 return 1;
2232 return 0;
2234 case WIDGET_DRAW:
2235 if (!bb->visible)
2236 return 1;
2237 widget_move (&bb->widget, 0, 0);
2238 attrset (DEFAULT_COLOR);
2239 printw ("%-*s", bb->widget.cols, "");
2240 for (i = 0; i < COLS/8 && i < 10; i++){
2241 widget_move (&bb->widget, 0, i*8);
2242 attrset (DEFAULT_COLOR);
2243 printw ("%d", i+1);
2244 attrset (SELECTED_COLOR);
2245 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2246 bb->labels [i].text ? bb->labels [i].text : "");
2247 attrset (DEFAULT_COLOR);
2249 attrset (SELECTED_COLOR);
2250 return 1;
2252 return default_proc (msg, par);
2255 static void
2256 buttonbar_destroy (WButtonBar *bb)
2258 int i;
2260 for (i = 0; i < 10; i++){
2261 if (bb->labels [i].text)
2262 g_free (bb->labels [i].text);
2266 static int
2267 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2269 int button;
2271 if (!(event->type & GPM_UP))
2272 return MOU_NORMAL;
2273 if (event->y == 2)
2274 return MOU_NORMAL;
2275 button = event->x / 8;
2276 if (button < 10 && bb->labels [button].function)
2277 (*bb->labels [button].function)(bb->labels [button].data);
2278 return MOU_NORMAL;
2281 WButtonBar *
2282 buttonbar_new (int visible)
2284 int i;
2285 WButtonBar *bb = g_new (WButtonBar, 1);
2287 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2288 (callback_fn) buttonbar_callback,
2289 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2291 bb->visible = visible;
2292 for (i = 0; i < 10; i++){
2293 bb->labels [i].text = 0;
2294 bb->labels [i].function = 0;
2296 widget_want_hotkey (bb->widget, 1);
2297 widget_want_cursor (bb->widget, 0);
2299 return bb;
2302 static void
2303 set_label_text (WButtonBar * bb, int index, char *text)
2305 if (bb->labels[index - 1].text)
2306 g_free (bb->labels[index - 1].text);
2308 bb->labels[index - 1].text = g_strdup (text);
2311 /* Find ButtonBar widget in the dialog */
2312 WButtonBar *
2313 find_buttonbar (Dlg_head *h)
2315 WButtonBar *bb;
2317 bb = (WButtonBar *) find_widget_type (h, (callback_fn)
2318 buttonbar_callback);
2319 return bb;
2322 void
2323 define_label_data (Dlg_head *h, int idx, char *text, buttonbarfn cback,
2324 void *data)
2326 WButtonBar *bb = find_buttonbar (h);
2328 if (!bb)
2329 return;
2331 set_label_text (bb, idx, text);
2332 bb->labels[idx - 1].function = cback;
2333 bb->labels[idx - 1].data = data;
2336 void
2337 define_label (Dlg_head *h, int idx, char *text, void (*cback) (void))
2339 define_label_data (h, idx, text, (buttonbarfn) cback, 0);
2342 /* Redraw labels of the buttonbar */
2343 void
2344 redraw_labels (Dlg_head *h)
2346 WButtonBar *bb = find_buttonbar (h);
2348 if (!bb)
2349 return;
2351 send_message ((Widget *) bb, WIDGET_DRAW, 0);