Last fuzzy trimmed...
[midnight-commander.git] / src / widget.c
blobda6476587fcd770b69727bfc927851a197186268
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 <fcntl.h>
34 #include <ctype.h>
35 #include "global.h"
36 #include "tty.h"
37 #include "color.h"
38 #include "mouse.h"
39 #include "dlg.h"
40 #include "widget.h"
41 #include "win.h"
42 #include "complete.h"
43 #include "key.h" /* XCTRL and ALT macros */
44 #include "profile.h" /* for history loading and saving */
46 static int button_event (Gpm_Event *event, WButton *b);
48 int quote = 0;
50 static int
51 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
53 char buf[BUF_SMALL];
54 int stop = 0;
55 int off = 0;
57 switch (Msg){
58 case WIDGET_INIT:
59 return 1;
61 case WIDGET_HOTKEY:
62 if (b->hotkey == Par || toupper(b->hotkey) == Par){
63 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
64 return 1;
65 } else
66 return 0;
68 case WIDGET_KEY:
69 if (Par != ' ' && Par != '\n')
70 break;
72 if (b->callback)
73 stop = (*b->callback)(b->action, b->callback_data);
74 if (!b->callback || stop){
75 h->ret_value = b->action;
76 dlg_stop (h);
78 return 1;
80 case WIDGET_CURSOR:
81 switch (b->flags) {
82 case DEFPUSH_BUTTON:
83 off = 3;
84 break;
85 case NORMAL_BUTTON:
86 off = 2;
87 break;
88 case NARROW_BUTTON:
89 off = 1;
90 break;
91 case HIDDEN_BUTTON:
92 default:
93 off = 0;
94 break;
96 widget_move (&b->widget, 0, b->hotpos + off);
97 return 1;
99 case WIDGET_UNFOCUS:
100 case WIDGET_FOCUS:
101 case WIDGET_DRAW:
102 if (Msg==WIDGET_UNFOCUS)
103 b->selected = 0;
104 else if (Msg==WIDGET_FOCUS)
105 b->selected = 1;
107 switch (b->flags){
108 case DEFPUSH_BUTTON:
109 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
110 off = 3;
111 break;
112 case NORMAL_BUTTON:
113 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
114 off = 2;
115 break;
116 case NARROW_BUTTON:
117 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
118 off = 1;
119 break;
120 case HIDDEN_BUTTON:
121 default:
122 buf[0] = '\0';
123 off = 0;
124 break;
127 attrset ((b->selected) ? FOCUSC : NORMALC);
128 widget_move (&b->widget, 0, 0);
130 addstr (buf);
132 if (b->hotpos >= 0){
133 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
134 widget_move (&b->widget, 0, b->hotpos+off);
135 addch ((unsigned char)b->text [b->hotpos]);
137 if (Msg == WIDGET_FOCUS)
138 break;
139 else
140 return 1;
141 break;
143 return default_proc (h, Msg, Par);
146 static int
147 button_event (Gpm_Event *event, WButton *b)
149 if (event->type & (GPM_DOWN|GPM_UP)){
150 Dlg_head *h=b->widget.parent;
151 dlg_select_widget (h, b);
152 if (event->type & GPM_UP){
153 button_callback (h, b, WIDGET_KEY, ' ');
154 (*h->callback) (h, ' ', DLG_POST_KEY);
155 return MOU_NORMAL;
158 return MOU_NORMAL;
161 static void
162 button_destroy (WButton *b)
164 g_free (b->text);
167 static int
168 button_len (const char *text, unsigned int flags)
170 int ret = strlen (text);
171 switch (flags){
172 case DEFPUSH_BUTTON:
173 ret += 6;
174 break;
175 case NORMAL_BUTTON:
176 ret += 4;
177 break;
178 case NARROW_BUTTON:
179 ret += 2;
180 break;
181 case HIDDEN_BUTTON:
182 default:
183 return 0;
185 return ret;
189 * Assuming that button text is malloc'ed, we may safely change it
190 * (as opposed to statically allocated); from other hand, excluding &
191 * and shifting data past it to the left results to one unused byte.
192 * This does not harm though :)
194 static void
195 button_scan_hotkey(WButton* b)
197 char* cp = strchr (b->text, '&');
199 if (cp != NULL && cp[1] != '\0'){
200 strcpy (cp, cp+1);
201 b->hotkey = tolower (*cp);
202 b->hotpos = cp - b->text;
206 WButton *
207 button_new (int y, int x, int action, int flags, char *text,
208 int (*callback)(int, void *), void *callback_data, char *tkname)
210 WButton *b = g_new (WButton, 1);
212 init_widget (&b->widget, y, x, 1, button_len (text, flags),
213 (callback_fn) button_callback,
214 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
216 b->action = action;
217 b->flags = flags;
218 b->selected = 0;
219 b->text = g_strdup (text);
220 b->callback = callback;
221 b->callback_data = callback_data;
222 widget_want_hotkey (b->widget, 1);
223 b->hotkey = 0;
224 b->hotpos = -1;
226 button_scan_hotkey(b);
227 return b;
230 void
231 button_set_text (WButton *b, char *text)
233 g_free (b->text);
234 b->text = g_strdup (text);
235 b->widget.cols = button_len (text, b->flags);
236 button_scan_hotkey(b);
237 dlg_redraw (b->widget.parent);
241 /* Radio button widget */
242 static int radio_event (Gpm_Event *event, WRadio *r);
244 static int
245 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
247 int i;
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 (h, 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 (h, 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 (h, 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 (h, 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 (h, r, WIDGET_KEY, ' ');
342 radio_callback (h, 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 (Dlg_head *h, WCheck *c, int Msg, int Par)
385 switch (Msg) {
386 case WIDGET_INIT:
387 return 1;
389 case WIDGET_HOTKEY:
390 if (c->hotkey==Par ||
391 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
392 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
393 return 1;
394 } else
395 return 0;
397 case WIDGET_KEY:
398 if (Par != ' ')
399 break;
400 c->state ^= C_BOOL;
401 c->state ^= C_CHANGE;
402 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
403 check_callback (h, c, WIDGET_FOCUS, ' ');
404 return 1;
406 case WIDGET_CURSOR:
407 widget_move (&c->widget, 0, 1);
408 break;
410 case WIDGET_FOCUS:
411 case WIDGET_UNFOCUS:
412 case WIDGET_DRAW:
413 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
414 widget_move (&c->widget, 0, 0);
415 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
417 if (c->hotpos >= 0){
418 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
419 widget_move (&c->widget, 0, + c->hotpos+4);
420 addch ((unsigned char)c->text [c->hotpos]);
422 return 1;
424 return default_proc (h, Msg, Par);
427 static int
428 check_event (Gpm_Event *event, WCheck *c)
430 if (event->type & (GPM_DOWN|GPM_UP)){
431 Dlg_head *h = c->widget.parent;
433 dlg_select_widget (h, c);
434 if (event->type & GPM_UP){
435 check_callback (h, c, WIDGET_KEY, ' ');
436 check_callback (h, c, WIDGET_FOCUS, 0);
437 (*h->callback) (h, ' ', DLG_POST_KEY);
438 return MOU_NORMAL;
441 return MOU_NORMAL;
444 static void
445 check_destroy (WCheck *c)
447 g_free (c->text);
450 WCheck *
451 check_new (int y, int x, int state, char *text, char *tkname)
453 WCheck *c = g_new (WCheck, 1);
454 char *s, *t;
456 init_widget (&c->widget, y, x, 1, strlen (text),
457 (callback_fn)check_callback,
458 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
459 c->state = state ? C_BOOL : 0;
460 c->text = g_strdup (text);
461 c->hotkey = 0;
462 c->hotpos = -1;
463 widget_want_hotkey (c->widget, 1);
465 /* Scan for the hotkey */
466 for (s = text, t = c->text; *s; s++, t++){
467 if (*s != '&'){
468 *t = *s;
469 continue;
471 s++;
472 if (*s){
473 c->hotkey = tolower (*s);
474 c->hotpos = t - c->text;
476 *t = *s;
478 *t = 0;
479 return c;
483 /* Label widget */
485 static int
486 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
488 if (Msg == WIDGET_INIT)
489 return 1;
491 /* We don't want to get the focus */
492 if (Msg == WIDGET_FOCUS)
493 return 0;
494 if (Msg == WIDGET_DRAW && l->text){
495 char *p = l->text, *q, c = 0;
496 int y = 0;
497 if (l->transparent)
498 attrset (DEFAULT_COLOR);
499 else
500 attrset (NORMALC);
501 for (;;){
502 int xlen;
504 q = strchr (p, '\n');
505 if (q){
506 c = *q;
507 *q = 0;
509 widget_move (&l->widget, y, 0);
510 printw ("%s", p);
511 xlen = l->widget.cols - strlen (p);
512 if (xlen > 0)
513 printw ("%*s", xlen, " ");
514 if (!q)
515 break;
516 *q = c;
517 p = q + 1;
518 y++;
520 return 1;
522 return default_proc (h, Msg, Par);
525 void
526 label_set_text (WLabel *label, char *text)
528 int newcols = label->widget.cols;
530 if (label->text && text && !strcmp (label->text, text))
531 return; /* Flickering is not nice */
533 if (label->text){
534 g_free (label->text);
536 if (text){
537 label->text = g_strdup (text);
538 if (label->auto_adjust_cols) {
539 newcols = strlen (text);
540 if (newcols > label->widget.cols)
541 label->widget.cols = newcols;
543 } else
544 label->text = 0;
546 if (label->widget.parent)
547 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
549 if (newcols < label->widget.cols)
550 label->widget.cols = newcols;
553 static void
554 label_destroy (WLabel *l)
556 if (l->text)
557 g_free (l->text);
560 WLabel *
561 label_new (int y, int x, const char *text, char *tkname)
563 WLabel *l;
564 int width;
566 /* Multiline labels are immutable - no need to compute their sizes */
567 if (!text || strchr(text, '\n'))
568 width = 1;
569 else
570 width = strlen (text);
572 l = g_new (WLabel, 1);
573 init_widget (&l->widget, y, x, 1, width,
574 (callback_fn) label_callback,
575 (destroy_fn) label_destroy, NULL, tkname);
576 l->text = text ? g_strdup (text) : 0;
577 l->auto_adjust_cols = 1;
578 l->transparent = 0;
579 widget_want_cursor (l->widget, 0);
580 return l;
584 /* Gauge widget (progress indicator) */
585 /* Currently width is hardcoded here for text mode */
586 #define gauge_len 47
588 static int
589 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
592 if (Msg == WIDGET_INIT)
593 return 1;
595 /* We don't want to get the focus */
596 if (Msg == WIDGET_FOCUS)
597 return 0;
599 if (Msg == WIDGET_DRAW){
600 widget_move (&g->widget, 0, 0);
601 attrset (NORMALC);
602 if (!g->shown)
603 printw ("%*s", gauge_len, "");
604 else {
605 long percentage, columns;
606 long total = g->max, done = g->current;
608 if (total <= 0 || done < 0) {
609 done = 0;
610 total = 100;
612 if (done > total)
613 done = total;
614 while (total > 65535) {
615 total /= 256;
616 done /= 256;
618 percentage = (200 * done / total + 1) / 2;
619 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
620 addch ('[');
621 attrset (GAUGE_COLOR);
622 printw ("%*s", columns, "");
623 attrset (NORMALC);
624 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
626 return 1;
628 return default_proc (h, Msg, Par);
631 void
632 gauge_set_value (WGauge *g, int max, int current)
634 if (g->current == current && g->max == max)
635 return; /* Do not flicker */
636 if (max == 0)
637 max = 1; /* I do not like division by zero :) */
639 g->current = current;
640 g->max = max;
641 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
644 void
645 gauge_show (WGauge *g, int shown)
647 if (g->shown == shown)
648 return;
649 g->shown = shown;
650 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
653 static void
654 gauge_destroy (WGauge *g)
656 /* nothing */
659 WGauge *
660 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
662 WGauge *g = g_new (WGauge, 1);
664 init_widget (&g->widget, y, x, 1, gauge_len,
665 (callback_fn) gauge_callback,
666 (destroy_fn) gauge_destroy, NULL, tkname);
667 g->shown = shown;
668 if (max == 0)
669 max = 1; /* I do not like division by zero :) */
670 g->max = max;
671 g->current = current;
672 g->pixels = 0;
673 widget_want_cursor (g->widget, 0);
674 return g;
678 /* Input widget */
680 /* {{{ history button */
682 #define LARGE_HISTORY_BUTTON 1
684 #ifdef LARGE_HISTORY_BUTTON
685 # define HISTORY_BUTTON_WIDTH 3
686 #else
687 # define HISTORY_BUTTON_WIDTH 1
688 #endif
690 #define should_show_history_button(in) \
691 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
693 static void draw_history_button (WInput * in)
695 char c;
696 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
697 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
698 #ifdef LARGE_HISTORY_BUTTON
700 Dlg_head *h;
701 h = in->widget.parent;
702 #if 0
703 attrset (NORMALC); /* button has the same colour as other buttons */
704 addstr ("[ ]");
705 attrset (HOT_NORMALC);
706 #else
707 attrset (NORMAL_COLOR);
708 addstr ("[ ]");
709 /* Too distracting: attrset (MARKED_COLOR); */
710 #endif
711 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
712 addch (c);
714 #else
715 attrset (MARKED_COLOR);
716 addch (c);
717 #endif
720 /* }}} history button */
723 /* Input widgets now have a global kill ring */
724 /* Pointer to killed data */
725 static char *kill_buffer = 0;
727 void
728 update_input (WInput *in, int clear_first)
730 int has_history = 0;
731 int i, j;
732 unsigned char c;
733 int buf_len = strlen (in->buffer);
735 if (should_show_history_button (in))
736 has_history = HISTORY_BUTTON_WIDTH;
738 if (in->disable_update)
739 return;
741 /* Make the point visible */
742 if ((in->point < in->first_shown) ||
743 (in->point >= in->first_shown+in->field_len - has_history)){
744 in->first_shown = in->point - (in->field_len / 3);
745 if (in->first_shown < 0)
746 in->first_shown = 0;
749 /* Adjust the mark */
750 if (in->mark > buf_len)
751 in->mark = buf_len;
753 if (has_history)
754 draw_history_button (in);
756 attrset (in->color);
758 widget_move (&in->widget, 0, 0);
759 for (i = 0; i < in->field_len - has_history; i++)
760 addch (' ');
761 widget_move (&in->widget, 0, 0);
763 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
764 c = in->buffer [j++];
765 c = is_printable (c) ? c : '.';
766 if (in->is_password)
767 c = '*';
768 addch (c);
770 widget_move (&in->widget, 0, in->point - in->first_shown);
772 if (clear_first)
773 in->first = 0;
776 void
777 winput_set_origin (WInput *in, int x, int field_len)
779 in->widget.x = x;
780 in->field_len = in->widget.cols = field_len;
781 update_input (in, 0);
784 /* {{{ history saving and loading */
787 This loads and saves the history of an input line to and from the
788 widget. It is called with the widgets tk name on creation of the
789 widget, and returns the Hist list. It stores histories in the file
790 ~/.mc/history in using the profile code.
792 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
793 function) then input_new assigns the default text to be the last text
794 entered, or "" if not found.
797 int num_history_items_recorded = 60;
799 Hist *history_get (char *input_name)
801 int i;
802 Hist *old, *new;
803 char *profile;
805 old = new = NULL;
807 if (!num_history_items_recorded) /* this is how to disable */
808 return 0;
809 if (!input_name)
810 return 0;
811 if (!*input_name)
812 return 0;
813 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
814 for (i = 0;; i++) {
815 char key_name[BUF_TINY];
816 char this_entry[BUF_LARGE];
817 g_snprintf (key_name, sizeof (key_name), "%d", i);
818 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
819 if (!*this_entry)
820 break;
821 new = g_new0 (Hist, 1);
822 new->text = g_strdup (this_entry);
823 new->prev = old; /* set up list pointers */
824 if (old)
825 old->next = new;
826 old = new;
828 g_free (profile);
829 return new; /* return pointer to last entry in list */
832 void history_put (char *input_name, Hist *h)
834 int i;
835 char *profile;
837 if (!input_name)
838 return;
840 if (!*input_name)
841 return;
843 if (!h)
844 return;
846 if (!num_history_items_recorded) /* this is how to disable */
847 return;
849 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
851 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
852 close (i);
853 /* Just in case I forgot to strip passwords somewhere -- Norbert */
854 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
855 g_free (profile);
856 return;
859 while (h->next) /* go to end of list */
860 h = h->next;
862 /* go back 60 places */
863 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
864 h = h->prev;
866 if (input_name)
867 profile_clean_section (input_name, profile);
869 /* dump histories into profile */
870 for (i = 0; h; h = h->next){
871 if (h->text){
873 /* probably aren't any null entries, but lets be sure */
874 if (*(h->text)){
875 char key_name[BUF_TINY];
876 g_snprintf (key_name, sizeof(key_name), "%d", i++);
877 WritePrivateProfileString (input_name, key_name, h->text, profile);
881 g_free (profile);
884 /* }}} history saving and loading */
887 /* {{{ history display */
889 static char *
890 i18n_htitle (void)
892 static char *history_title = NULL;
894 if (history_title == NULL)
895 history_title = _(" History ");
896 return history_title;
899 static int
900 history_callback (Dlg_head * h, int Par, int Msg)
902 switch (Msg) {
903 case DLG_DRAW:
904 attrset (COLOR_NORMAL);
905 dlg_erase (h);
906 draw_box (h, 0, 0, h->lines, h->cols);
907 attrset (COLOR_HOT_NORMAL);
908 dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
909 printw (i18n_htitle());
910 break;
912 return 0;
915 static inline int listbox_fwd (WListbox *l);
917 char *show_hist (Hist *history, int widget_x, int widget_y)
919 Hist *hi, *z;
920 size_t maxlen = strlen (i18n_htitle()), i, count = 0;
921 int x, y, w, h;
922 char *q, *r = 0;
923 Dlg_head *query_dlg;
924 WListbox *query_list;
926 z = history;
927 if (!z)
928 return 0;
930 while (z->prev) /* goto first */
931 z = z->prev;
932 hi = z;
933 while (hi) {
934 if ((i = strlen (hi->text)) > maxlen)
935 maxlen = i;
936 count++;
937 hi = hi->next;
940 y = widget_y;
941 h = count + 2;
942 if (h <= y || y > LINES - 6)
944 h = min(h, y - 1);
945 y -= h;
947 else
949 y++;
950 h = min(h, LINES - y);
953 if (widget_x > 2)
954 x = widget_x - 2;
955 else
956 x = 0;
957 if ((w = maxlen + 4) + x > COLS)
959 w = min(w,COLS);
960 x = COLS - w;
963 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
964 "[History-query]", "history", DLG_NONE);
965 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
966 add_widget (query_dlg, query_list);
967 hi = z;
968 if (y < widget_y) {
969 while (hi) { /* traverse */
970 listbox_add_item (query_list, 0, 0, hi->text, NULL);
971 hi = hi->next;
973 while (listbox_fwd (query_list));
974 } else {
975 while (hi->next)
976 hi = hi->next;
977 while (hi) { /* traverse backwards */
978 listbox_add_item (query_list, 0, 0, hi->text, NULL);
979 hi = hi->prev;
982 run_dlg (query_dlg);
983 q = NULL;
984 if (query_dlg->ret_value != B_CANCEL) {
985 listbox_get_current (query_list, &q, NULL);
986 if (q)
987 r = g_strdup (q);
989 destroy_dlg (query_dlg);
990 return r;
993 static void do_show_hist (WInput * in)
995 char *r;
996 r = show_hist (in->history, in->widget.x, in->widget.y);
997 if (r) {
998 assign_text (in, r);
999 g_free (r);
1003 /* }}} history display */
1005 static void
1006 input_destroy (WInput *in)
1008 if (!in){
1009 fprintf (stderr, "Internal error: null Input *\n");
1010 exit (1);
1013 new_input (in);
1014 if (in->history){
1015 Hist *current, *old;
1017 if (!in->is_password) /* don't save passwords ;-) */
1018 history_put (in->history_name, in->history);
1020 current = in->history;
1021 while (current->next)
1022 current = current->next;
1023 while (current){
1024 old = current;
1025 current = current->prev;
1026 g_free (old->text);
1027 g_free (old);
1030 g_free (in->buffer);
1031 free_completions (in);
1032 if (in->history_name)
1033 g_free (in->history_name);
1036 static char disable_update = 0;
1038 void
1039 input_disable_update (WInput *in)
1041 in->disable_update++;
1044 void
1045 input_enable_update (WInput *in)
1047 in->disable_update--;
1048 update_input (in, 0);
1051 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1054 push_history (WInput *in, char *text)
1056 static int i18n;
1057 /* input widget where urls with passwords are entered without any
1058 vfs prefix */
1059 static const char *password_input_fields[] = {
1060 N_(" Link to a remote machine "),
1061 N_(" FTP to machine "),
1062 N_(" SMB link to machine ")
1064 Hist *new;
1065 char *p;
1066 int i;
1068 if (!i18n) {
1069 i18n = 1;
1070 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1071 password_input_fields[i] = _(password_input_fields[i]);
1074 for (p = text; *p == ' ' || *p == '\t'; p++);
1075 if (!*p)
1076 return 0;
1077 if (in->history){
1078 while (in->history->next)
1079 in->history = in->history->next;
1080 if (!strcmp (in->history->text, text))
1081 return 1;
1082 new = g_new (Hist, 1);
1083 in->history->next = new;
1084 } else
1085 new = g_new (Hist, 1);
1086 in->need_push = 0;
1087 new->next = 0;
1088 new->prev = in->history;
1089 new->text = g_strdup (text);
1090 if (in->history_name) {
1091 p = in->history_name + 3;
1092 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1093 if (strcmp (p, password_input_fields[i]) == 0)
1094 break;
1095 if (i < ELEMENTS(password_input_fields))
1096 strip_password (new->text, 0);
1097 else
1098 strip_password (new->text, 1);
1101 in->history = new;
1102 return 2;
1105 #undef ELEMENTS
1107 /* Cleans the input line and adds the current text to the history */
1108 void
1109 new_input (WInput *in)
1111 if (in->buffer)
1112 push_history (in, in->buffer);
1113 in->need_push = 1;
1114 in->buffer [0] = 0;
1115 in->point = 0;
1116 in->mark = 0;
1117 free_completions (in);
1118 update_input (in, 0);
1121 static int
1122 insert_char (WInput *in, int c_code)
1124 int i;
1126 if (c_code == -1)
1127 return 0;
1129 in->need_push = 1;
1130 if (strlen (in->buffer)+1 == in->current_max_len){
1131 /* Expand the buffer */
1132 char *narea = g_malloc (in->current_max_len + in->field_len);
1133 if (narea){
1134 char *p = in->buffer;
1136 strcpy (narea, in->buffer);
1137 in->buffer = narea;
1138 in->current_max_len += in->field_len;
1139 g_free (p);
1142 if (strlen (in->buffer)+1 < in->current_max_len){
1143 int l = strlen (&in->buffer [in->point]);
1144 for (i = l+1; i > 0; i--)
1145 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1146 in->buffer [in->point] = c_code;
1147 in->point++;
1149 return 1;
1152 static void
1153 beginning_of_line (WInput *in)
1155 in->point = 0;
1158 static void
1159 end_of_line (WInput *in)
1161 in->point = strlen (in->buffer);
1164 static void
1165 backward_char (WInput *in)
1167 if (in->point)
1168 in->point--;
1171 static void
1172 forward_char (WInput *in)
1174 if (in->buffer [in->point])
1175 in->point++;
1178 static void
1179 forward_word (WInput *in)
1181 unsigned char *p = in->buffer+in->point;
1183 while (*p && (isspace (*p) || ispunct (*p)))
1184 p++;
1185 while (*p && isalnum (*p))
1186 p++;
1187 in->point = p - in->buffer;
1190 static void
1191 backward_word (WInput *in)
1193 unsigned char *p = in->buffer+in->point;
1195 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1196 p--;
1197 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1198 p--;
1199 in->point = p - in->buffer;
1202 static void
1203 key_left (WInput *in)
1205 if (ctrl_pressed ())
1206 backward_word (in);
1207 else
1208 backward_char (in);
1211 static void
1212 key_right (WInput *in)
1214 if (ctrl_pressed ())
1215 forward_word (in);
1216 else
1217 forward_char (in);
1220 static void
1221 backward_delete (WInput *in)
1223 int i;
1225 if (!in->point)
1226 return;
1227 for (i = in->point; in->buffer [i-1]; i++)
1228 in->buffer [i-1] = in->buffer [i];
1229 in->need_push = 1;
1230 in->point--;
1233 static void
1234 delete_char (WInput *in)
1236 int i;
1238 for (i = in->point; in->buffer [i]; i++)
1239 in->buffer [i] = in->buffer [i+1];
1240 in->need_push = 1;
1243 static void
1244 copy_region (WInput *in, int x_first, int x_last)
1246 int first = min (x_first, x_last);
1247 int last = max (x_first, x_last);
1249 if (last == first)
1250 return;
1252 if (kill_buffer)
1253 g_free (kill_buffer);
1255 kill_buffer = g_malloc (last-first + 1);
1256 strncpy (kill_buffer, in->buffer+first, last-first);
1257 kill_buffer [last-first] = 0;
1260 static void
1261 delete_region (WInput *in, int x_first, int x_last)
1263 int first = min (x_first, x_last);
1264 int last = max (x_first, x_last);
1266 in->point = first;
1267 in->mark = first;
1268 strcpy (&in->buffer [first], &in->buffer [last]);
1269 in->need_push = 1;
1272 static void
1273 kill_word (WInput *in)
1275 int old_point = in->point;
1276 int new_point;
1278 forward_word (in);
1279 new_point = in->point;
1280 in->point = old_point;
1282 copy_region (in, old_point, new_point);
1283 delete_region (in, old_point, new_point);
1284 in->need_push = 1;
1287 static void
1288 back_kill_word (WInput *in)
1290 int old_point = in->point;
1291 int new_point;
1293 backward_word (in);
1294 new_point = in->point;
1295 in->point = old_point;
1297 copy_region (in, old_point, new_point);
1298 delete_region (in, old_point, new_point);
1299 in->need_push = 1;
1302 static void
1303 set_mark (WInput *in)
1305 in->mark = in->point;
1308 static void
1309 kill_save (WInput *in)
1311 copy_region (in, in->mark, in->point);
1314 static void
1315 kill_region (WInput *in)
1317 kill_save (in);
1318 delete_region (in, in->point, in->mark);
1321 static void
1322 yank (WInput *in)
1324 char *p;
1326 if (!kill_buffer)
1327 return;
1328 for (p = kill_buffer; *p; p++)
1329 insert_char (in, *p);
1332 static void
1333 kill_line (WInput *in)
1335 if (kill_buffer)
1336 g_free (kill_buffer);
1337 kill_buffer = g_strdup (&in->buffer [in->point]);
1338 in->buffer [in->point] = 0;
1341 void
1342 assign_text (WInput *in, char *text)
1344 free_completions (in);
1345 g_free (in->buffer);
1346 in->buffer = g_strdup (text); /* was in->buffer->text */
1347 in->current_max_len = strlen (in->buffer) + 1;
1348 in->point = strlen (in->buffer);
1349 in->mark = 0;
1350 in->need_push = 1;
1353 static void
1354 hist_prev (WInput *in)
1356 if (!in->history)
1357 return;
1359 if (in->need_push) {
1360 switch (push_history (in, in->buffer)) {
1361 case 2: in->history = in->history->prev; break;
1362 case 1: if (in->history->prev) in->history = in->history->prev; break;
1363 case 0: break;
1365 } else if (in->history->prev)
1366 in->history = in->history->prev;
1367 else
1368 return;
1369 assign_text (in, in->history->text);
1370 in->need_push = 0;
1373 static void
1374 hist_next (WInput *in)
1376 if (in->need_push) {
1377 switch (push_history (in, in->buffer)) {
1378 case 2:
1379 assign_text (in, "");
1380 return;
1381 case 0:
1382 return;
1386 if (!in->history)
1387 return;
1389 if (!in->history->next) {
1390 assign_text (in, "");
1391 return;
1394 in->history = in->history->next;
1395 assign_text (in, in->history->text);
1396 in->need_push = 0;
1399 static const struct {
1400 int key_code;
1401 void (*fn)(WInput *in);
1402 } input_map [] = {
1403 /* Motion */
1404 { XCTRL('a'), beginning_of_line },
1405 { KEY_HOME, beginning_of_line },
1406 { KEY_A1, beginning_of_line },
1407 { XCTRL('e'), end_of_line },
1408 { KEY_END, end_of_line },
1409 { KEY_C1, end_of_line },
1410 { KEY_LEFT, key_left },
1411 { XCTRL('b'), backward_char },
1412 { ALT('b'), backward_word },
1413 { KEY_RIGHT, key_right },
1414 { XCTRL('f'), forward_char },
1415 { ALT('f'), forward_word },
1417 /* Editing */
1418 { 0177, backward_delete },
1419 { KEY_BACKSPACE, backward_delete },
1420 { XCTRL('h'), backward_delete },
1421 { KEY_DC, delete_char },
1422 { XCTRL('d'), delete_char },
1423 { ALT('d'), kill_word },
1424 { ALT(KEY_BACKSPACE), back_kill_word },
1425 { ALT(XCTRL('h')), back_kill_word },
1426 { ALT(127), back_kill_word },
1428 /* Region manipulation */
1429 { 0, set_mark },
1430 { XCTRL('w'), kill_region },
1431 { ALT('w'), kill_save },
1432 { XCTRL('y'), yank },
1433 { XCTRL('k'), kill_line },
1435 /* History */
1436 { ALT('p'), hist_prev },
1437 { ALT('n'), hist_next },
1438 { ALT('h'), do_show_hist },
1440 /* Completion */
1441 { ALT('\t'), complete },
1443 { 0, 0 }
1446 /* This function is a test for a special input key used in complete.c */
1447 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1448 and 2 if it is a complete key */
1450 is_in_input_map (WInput *in, int c_code)
1452 int i;
1454 for (i = 0; input_map [i].fn; i++)
1455 if (c_code == input_map [i].key_code) {
1456 if (input_map [i].fn == complete)
1457 return 2;
1458 else
1459 return 1;
1461 return 0;
1464 static void
1465 port_region_marked_for_delete (WInput *in)
1467 *in->buffer = 0;
1468 in->point = 0;
1469 in->first = 0;
1473 handle_char (WInput *in, int c_code)
1475 int i;
1476 int v;
1478 v = 0;
1480 if (quote){
1481 free_completions (in);
1482 v = insert_char (in, c_code);
1483 update_input (in, 1);
1484 quote = 0;
1485 return v;
1488 for (i = 0; input_map [i].fn; i++){
1489 if (c_code == input_map [i].key_code){
1490 if (input_map [i].fn != complete)
1491 free_completions (in);
1492 (*input_map [i].fn)(in);
1493 v = 1;
1494 break;
1497 if (!input_map [i].fn){
1498 if (c_code > 255 || !is_printable (c_code))
1499 return 0;
1500 if (in->first){
1501 port_region_marked_for_delete (in);
1503 free_completions (in);
1504 v = insert_char (in, c_code);
1505 in->inserted_one = c_code;
1507 if (!disable_update)
1508 update_input (in, 1);
1509 return v;
1512 /* Inserts text in input line */
1513 void
1514 stuff (WInput *in, char *text, int insert_extra_space)
1516 input_disable_update (in);
1517 while (*text)
1518 handle_char (in, *text++);
1519 if (insert_extra_space)
1520 handle_char (in, ' ');
1521 input_enable_update (in);
1522 update_input (in, 1);
1525 void
1526 input_set_point (WInput *in, int pos)
1528 if (pos > in->current_max_len)
1529 pos = in->current_max_len;
1530 if (pos != in->point)
1531 free_completions (in);
1532 in->point = pos;
1533 update_input (in, 1);
1536 int input_event (Gpm_Event *event, WInput *b);
1538 static int
1539 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1541 switch (Msg){
1542 case WIDGET_INIT:
1543 return 1;
1545 case WIDGET_KEY:
1546 if (Par == XCTRL('q')){
1547 int v;
1549 quote = 1;
1550 v = handle_char (in, mi_getch ());
1551 quote = 0;
1552 return v;
1554 if (Par == KEY_UP || Par == KEY_DOWN ||
1555 Par == ESC_CHAR || Par == KEY_F(10) ||
1556 Par == XCTRL('g'))
1557 return 0; /* We don't handle up/down */
1559 if (Par == '\n'){
1560 dlg_one_down (h);
1561 return 1;
1563 return handle_char (in, Par);
1565 case WIDGET_FOCUS:
1566 case WIDGET_UNFOCUS:
1567 case WIDGET_DRAW:
1568 update_input (in, 0);
1569 break;
1570 case WIDGET_CURSOR:
1571 widget_move (&in->widget, 0, in->point - in->first_shown);
1572 return 1;
1575 return default_proc (h, Msg, Par);
1578 /* Not declared static, since we check against this value in dlg.c */
1579 /* FIXME: Declare static again and provide an identification mechanism */
1580 int
1581 input_event (Gpm_Event *event, WInput *in)
1583 if (event->type & (GPM_DOWN|GPM_DRAG)){
1584 dlg_select_widget (in->widget.parent, in);
1586 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
1587 do_show_hist (in);
1588 } else {
1589 in->point = strlen (in->buffer);
1590 if (event->x - in->first_shown - 1 < in->point)
1591 in->point = event->x - in->first_shown - 1;
1592 if (in->point < 0)
1593 in->point = 0;
1595 update_input (in, 1);
1597 return MOU_NORMAL;
1600 WInput *
1601 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1603 WInput *in = g_new (WInput, 1);
1604 int initial_buffer_len;
1606 init_widget (&in->widget, y, x, 1, len,
1607 (callback_fn) input_callback,
1608 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1610 /* history setup */
1611 in->history = NULL;
1612 in->history_name = 0;
1613 if (tkname) {
1614 if (*tkname) {
1615 in->history_name = g_strdup (tkname);
1616 in->history = history_get (tkname);
1620 if (!def_text)
1621 def_text = "";
1623 if (def_text == INPUT_LAST_TEXT) {
1624 def_text = "";
1625 if (in->history)
1626 if (in->history->text)
1627 def_text = in->history->text;
1629 initial_buffer_len = 1 + max (len, strlen (def_text));
1630 in->widget.options |= W_IS_INPUT;
1631 in->completions = NULL;
1632 in->completion_flags =
1633 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1634 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1635 in->current_max_len = initial_buffer_len;
1636 in->buffer = g_malloc (initial_buffer_len);
1637 in->color = color;
1638 in->field_len = len;
1639 in->first = 1;
1640 in->first_shown = 0;
1641 in->disable_update = 0;
1642 in->mark = 0;
1643 in->need_push = 1;
1644 in->is_password = 0;
1646 strcpy (in->buffer, def_text);
1647 in->point = strlen (in->buffer);
1648 return in;
1652 /* Listbox widget */
1654 /* Should draw the scrollbar, but currently draws only
1655 * indications that there is more information
1657 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1659 static void
1660 listbox_drawscroll (WListbox *l)
1662 extern int slow_terminal;
1663 int line;
1664 int i, top;
1665 int max_line = l->height-1;
1667 /* Are we at the top? */
1668 widget_move (&l->widget, 0, l->width);
1669 if (l->list == l->top)
1670 one_vline ();
1671 else
1672 addch ('^');
1674 /* Are we at the bottom? */
1675 widget_move (&l->widget, max_line, l->width);
1676 top = listbox_cdiff (l->list, l->top);
1677 if ((top + l->height == l->count) || l->height >= l->count)
1678 one_vline ();
1679 else
1680 addch ('v');
1682 /* Now draw the nice relative pointer */
1683 if (l->count)
1684 line = 1+ ((l->pos * (l->height-2)) / l->count);
1685 else
1686 line = 0;
1688 for (i = 1; i < max_line; i++){
1689 widget_move (&l->widget, i, l->width);
1690 if (i != line)
1691 one_vline ();
1692 else
1693 addch ('*');
1697 static void
1698 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1700 WLEntry *e;
1701 int i;
1702 int sel_line;
1703 int normalc = NORMALC;
1704 int selc;
1705 char *text;
1707 if (focused){
1708 selc = FOCUSC;
1709 } else {
1710 selc = HOT_FOCUSC;
1712 sel_line = -1;
1714 for (e = l->top, i = 0; (i < l->height); i++){
1716 /* Display the entry */
1717 if (e == l->current && sel_line == -1){
1718 sel_line = i;
1719 attrset (selc);
1720 } else
1721 attrset (normalc);
1723 widget_move (&l->widget, i, 0);
1725 if ((i > 0 && e == l->list) || !l->list)
1726 text = "";
1727 else {
1728 text = e->text;
1729 e = e->next;
1731 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1733 l->cursor_y = sel_line;
1734 if (!l->scrollbar)
1735 return;
1736 attrset (normalc);
1737 listbox_drawscroll (l);
1740 /* Returns the number of items between s and e,
1741 must be on the same linked list */
1742 static int
1743 listbox_cdiff (WLEntry *s, WLEntry *e)
1745 int count;
1747 for (count = 0; s != e; count++)
1748 s = s->next;
1749 return count;
1752 static WLEntry *
1753 listbox_check_hotkey (WListbox *l, int key)
1755 int i;
1756 WLEntry *e;
1758 i = 0;
1759 e = l->list;
1760 if (!e)
1761 return 0;
1763 while (1){
1765 /* If we didn't find anything, return */
1766 if (i && e == l->list)
1767 return 0;
1769 if (e->hotkey == key)
1770 return e;
1772 i++;
1773 e = e->next;
1777 /* Used only for display updating, for avoiding line at a time scroll */
1778 void
1779 listbox_select_last (WListbox *l, int set_top)
1781 if (l->list){
1782 l->current = l->list->prev;
1783 l->pos = l->count - 1;
1784 if (set_top)
1785 l->top = l->list->prev;
1789 void
1790 listbox_remove_list (WListbox *l)
1792 WLEntry *p, *q;
1794 if (!l->count)
1795 return;
1797 p = l->list;
1799 while (l->count--) {
1800 q = p->next;
1801 g_free (p->text);
1802 g_free (p);
1803 p = q;
1805 l->pos = l->count = 0;
1806 l->list = l->top = l->current = 0;
1810 * bor 30.10.96: added force flag to remove *last* entry as well
1811 * bor 30.10.96: corrected selection bug if last entry was removed
1814 void
1815 listbox_remove_current (WListbox *l, int force)
1817 WLEntry *p;
1819 /* Ok, note: this won't allow for emtpy lists */
1820 if (!force && (!l->count || l->count == 1))
1821 return;
1823 l->count--;
1824 p = l->current;
1826 if (l->count) {
1827 l->current->next->prev = l->current->prev;
1828 l->current->prev->next = l->current->next;
1829 if (p->next == l->list) {
1830 l->current = p->prev;
1831 l->pos--;
1833 else
1834 l->current = p->next;
1836 if (p == l->list)
1837 l->list = l->top = p->next;
1838 } else {
1839 l->pos = 0;
1840 l->list = l->top = l->current = 0;
1843 g_free (p->text);
1844 g_free (p);
1847 /* Makes *e the selected entry (sets current and pos) */
1848 void
1849 listbox_select_entry (WListbox *l, WLEntry *dest)
1851 WLEntry *e;
1852 int pos;
1853 int top_seen;
1855 top_seen = 0;
1857 /* Special case */
1858 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1860 if (e == l->top)
1861 top_seen = 1;
1863 if (e == dest){
1864 l->current = e;
1865 if (top_seen){
1866 while (listbox_cdiff (l->top, l->current) >= l->height)
1867 l->top = l->top->next;
1868 } else {
1869 l->top = l->current;
1871 l->pos = pos;
1872 return;
1875 /* If we are unable to find it, set decent values */
1876 l->current = l->top = l->list;
1877 l->pos = 0;
1880 /* Selects from base the pos element */
1881 static WLEntry *
1882 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1884 WLEntry *last = l->list->prev;
1886 if (base == last)
1887 return last;
1888 while (pos--){
1889 base = base->next;
1890 if (base == last)
1891 break;
1893 return base;
1896 static inline int
1897 listbox_back (WListbox *l)
1899 if (l->pos){
1900 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1901 return 1;
1903 return 0;
1906 static inline int
1907 listbox_fwd (WListbox *l)
1909 if (l->current != l->list->prev){
1910 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1911 return 1;
1913 return 0;
1916 /* Returns 1 if we want a redraw */
1917 static int
1918 listbox_key (WListbox *l, int key)
1920 int i;
1921 int j = 0;
1923 if (!l->list)
1924 return 0;
1926 switch (key){
1927 case KEY_HOME:
1928 case KEY_A1:
1929 l->current = l->top = l->list;
1930 l->pos = 0;
1931 return 1;
1933 case KEY_END:
1934 case KEY_C1:
1935 l->current = l->top = l->list->prev;
1936 for (i = min (l->height - 1, l->count - 1); i; i--)
1937 l->top = l->top->prev;
1938 l->pos = l->count - 1;
1939 return 1;
1941 case XCTRL('p'):
1942 case KEY_UP:
1943 listbox_back (l);
1944 return 1;
1946 case XCTRL('n'):
1947 case KEY_DOWN:
1948 listbox_fwd (l);
1949 return 1;
1951 case KEY_NPAGE:
1952 case XCTRL('v'):
1953 for (i = 0; i < l->height-1; i++)
1954 j |= listbox_fwd (l);
1955 return j > 0;
1957 case KEY_PPAGE:
1958 case ALT('v'):
1959 for (i = 0; i < l->height-1; i++)
1960 j |= listbox_back (l);
1961 return j > 0;
1963 return 0;
1966 static int listbox_event (Gpm_Event *event, WListbox *l);
1967 static int
1968 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
1970 WLEntry *e;
1971 /* int selected_color; Never used */
1972 int ret_code;
1974 switch (msg){
1975 case WIDGET_INIT:
1976 return 1;
1978 case WIDGET_HOTKEY:
1979 if ((e = listbox_check_hotkey (l, par)) != NULL){
1980 listbox_select_entry (l, e);
1982 /* Take the appropriate action */
1983 if (l->action == listbox_finish){
1984 l->widget.parent->running = 0;
1985 l->widget.parent->ret_value = B_ENTER;
1986 } else if (l->action == listbox_cback){
1987 if ((*l->cback)(l) == listbox_finish){
1988 l->widget.parent->running = 0;
1989 l->widget.parent->ret_value = B_ENTER;
1992 return 1;
1993 } else
1994 return 0;
1996 case WIDGET_KEY:
1997 if ((ret_code = listbox_key (l, par)))
1998 listbox_draw (l, h, 1);
1999 return ret_code;
2001 case WIDGET_CURSOR:
2002 widget_move (&l->widget, l->cursor_y, 0);
2003 return 1;
2005 case WIDGET_FOCUS:
2006 case WIDGET_UNFOCUS:
2007 case WIDGET_DRAW:
2008 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2009 return 1;
2011 return default_proc (h, msg, par);
2014 static int
2015 listbox_event (Gpm_Event *event, WListbox *l)
2017 int i;
2019 Dlg_head *h = l->widget.parent;
2021 /* Single click */
2022 if (event->type & GPM_DOWN)
2023 dlg_select_widget (l->widget.parent, l);
2024 if (!l->list)
2025 return MOU_NORMAL;
2026 if (event->type & (GPM_DOWN|GPM_DRAG)){
2027 if (event->x < 0 || event->x >= l->width)
2028 return MOU_REPEAT;
2029 if (event->y < 1)
2030 for (i = -event->y; i >= 0; i--)
2031 listbox_back (l);
2032 else if (event->y > l->height)
2033 for (i = event->y - l->height; i > 0; i--)
2034 listbox_fwd (l);
2035 else
2036 listbox_select_entry (l, listbox_select_pos (l, l->top,
2037 event->y - 1));
2039 /* We need to refresh ourselves since the dialog manager doesn't */
2040 /* know about this event */
2041 listbox_callback (h, l, WIDGET_DRAW, 0);
2042 mc_refresh ();
2043 return MOU_REPEAT;
2046 /* Double click */
2047 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2048 if (event->x < 0 || event->x >= l->width)
2049 return MOU_NORMAL;
2050 if (event->y < 1 || event->y > l->height)
2051 return MOU_NORMAL;
2053 dlg_select_widget (l->widget.parent, l);
2054 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2056 switch (l->action){
2057 case listbox_nothing:
2058 break;
2060 case listbox_finish:
2061 h->ret_value = B_ENTER;
2062 dlg_stop (h);
2063 return MOU_ENDLOOP;
2065 case listbox_cback:
2066 if ((*l->cback)(l) == listbox_finish)
2067 return MOU_ENDLOOP;
2070 return MOU_NORMAL;
2073 static void
2074 listbox_destroy (WListbox *l)
2076 WLEntry *n, *p = l->list;
2077 int i;
2079 for (i = 0; i < l->count; i++){
2080 n = p->next;
2081 g_free (p->text);
2082 g_free (p);
2083 p = n;
2087 WListbox *
2088 listbox_new (int y, int x, int width, int height,
2089 int action, lcback callback, char *tkname)
2091 WListbox *l = g_new (WListbox, 1);
2092 extern int slow_terminal;
2094 init_widget (&l->widget, y, x, height, width,
2095 (callback_fn)listbox_callback,
2096 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2098 l->list = l->top = l->current = 0;
2099 l->pos = 0;
2100 l->width = width;
2101 if (height <= 0)
2102 l->height = 1;
2103 else
2104 l->height = height;
2105 l->count = 0;
2106 l->top = 0;
2107 l->current= 0;
2108 l->cback = callback;
2109 l->action = action;
2110 l->allow_duplicates = 1;
2111 l->scrollbar = slow_terminal ? 0 : 1;
2112 widget_want_hotkey (l->widget, 1);
2114 return l;
2117 /* Listbox item adding function. They still lack a lot of functionality */
2118 /* any takers? */
2119 /* 1.11.96 bor: added pos argument to control placement of new entry */
2120 static void
2121 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2123 if (!l->list){
2124 l->list = e;
2125 l->top = e;
2126 l->current = e;
2127 e->next = l->list;
2128 e->prev = l->list;
2129 } else if (pos == LISTBOX_APPEND_AT_END) {
2130 e->next = l->list;
2131 e->prev = l->list->prev;
2132 l->list->prev->next = e;
2133 l->list->prev = e;
2134 } else if (pos == LISTBOX_APPEND_BEFORE){
2135 e->next = l->current;
2136 e->prev = l->current->prev;
2137 l->current->prev->next = e;
2138 l->current->prev = e;
2139 if (l->list == l->current) { /* move list one position down */
2140 l->list = e;
2141 l->top = e;
2143 } else if (pos == LISTBOX_APPEND_AFTER) {
2144 e->prev = l->current;
2145 e->next = l->current->next;
2146 l->current->next->prev = e;
2147 l->current->next = e;
2149 l->count++;
2152 char *
2153 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2154 void *data)
2156 WLEntry *entry;
2158 if (!l)
2159 return 0;
2161 if (!l->allow_duplicates)
2162 if (listbox_search_text (l, text))
2163 return 0;
2165 entry = g_new (WLEntry, 1);
2166 entry->text = g_strdup (text);
2167 entry->data = data;
2168 entry->hotkey = hotkey;
2170 listbox_append_item (l, entry, pos);
2172 return entry->text;
2175 /* Selects the nth entry in the listbox */
2176 void
2177 listbox_select_by_number (WListbox *l, int n)
2179 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2182 WLEntry *
2183 listbox_search_text (WListbox *l, char *text)
2185 WLEntry *e;
2187 e = l->list;
2188 if (!e)
2189 return NULL;
2191 do {
2192 if(!strcmp (e->text, text))
2193 return e;
2194 e = e->next;
2195 } while (e!=l->list);
2197 return NULL;
2200 /* Returns the current string text as well as the associated extra data */
2201 void
2202 listbox_get_current (WListbox *l, char **string, char **extra)
2204 if (!l->current){
2205 *string = 0;
2206 *extra = 0;
2208 if (string && l->current)
2209 *string = l->current->text;
2210 if (extra && l->current)
2211 *extra = l->current->data;
2214 static int
2215 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2217 int i;
2219 switch (msg){
2220 case WIDGET_INIT:
2221 return 1;
2223 case WIDGET_FOCUS:
2224 return 0;
2226 case WIDGET_HOTKEY:
2227 for (i = 0; i < 10; i++){
2228 if (par == KEY_F(i+1) && bb->labels [i].function){
2229 (*bb->labels [i].function)(bb->labels [i].data);
2230 return 1;
2233 return 0;
2235 case WIDGET_DRAW:
2236 if (!bb->visible)
2237 return 1;
2238 widget_move (&bb->widget, 0, 0);
2239 attrset (DEFAULT_COLOR);
2240 printw ("%-*s", bb->widget.cols - 1, "");
2241 for (i = 0; i < COLS/8 && i < 10; i++){
2242 widget_move (&bb->widget, 0, i*8);
2243 attrset (DEFAULT_COLOR);
2244 printw ("%d", i+1);
2245 attrset (SELECTED_COLOR);
2246 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2247 bb->labels [i].text ? bb->labels [i].text : "");
2248 attrset (DEFAULT_COLOR);
2250 attrset (SELECTED_COLOR);
2251 return 1;
2253 return default_proc (h, msg, par);
2256 static void
2257 buttonbar_destroy (WButtonBar *bb)
2259 int i;
2261 for (i = 0; i < 10; i++){
2262 if (bb->labels [i].text)
2263 g_free (bb->labels [i].text);
2267 static int
2268 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2270 int button;
2272 if (!(event->type & GPM_UP))
2273 return MOU_NORMAL;
2274 if (event->y == 2)
2275 return MOU_NORMAL;
2276 button = event->x / 8;
2277 if (button < 10 && bb->labels [button].function)
2278 (*bb->labels [button].function)(bb->labels [button].data);
2279 return MOU_NORMAL;
2282 WButtonBar *
2283 buttonbar_new (int visible)
2285 int i;
2286 WButtonBar *bb = g_new (WButtonBar, 1);
2288 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2289 (callback_fn) buttonbar_callback,
2290 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2292 bb->visible = visible;
2293 for (i = 0; i < 10; i++){
2294 bb->labels [i].text = 0;
2295 bb->labels [i].function = 0;
2297 widget_want_hotkey (bb->widget, 1);
2298 widget_want_cursor (bb->widget, 0);
2300 return bb;
2303 void
2304 set_label_text (WButtonBar *bb, int index, char *text)
2306 if (bb->labels [index-1].text)
2307 g_free (bb->labels [index-1].text);
2309 bb->labels [index-1].text = g_strdup (text);
2312 /* paneletc is either the panel widget, or info or view or tree widget */
2313 WButtonBar *
2314 find_buttonbar (Dlg_head *h, Widget *paneletc)
2316 WButtonBar *bb;
2317 Widget_Item *item;
2318 int i;
2320 bb = 0;
2321 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2322 if (item->widget->callback == (callback_fn) buttonbar_callback){
2323 bb = (WButtonBar *) item->widget;
2324 break;
2327 return bb;
2330 void
2331 define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
2332 buttonbarfn cback, void *data)
2334 WButtonBar *bb = find_buttonbar (h, paneletc);
2335 if (!bb)
2336 return;
2338 set_label_text (bb, idx, text);
2339 bb->labels [idx-1].function = cback;
2340 bb->labels [idx-1].data = data;
2343 void
2344 define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
2346 define_label_data (h, paneletc, idx, text, (buttonbarfn) cback, 0);
2349 void
2350 redraw_labels (Dlg_head *h, Widget *paneletc)
2352 Widget_Item *item;
2353 int i;
2355 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2356 if (item->widget->callback == (callback_fn) buttonbar_callback){
2357 widget_redraw (h, item);
2358 return;