Last fuzzy updated...
[midnight-commander.git] / src / widget.c
blob09e9c6b719faa9a9190869e11e80085af4e056a6
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 */
791 This loads and saves the history of an input line to and from the
792 widget. It is called with the widgets tk name on creation of the
793 widget, and returns the Hist list. It stores histories in the file
794 ~/.mc/history in using the profile code.
796 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
797 function) then input_new assigns the default text to be the last text
798 entered, or "" if not found.
801 int num_history_items_recorded = 60;
803 Hist *
804 history_get (char *input_name)
806 int i;
807 Hist *old, *new;
808 char *profile;
810 old = new = 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, sizeof (this_entry), profile);
824 if (!*this_entry)
825 break;
826 new = g_new0 (Hist, 1);
827 new->text = g_strdup (this_entry);
828 new->prev = old; /* set up list pointers */
829 if (old)
830 old->next = new;
831 old = new;
833 g_free (profile);
834 return new; /* return pointer to last entry in list */
837 void
838 history_put (char *input_name, Hist *h)
840 int i;
841 char *profile;
843 if (!input_name)
844 return;
846 if (!*input_name)
847 return;
849 if (!h)
850 return;
852 if (!num_history_items_recorded) /* this is how to disable */
853 return;
855 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
857 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
858 close (i);
859 /* Just in case I forgot to strip passwords somewhere -- Norbert */
860 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
861 g_free (profile);
862 return;
865 while (h->next) /* go to end of list */
866 h = h->next;
868 /* go back 60 places */
869 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
870 h = h->prev;
872 if (input_name)
873 profile_clean_section (input_name, profile);
875 /* dump histories into profile */
876 for (i = 0; h; h = h->next){
877 if (h->text){
879 /* probably aren't any null entries, but lets be sure */
880 if (*(h->text)){
881 char key_name[BUF_TINY];
882 g_snprintf (key_name, sizeof(key_name), "%d", i++);
883 WritePrivateProfileString (input_name, key_name, h->text, profile);
887 g_free (profile);
890 /* }}} history saving and loading */
893 /* {{{ history display */
895 static char *
896 i18n_htitle (void)
898 static char *history_title = NULL;
900 if (history_title == NULL)
901 history_title = _(" History ");
902 return history_title;
905 static inline int listbox_fwd (WListbox *l);
907 char *
908 show_hist (Hist * history, int widget_x, int widget_y)
910 Hist *hi, *z;
911 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
912 int x, y, w, h;
913 char *q, *r = 0;
914 Dlg_head *query_dlg;
915 WListbox *query_list;
917 z = history;
918 if (!z)
919 return 0;
921 while (z->prev) /* goto first */
922 z = z->prev;
923 hi = z;
924 while (hi) {
925 if ((i = strlen (hi->text)) > maxlen)
926 maxlen = i;
927 count++;
928 hi = hi->next;
931 y = widget_y;
932 h = count + 2;
933 if (h <= y || y > LINES - 6) {
934 h = min (h, y - 1);
935 y -= h;
936 } else {
937 y++;
938 h = min (h, LINES - y);
941 if (widget_x > 2)
942 x = widget_x - 2;
943 else
944 x = 0;
945 if ((w = maxlen + 4) + x > COLS) {
946 w = min (w, COLS);
947 x = COLS - w;
950 query_dlg =
951 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
952 i18n_htitle (), DLG_COMPACT);
953 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
954 add_widget (query_dlg, query_list);
955 hi = z;
956 if (y < widget_y) {
957 while (hi) { /* traverse */
958 listbox_add_item (query_list, 0, 0, hi->text, NULL);
959 hi = hi->next;
961 while (listbox_fwd (query_list));
962 } else {
963 while (hi->next)
964 hi = hi->next;
965 while (hi) { /* traverse backwards */
966 listbox_add_item (query_list, 0, 0, hi->text, NULL);
967 hi = hi->prev;
970 run_dlg (query_dlg);
971 q = NULL;
972 if (query_dlg->ret_value != B_CANCEL) {
973 listbox_get_current (query_list, &q, NULL);
974 if (q)
975 r = g_strdup (q);
977 destroy_dlg (query_dlg);
978 return r;
981 static void do_show_hist (WInput * in)
983 char *r;
984 r = show_hist (in->history, in->widget.x, in->widget.y);
985 if (r) {
986 assign_text (in, r);
987 g_free (r);
991 /* }}} history display */
993 static void
994 input_destroy (WInput *in)
996 if (!in){
997 fprintf (stderr, "Internal error: null Input *\n");
998 exit (1);
1001 new_input (in);
1002 if (in->history){
1003 Hist *current, *old;
1005 if (!in->is_password) /* don't save passwords ;-) */
1006 history_put (in->history_name, in->history);
1008 current = in->history;
1009 while (current->next)
1010 current = current->next;
1011 while (current){
1012 old = current;
1013 current = current->prev;
1014 g_free (old->text);
1015 g_free (old);
1018 g_free (in->buffer);
1019 free_completions (in);
1020 if (in->history_name)
1021 g_free (in->history_name);
1024 static char disable_update = 0;
1026 void
1027 input_disable_update (WInput *in)
1029 in->disable_update++;
1032 void
1033 input_enable_update (WInput *in)
1035 in->disable_update--;
1036 update_input (in, 0);
1039 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1042 push_history (WInput *in, char *text)
1044 static int i18n;
1045 /* input widget where urls with passwords are entered without any
1046 vfs prefix */
1047 static const char *password_input_fields[] = {
1048 N_(" Link to a remote machine "),
1049 N_(" FTP to machine "),
1050 N_(" SMB link to machine ")
1052 Hist *new;
1053 char *p;
1054 int i;
1056 if (!i18n) {
1057 i18n = 1;
1058 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1059 password_input_fields[i] = _(password_input_fields[i]);
1062 for (p = text; *p == ' ' || *p == '\t'; p++);
1063 if (!*p)
1064 return 0;
1065 if (in->history){
1066 while (in->history->next)
1067 in->history = in->history->next;
1068 if (!strcmp (in->history->text, text))
1069 return 1;
1070 new = g_new (Hist, 1);
1071 in->history->next = new;
1072 } else
1073 new = g_new (Hist, 1);
1074 in->need_push = 0;
1075 new->next = 0;
1076 new->prev = in->history;
1077 new->text = g_strdup (text);
1078 if (in->history_name) {
1079 p = in->history_name + 3;
1080 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1081 if (strcmp (p, password_input_fields[i]) == 0)
1082 break;
1083 if (i < ELEMENTS(password_input_fields))
1084 strip_password (new->text, 0);
1085 else
1086 strip_password (new->text, 1);
1089 in->history = new;
1090 return 2;
1093 #undef ELEMENTS
1095 /* Cleans the input line and adds the current text to the history */
1096 void
1097 new_input (WInput *in)
1099 if (in->buffer)
1100 push_history (in, in->buffer);
1101 in->need_push = 1;
1102 in->buffer [0] = 0;
1103 in->point = 0;
1104 in->mark = 0;
1105 free_completions (in);
1106 update_input (in, 0);
1109 static int
1110 insert_char (WInput *in, int c_code)
1112 int i;
1114 if (c_code == -1)
1115 return 0;
1117 in->need_push = 1;
1118 if (strlen (in->buffer)+1 == in->current_max_len){
1119 /* Expand the buffer */
1120 char *narea = g_malloc (in->current_max_len + in->field_len);
1121 if (narea){
1122 char *p = in->buffer;
1124 strcpy (narea, in->buffer);
1125 in->buffer = narea;
1126 in->current_max_len += in->field_len;
1127 g_free (p);
1130 if (strlen (in->buffer)+1 < in->current_max_len){
1131 int l = strlen (&in->buffer [in->point]);
1132 for (i = l+1; i > 0; i--)
1133 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1134 in->buffer [in->point] = c_code;
1135 in->point++;
1137 return 1;
1140 static void
1141 beginning_of_line (WInput *in)
1143 in->point = 0;
1146 static void
1147 end_of_line (WInput *in)
1149 in->point = strlen (in->buffer);
1152 static void
1153 backward_char (WInput *in)
1155 if (in->point)
1156 in->point--;
1159 static void
1160 forward_char (WInput *in)
1162 if (in->buffer [in->point])
1163 in->point++;
1166 static void
1167 forward_word (WInput *in)
1169 unsigned char *p = in->buffer+in->point;
1171 while (*p && (isspace (*p) || ispunct (*p)))
1172 p++;
1173 while (*p && isalnum (*p))
1174 p++;
1175 in->point = p - in->buffer;
1178 static void
1179 backward_word (WInput *in)
1181 unsigned char *p = in->buffer+in->point;
1183 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1184 p--;
1185 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1186 p--;
1187 in->point = p - in->buffer;
1190 static void
1191 key_left (WInput *in)
1193 if (ctrl_pressed ())
1194 backward_word (in);
1195 else
1196 backward_char (in);
1199 static void
1200 key_right (WInput *in)
1202 if (ctrl_pressed ())
1203 forward_word (in);
1204 else
1205 forward_char (in);
1208 static void
1209 backward_delete (WInput *in)
1211 int i;
1213 if (!in->point)
1214 return;
1215 for (i = in->point; in->buffer [i-1]; i++)
1216 in->buffer [i-1] = in->buffer [i];
1217 in->need_push = 1;
1218 in->point--;
1221 static void
1222 delete_char (WInput *in)
1224 int i;
1226 for (i = in->point; in->buffer [i]; i++)
1227 in->buffer [i] = in->buffer [i+1];
1228 in->need_push = 1;
1231 static void
1232 copy_region (WInput *in, int x_first, int x_last)
1234 int first = min (x_first, x_last);
1235 int last = max (x_first, x_last);
1237 if (last == first)
1238 return;
1240 if (kill_buffer)
1241 g_free (kill_buffer);
1243 kill_buffer = g_malloc (last-first + 1);
1244 strncpy (kill_buffer, in->buffer+first, last-first);
1245 kill_buffer [last-first] = 0;
1248 static void
1249 delete_region (WInput *in, int x_first, int x_last)
1251 int first = min (x_first, x_last);
1252 int last = max (x_first, x_last);
1254 in->point = first;
1255 in->mark = first;
1256 strcpy (&in->buffer [first], &in->buffer [last]);
1257 in->need_push = 1;
1260 static void
1261 kill_word (WInput *in)
1263 int old_point = in->point;
1264 int new_point;
1266 forward_word (in);
1267 new_point = in->point;
1268 in->point = old_point;
1270 copy_region (in, old_point, new_point);
1271 delete_region (in, old_point, new_point);
1272 in->need_push = 1;
1275 static void
1276 back_kill_word (WInput *in)
1278 int old_point = in->point;
1279 int new_point;
1281 backward_word (in);
1282 new_point = in->point;
1283 in->point = old_point;
1285 copy_region (in, old_point, new_point);
1286 delete_region (in, old_point, new_point);
1287 in->need_push = 1;
1290 static void
1291 set_mark (WInput *in)
1293 in->mark = in->point;
1296 static void
1297 kill_save (WInput *in)
1299 copy_region (in, in->mark, in->point);
1302 static void
1303 kill_region (WInput *in)
1305 kill_save (in);
1306 delete_region (in, in->point, in->mark);
1309 static void
1310 yank (WInput *in)
1312 char *p;
1314 if (!kill_buffer)
1315 return;
1316 for (p = kill_buffer; *p; p++)
1317 insert_char (in, *p);
1320 static void
1321 kill_line (WInput *in)
1323 if (kill_buffer)
1324 g_free (kill_buffer);
1325 kill_buffer = g_strdup (&in->buffer [in->point]);
1326 in->buffer [in->point] = 0;
1329 void
1330 assign_text (WInput *in, char *text)
1332 free_completions (in);
1333 g_free (in->buffer);
1334 in->buffer = g_strdup (text); /* was in->buffer->text */
1335 in->current_max_len = strlen (in->buffer) + 1;
1336 in->point = strlen (in->buffer);
1337 in->mark = 0;
1338 in->need_push = 1;
1341 static void
1342 hist_prev (WInput *in)
1344 if (!in->history)
1345 return;
1347 if (in->need_push) {
1348 switch (push_history (in, in->buffer)) {
1349 case 2: in->history = in->history->prev; break;
1350 case 1: if (in->history->prev) in->history = in->history->prev; break;
1351 case 0: break;
1353 } else if (in->history->prev)
1354 in->history = in->history->prev;
1355 else
1356 return;
1357 assign_text (in, in->history->text);
1358 in->need_push = 0;
1361 static void
1362 hist_next (WInput *in)
1364 if (in->need_push) {
1365 switch (push_history (in, in->buffer)) {
1366 case 2:
1367 assign_text (in, "");
1368 return;
1369 case 0:
1370 return;
1374 if (!in->history)
1375 return;
1377 if (!in->history->next) {
1378 assign_text (in, "");
1379 return;
1382 in->history = in->history->next;
1383 assign_text (in, in->history->text);
1384 in->need_push = 0;
1387 static const struct {
1388 int key_code;
1389 void (*fn)(WInput *in);
1390 } input_map [] = {
1391 /* Motion */
1392 { XCTRL('a'), beginning_of_line },
1393 { KEY_HOME, beginning_of_line },
1394 { KEY_A1, beginning_of_line },
1395 { XCTRL('e'), end_of_line },
1396 { KEY_END, end_of_line },
1397 { KEY_C1, end_of_line },
1398 { KEY_LEFT, key_left },
1399 { XCTRL('b'), backward_char },
1400 { ALT('b'), backward_word },
1401 { KEY_RIGHT, key_right },
1402 { XCTRL('f'), forward_char },
1403 { ALT('f'), forward_word },
1405 /* Editing */
1406 { 0177, backward_delete },
1407 { KEY_BACKSPACE, backward_delete },
1408 { XCTRL('h'), backward_delete },
1409 { KEY_DC, delete_char },
1410 { XCTRL('d'), delete_char },
1411 { ALT('d'), kill_word },
1412 { ALT(KEY_BACKSPACE), back_kill_word },
1413 { ALT(XCTRL('h')), back_kill_word },
1414 { ALT(127), back_kill_word },
1416 /* Region manipulation */
1417 { 0, set_mark },
1418 { XCTRL('w'), kill_region },
1419 { ALT('w'), kill_save },
1420 { XCTRL('y'), yank },
1421 { XCTRL('k'), kill_line },
1423 /* History */
1424 { ALT('p'), hist_prev },
1425 { ALT('n'), hist_next },
1426 { ALT('h'), do_show_hist },
1428 /* Completion */
1429 { ALT('\t'), complete },
1431 { 0, 0 }
1434 /* This function is a test for a special input key used in complete.c */
1435 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1436 and 2 if it is a complete key */
1438 is_in_input_map (WInput *in, int c_code)
1440 int i;
1442 for (i = 0; input_map [i].fn; i++)
1443 if (c_code == input_map [i].key_code) {
1444 if (input_map [i].fn == complete)
1445 return 2;
1446 else
1447 return 1;
1449 return 0;
1452 static void
1453 port_region_marked_for_delete (WInput *in)
1455 *in->buffer = 0;
1456 in->point = 0;
1457 in->first = 0;
1461 handle_char (WInput *in, int c_code)
1463 int i;
1464 int v;
1466 v = 0;
1468 if (quote){
1469 free_completions (in);
1470 v = insert_char (in, c_code);
1471 update_input (in, 1);
1472 quote = 0;
1473 return v;
1476 for (i = 0; input_map [i].fn; i++){
1477 if (c_code == input_map [i].key_code){
1478 if (input_map [i].fn != complete)
1479 free_completions (in);
1480 (*input_map [i].fn)(in);
1481 v = 1;
1482 break;
1485 if (!input_map [i].fn){
1486 if (c_code > 255 || !is_printable (c_code))
1487 return 0;
1488 if (in->first){
1489 port_region_marked_for_delete (in);
1491 free_completions (in);
1492 v = insert_char (in, c_code);
1494 if (!disable_update)
1495 update_input (in, 1);
1496 return v;
1499 /* Inserts text in input line */
1500 void
1501 stuff (WInput *in, char *text, int insert_extra_space)
1503 input_disable_update (in);
1504 while (*text)
1505 handle_char (in, *text++);
1506 if (insert_extra_space)
1507 handle_char (in, ' ');
1508 input_enable_update (in);
1509 update_input (in, 1);
1512 void
1513 input_set_point (WInput *in, int pos)
1515 if (pos > in->current_max_len)
1516 pos = in->current_max_len;
1517 if (pos != in->point)
1518 free_completions (in);
1519 in->point = pos;
1520 update_input (in, 1);
1524 input_callback (WInput *in, int Msg, int Par)
1526 Dlg_head *h = in->widget.parent;
1528 switch (Msg){
1529 case WIDGET_INIT:
1530 return 1;
1532 case WIDGET_KEY:
1533 if (Par == XCTRL('q')){
1534 int v;
1536 quote = 1;
1537 v = handle_char (in, mi_getch ());
1538 quote = 0;
1539 return v;
1541 if (Par == KEY_UP || Par == KEY_DOWN ||
1542 Par == ESC_CHAR || Par == KEY_F(10) ||
1543 Par == XCTRL('g'))
1544 return 0; /* We don't handle up/down */
1546 if (Par == '\n'){
1547 dlg_one_down (h);
1548 return 1;
1550 return handle_char (in, Par);
1552 case WIDGET_FOCUS:
1553 case WIDGET_UNFOCUS:
1554 case WIDGET_DRAW:
1555 update_input (in, 0);
1556 break;
1557 case WIDGET_CURSOR:
1558 widget_move (&in->widget, 0, in->point - in->first_shown);
1559 return 1;
1562 return default_proc (Msg, Par);
1565 static int
1566 input_event (Gpm_Event * event, WInput * in)
1568 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1569 dlg_select_widget (in->widget.parent, in);
1571 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1572 && should_show_history_button (in)) {
1573 do_show_hist (in);
1574 } else {
1575 in->point = strlen (in->buffer);
1576 if (event->x - in->first_shown - 1 < in->point)
1577 in->point = event->x - in->first_shown - 1;
1578 if (in->point < 0)
1579 in->point = 0;
1581 update_input (in, 1);
1583 return MOU_NORMAL;
1586 WInput *
1587 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1589 WInput *in = g_new (WInput, 1);
1590 int initial_buffer_len;
1592 init_widget (&in->widget, y, x, 1, len,
1593 (callback_fn) input_callback,
1594 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1596 /* history setup */
1597 in->history = NULL;
1598 in->history_name = 0;
1599 if (tkname) {
1600 if (*tkname) {
1601 in->history_name = g_strdup (tkname);
1602 in->history = history_get (tkname);
1606 if (!def_text)
1607 def_text = "";
1609 if (def_text == INPUT_LAST_TEXT) {
1610 def_text = "";
1611 if (in->history)
1612 if (in->history->text)
1613 def_text = in->history->text;
1615 initial_buffer_len = 1 + max (len, strlen (def_text));
1616 in->widget.options |= W_IS_INPUT;
1617 in->completions = NULL;
1618 in->completion_flags =
1619 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1620 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1621 in->current_max_len = initial_buffer_len;
1622 in->buffer = g_malloc (initial_buffer_len);
1623 in->color = color;
1624 in->field_len = len;
1625 in->first = 1;
1626 in->first_shown = 0;
1627 in->disable_update = 0;
1628 in->mark = 0;
1629 in->need_push = 1;
1630 in->is_password = 0;
1632 strcpy (in->buffer, def_text);
1633 in->point = strlen (in->buffer);
1634 return in;
1638 /* Listbox widget */
1640 /* Should draw the scrollbar, but currently draws only
1641 * indications that there is more information
1643 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1645 static void
1646 listbox_drawscroll (WListbox *l)
1648 extern int slow_terminal;
1649 int line;
1650 int i, top;
1651 int max_line = l->height-1;
1653 /* Are we at the top? */
1654 widget_move (&l->widget, 0, l->width);
1655 if (l->list == l->top)
1656 one_vline ();
1657 else
1658 addch ('^');
1660 /* Are we at the bottom? */
1661 widget_move (&l->widget, max_line, l->width);
1662 top = listbox_cdiff (l->list, l->top);
1663 if ((top + l->height == l->count) || l->height >= l->count)
1664 one_vline ();
1665 else
1666 addch ('v');
1668 /* Now draw the nice relative pointer */
1669 if (l->count)
1670 line = 1+ ((l->pos * (l->height-2)) / l->count);
1671 else
1672 line = 0;
1674 for (i = 1; i < max_line; i++){
1675 widget_move (&l->widget, i, l->width);
1676 if (i != line)
1677 one_vline ();
1678 else
1679 addch ('*');
1683 static void
1684 listbox_draw (WListbox *l, int focused)
1686 WLEntry *e;
1687 int i;
1688 int sel_line;
1689 Dlg_head *h = l->widget.parent;
1690 int normalc = NORMALC;
1691 int selc;
1692 char *text;
1694 if (focused){
1695 selc = FOCUSC;
1696 } else {
1697 selc = HOT_FOCUSC;
1699 sel_line = -1;
1701 for (e = l->top, i = 0; (i < l->height); i++){
1703 /* Display the entry */
1704 if (e == l->current && sel_line == -1){
1705 sel_line = i;
1706 attrset (selc);
1707 } else
1708 attrset (normalc);
1710 widget_move (&l->widget, i, 0);
1712 if ((i > 0 && e == l->list) || !l->list)
1713 text = "";
1714 else {
1715 text = e->text;
1716 e = e->next;
1718 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1720 l->cursor_y = sel_line;
1721 if (!l->scrollbar)
1722 return;
1723 attrset (normalc);
1724 listbox_drawscroll (l);
1727 /* Returns the number of items between s and e,
1728 must be on the same linked list */
1729 static int
1730 listbox_cdiff (WLEntry *s, WLEntry *e)
1732 int count;
1734 for (count = 0; s != e; count++)
1735 s = s->next;
1736 return count;
1739 static WLEntry *
1740 listbox_check_hotkey (WListbox *l, int key)
1742 int i;
1743 WLEntry *e;
1745 i = 0;
1746 e = l->list;
1747 if (!e)
1748 return 0;
1750 while (1){
1752 /* If we didn't find anything, return */
1753 if (i && e == l->list)
1754 return 0;
1756 if (e->hotkey == key)
1757 return e;
1759 i++;
1760 e = e->next;
1764 /* Used only for display updating, for avoiding line at a time scroll */
1765 void
1766 listbox_select_last (WListbox *l, int set_top)
1768 if (l->list){
1769 l->current = l->list->prev;
1770 l->pos = l->count - 1;
1771 if (set_top)
1772 l->top = l->list->prev;
1776 void
1777 listbox_remove_list (WListbox *l)
1779 WLEntry *p, *q;
1781 if (!l->count)
1782 return;
1784 p = l->list;
1786 while (l->count--) {
1787 q = p->next;
1788 g_free (p->text);
1789 g_free (p);
1790 p = q;
1792 l->pos = l->count = 0;
1793 l->list = l->top = l->current = 0;
1797 * bor 30.10.96: added force flag to remove *last* entry as well
1798 * bor 30.10.96: corrected selection bug if last entry was removed
1801 void
1802 listbox_remove_current (WListbox *l, int force)
1804 WLEntry *p;
1806 /* Ok, note: this won't allow for emtpy lists */
1807 if (!force && (!l->count || l->count == 1))
1808 return;
1810 l->count--;
1811 p = l->current;
1813 if (l->count) {
1814 l->current->next->prev = l->current->prev;
1815 l->current->prev->next = l->current->next;
1816 if (p->next == l->list) {
1817 l->current = p->prev;
1818 l->pos--;
1820 else
1821 l->current = p->next;
1823 if (p == l->list)
1824 l->list = l->top = p->next;
1825 } else {
1826 l->pos = 0;
1827 l->list = l->top = l->current = 0;
1830 g_free (p->text);
1831 g_free (p);
1834 /* Makes *e the selected entry (sets current and pos) */
1835 void
1836 listbox_select_entry (WListbox *l, WLEntry *dest)
1838 WLEntry *e;
1839 int pos;
1840 int top_seen;
1842 top_seen = 0;
1844 /* Special case */
1845 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1847 if (e == l->top)
1848 top_seen = 1;
1850 if (e == dest){
1851 l->current = e;
1852 if (top_seen){
1853 while (listbox_cdiff (l->top, l->current) >= l->height)
1854 l->top = l->top->next;
1855 } else {
1856 l->top = l->current;
1858 l->pos = pos;
1859 return;
1862 /* If we are unable to find it, set decent values */
1863 l->current = l->top = l->list;
1864 l->pos = 0;
1867 /* Selects from base the pos element */
1868 static WLEntry *
1869 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1871 WLEntry *last = l->list->prev;
1873 if (base == last)
1874 return last;
1875 while (pos--){
1876 base = base->next;
1877 if (base == last)
1878 break;
1880 return base;
1883 static inline int
1884 listbox_back (WListbox *l)
1886 if (l->pos){
1887 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1888 return 1;
1890 return 0;
1893 static inline int
1894 listbox_fwd (WListbox *l)
1896 if (l->current != l->list->prev){
1897 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1898 return 1;
1900 return 0;
1903 /* Returns 1 if we want a redraw */
1904 static int
1905 listbox_key (WListbox *l, int key)
1907 int i;
1908 int j = 0;
1910 if (!l->list)
1911 return 0;
1913 switch (key){
1914 case KEY_HOME:
1915 case KEY_A1:
1916 l->current = l->top = l->list;
1917 l->pos = 0;
1918 return 1;
1920 case KEY_END:
1921 case KEY_C1:
1922 l->current = l->top = l->list->prev;
1923 for (i = min (l->height - 1, l->count - 1); i; i--)
1924 l->top = l->top->prev;
1925 l->pos = l->count - 1;
1926 return 1;
1928 case XCTRL('p'):
1929 case KEY_UP:
1930 listbox_back (l);
1931 return 1;
1933 case XCTRL('n'):
1934 case KEY_DOWN:
1935 listbox_fwd (l);
1936 return 1;
1938 case KEY_NPAGE:
1939 case XCTRL('v'):
1940 for (i = 0; i < l->height-1; i++)
1941 j |= listbox_fwd (l);
1942 return j > 0;
1944 case KEY_PPAGE:
1945 case ALT('v'):
1946 for (i = 0; i < l->height-1; i++)
1947 j |= listbox_back (l);
1948 return j > 0;
1950 return 0;
1953 static int listbox_event (Gpm_Event *event, WListbox *l);
1954 static int
1955 listbox_callback (WListbox *l, int msg, int par)
1957 WLEntry *e;
1958 /* int selected_color; Never used */
1959 int ret_code;
1961 switch (msg){
1962 case WIDGET_INIT:
1963 return 1;
1965 case WIDGET_HOTKEY:
1966 if ((e = listbox_check_hotkey (l, par)) != NULL){
1967 listbox_select_entry (l, e);
1969 /* Take the appropriate action */
1970 if (l->action == listbox_finish){
1971 l->widget.parent->running = 0;
1972 l->widget.parent->ret_value = B_ENTER;
1973 } else if (l->action == listbox_cback){
1974 if ((*l->cback)(l) == listbox_finish){
1975 l->widget.parent->running = 0;
1976 l->widget.parent->ret_value = B_ENTER;
1979 return 1;
1980 } else
1981 return 0;
1983 case WIDGET_KEY:
1984 if ((ret_code = listbox_key (l, par)))
1985 listbox_draw (l, 1);
1986 return ret_code;
1988 case WIDGET_CURSOR:
1989 widget_move (&l->widget, l->cursor_y, 0);
1990 return 1;
1992 case WIDGET_FOCUS:
1993 case WIDGET_UNFOCUS:
1994 case WIDGET_DRAW:
1995 listbox_draw (l, msg != WIDGET_UNFOCUS);
1996 return 1;
1998 return default_proc (msg, par);
2001 static int
2002 listbox_event (Gpm_Event *event, WListbox *l)
2004 int i;
2006 Dlg_head *h = l->widget.parent;
2008 /* Single click */
2009 if (event->type & GPM_DOWN)
2010 dlg_select_widget (l->widget.parent, l);
2011 if (!l->list)
2012 return MOU_NORMAL;
2013 if (event->type & (GPM_DOWN|GPM_DRAG)){
2014 if (event->x < 0 || event->x >= l->width)
2015 return MOU_REPEAT;
2016 if (event->y < 1)
2017 for (i = -event->y; i >= 0; i--)
2018 listbox_back (l);
2019 else if (event->y > l->height)
2020 for (i = event->y - l->height; i > 0; i--)
2021 listbox_fwd (l);
2022 else
2023 listbox_select_entry (l, listbox_select_pos (l, l->top,
2024 event->y - 1));
2026 /* We need to refresh ourselves since the dialog manager doesn't */
2027 /* know about this event */
2028 listbox_callback (l, WIDGET_DRAW, 0);
2029 mc_refresh ();
2030 return MOU_REPEAT;
2033 /* Double click */
2034 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2035 if (event->x < 0 || event->x >= l->width)
2036 return MOU_NORMAL;
2037 if (event->y < 1 || event->y > l->height)
2038 return MOU_NORMAL;
2040 dlg_select_widget (l->widget.parent, l);
2041 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2043 switch (l->action){
2044 case listbox_nothing:
2045 break;
2047 case listbox_finish:
2048 h->ret_value = B_ENTER;
2049 dlg_stop (h);
2050 return MOU_NORMAL;
2052 case listbox_cback:
2053 if ((*l->cback)(l) == listbox_finish)
2054 return MOU_NORMAL;
2057 return MOU_NORMAL;
2060 static void
2061 listbox_destroy (WListbox *l)
2063 WLEntry *n, *p = l->list;
2064 int i;
2066 for (i = 0; i < l->count; i++){
2067 n = p->next;
2068 g_free (p->text);
2069 g_free (p);
2070 p = n;
2074 WListbox *
2075 listbox_new (int y, int x, int width, int height,
2076 int action, lcback callback, char *tkname)
2078 WListbox *l = g_new (WListbox, 1);
2079 extern int slow_terminal;
2081 init_widget (&l->widget, y, x, height, width,
2082 (callback_fn)listbox_callback,
2083 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2085 l->list = l->top = l->current = 0;
2086 l->pos = 0;
2087 l->width = width;
2088 if (height <= 0)
2089 l->height = 1;
2090 else
2091 l->height = height;
2092 l->count = 0;
2093 l->top = 0;
2094 l->current= 0;
2095 l->cback = callback;
2096 l->action = action;
2097 l->allow_duplicates = 1;
2098 l->scrollbar = slow_terminal ? 0 : 1;
2099 widget_want_hotkey (l->widget, 1);
2101 return l;
2104 /* Listbox item adding function. They still lack a lot of functionality */
2105 /* any takers? */
2106 /* 1.11.96 bor: added pos argument to control placement of new entry */
2107 static void
2108 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2110 if (!l->list){
2111 l->list = e;
2112 l->top = e;
2113 l->current = e;
2114 e->next = l->list;
2115 e->prev = l->list;
2116 } else if (pos == LISTBOX_APPEND_AT_END) {
2117 e->next = l->list;
2118 e->prev = l->list->prev;
2119 l->list->prev->next = e;
2120 l->list->prev = e;
2121 } else if (pos == LISTBOX_APPEND_BEFORE){
2122 e->next = l->current;
2123 e->prev = l->current->prev;
2124 l->current->prev->next = e;
2125 l->current->prev = e;
2126 if (l->list == l->current) { /* move list one position down */
2127 l->list = e;
2128 l->top = e;
2130 } else if (pos == LISTBOX_APPEND_AFTER) {
2131 e->prev = l->current;
2132 e->next = l->current->next;
2133 l->current->next->prev = e;
2134 l->current->next = e;
2136 l->count++;
2139 char *
2140 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2141 void *data)
2143 WLEntry *entry;
2145 if (!l)
2146 return 0;
2148 if (!l->allow_duplicates)
2149 if (listbox_search_text (l, text))
2150 return 0;
2152 entry = g_new (WLEntry, 1);
2153 entry->text = g_strdup (text);
2154 entry->data = data;
2155 entry->hotkey = hotkey;
2157 listbox_append_item (l, entry, pos);
2159 return entry->text;
2162 /* Selects the nth entry in the listbox */
2163 void
2164 listbox_select_by_number (WListbox *l, int n)
2166 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2169 WLEntry *
2170 listbox_search_text (WListbox *l, char *text)
2172 WLEntry *e;
2174 e = l->list;
2175 if (!e)
2176 return NULL;
2178 do {
2179 if(!strcmp (e->text, text))
2180 return e;
2181 e = e->next;
2182 } while (e!=l->list);
2184 return NULL;
2187 /* Returns the current string text as well as the associated extra data */
2188 void
2189 listbox_get_current (WListbox *l, char **string, char **extra)
2191 if (!l->current){
2192 *string = 0;
2193 *extra = 0;
2195 if (string && l->current)
2196 *string = l->current->text;
2197 if (extra && l->current)
2198 *extra = l->current->data;
2201 static int
2202 buttonbar_callback (WButtonBar *bb, int msg, int par)
2204 int i;
2206 switch (msg){
2207 case WIDGET_INIT:
2208 return 1;
2210 case WIDGET_FOCUS:
2211 return 0;
2213 case WIDGET_HOTKEY:
2214 for (i = 0; i < 10; i++){
2215 if (par == KEY_F(i+1) && bb->labels [i].function){
2216 (*bb->labels [i].function)(bb->labels [i].data);
2217 return 1;
2220 return 0;
2222 case WIDGET_DRAW:
2223 if (!bb->visible)
2224 return 1;
2225 widget_move (&bb->widget, 0, 0);
2226 attrset (DEFAULT_COLOR);
2227 printw ("%-*s", bb->widget.cols, "");
2228 for (i = 0; i < COLS/8 && i < 10; i++){
2229 widget_move (&bb->widget, 0, i*8);
2230 attrset (DEFAULT_COLOR);
2231 printw ("%d", i+1);
2232 attrset (SELECTED_COLOR);
2233 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2234 bb->labels [i].text ? bb->labels [i].text : "");
2235 attrset (DEFAULT_COLOR);
2237 attrset (SELECTED_COLOR);
2238 return 1;
2240 return default_proc (msg, par);
2243 static void
2244 buttonbar_destroy (WButtonBar *bb)
2246 int i;
2248 for (i = 0; i < 10; i++){
2249 if (bb->labels [i].text)
2250 g_free (bb->labels [i].text);
2254 static int
2255 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2257 int button;
2259 if (!(event->type & GPM_UP))
2260 return MOU_NORMAL;
2261 if (event->y == 2)
2262 return MOU_NORMAL;
2263 button = event->x / 8;
2264 if (button < 10 && bb->labels [button].function)
2265 (*bb->labels [button].function)(bb->labels [button].data);
2266 return MOU_NORMAL;
2269 WButtonBar *
2270 buttonbar_new (int visible)
2272 int i;
2273 WButtonBar *bb = g_new (WButtonBar, 1);
2275 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2276 (callback_fn) buttonbar_callback,
2277 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2279 bb->visible = visible;
2280 for (i = 0; i < 10; i++){
2281 bb->labels [i].text = 0;
2282 bb->labels [i].function = 0;
2284 widget_want_hotkey (bb->widget, 1);
2285 widget_want_cursor (bb->widget, 0);
2287 return bb;
2290 static void
2291 set_label_text (WButtonBar * bb, int index, char *text)
2293 if (bb->labels[index - 1].text)
2294 g_free (bb->labels[index - 1].text);
2296 bb->labels[index - 1].text = g_strdup (text);
2299 /* Find ButtonBar widget in the dialog */
2300 WButtonBar *
2301 find_buttonbar (Dlg_head *h)
2303 WButtonBar *bb;
2305 bb = (WButtonBar *) find_widget_type (h, (callback_fn)
2306 buttonbar_callback);
2307 return bb;
2310 void
2311 define_label_data (Dlg_head *h, int idx, char *text, buttonbarfn cback,
2312 void *data)
2314 WButtonBar *bb = find_buttonbar (h);
2316 if (!bb)
2317 return;
2319 set_label_text (bb, idx, text);
2320 bb->labels[idx - 1].function = cback;
2321 bb->labels[idx - 1].data = data;
2324 void
2325 define_label (Dlg_head *h, int idx, char *text, void (*cback) (void))
2327 define_label_data (h, idx, text, (buttonbarfn) cback, 0);
2330 /* Redraw labels of the buttonbar */
2331 void
2332 redraw_labels (Dlg_head *h)
2334 WButtonBar *bb = find_buttonbar (h);
2336 if (!bb)
2337 return;
2339 send_message ((Widget *) bb, WIDGET_DRAW, 0);