corrected some file operations dialog msgs
[midnight-commander.git] / src / widget.c
blobff164991cbb01f688892458e4e7acd39a474a8a7
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 */
45 #include "wtools.h" /* For common_dialog_repaint() */
47 static int button_event (Gpm_Event *event, WButton *b);
49 int quote = 0;
51 static int
52 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
54 char buf[BUF_SMALL];
55 int stop = 0;
56 int off = 0;
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 (h, 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 (h, 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 (h, 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 * Assuming that button text is malloc'ed, we may safely change it
191 * (as opposed to statically allocated); from other hand, excluding &
192 * and shifting data past it to the left results to one unused byte.
193 * This does not harm though :)
195 static void
196 button_scan_hotkey(WButton* b)
198 char* cp = strchr (b->text, '&');
200 if (cp != NULL && cp[1] != '\0'){
201 strcpy (cp, cp+1);
202 b->hotkey = tolower (*cp);
203 b->hotpos = cp - b->text;
207 WButton *
208 button_new (int y, int x, int action, int flags, char *text,
209 int (*callback)(int, void *), void *callback_data, char *tkname)
211 WButton *b = g_new (WButton, 1);
213 init_widget (&b->widget, y, x, 1, button_len (text, flags),
214 (callback_fn) button_callback,
215 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
217 b->action = action;
218 b->flags = flags;
219 b->selected = 0;
220 b->text = g_strdup (text);
221 b->callback = callback;
222 b->callback_data = callback_data;
223 widget_want_hotkey (b->widget, 1);
224 b->hotkey = 0;
225 b->hotpos = -1;
227 button_scan_hotkey(b);
228 return b;
231 void
232 button_set_text (WButton *b, char *text)
234 g_free (b->text);
235 b->text = g_strdup (text);
236 b->widget.cols = button_len (text, b->flags);
237 button_scan_hotkey(b);
238 dlg_redraw (b->widget.parent);
242 /* Radio button widget */
243 static int radio_event (Gpm_Event *event, WRadio *r);
245 static int
246 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
248 int i;
250 switch (Msg) {
251 case WIDGET_INIT:
252 return 1;
254 case WIDGET_HOTKEY:
256 int i, lp = tolower(Par);
257 char *cp;
259 for (i = 0; i < r->count; i++){
260 cp = strchr (r->texts [i], '&');
261 if (cp != NULL && cp[1] != '\0'){
262 int c = tolower (cp [1]);
264 if (c != lp)
265 continue;
266 r->pos = i;
267 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
268 return 1;
272 return 0;
274 case WIDGET_KEY:
275 switch (Par){
276 case ' ':
277 r->sel = r->pos;
278 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
279 radio_callback (h, r, WIDGET_FOCUS, ' ');
280 return 1;
282 case KEY_UP:
283 case KEY_LEFT:
284 if (r->pos > 0){
285 r->pos--;
286 return 1;
288 return 0;
290 case KEY_DOWN:
291 case KEY_RIGHT:
292 if (r->count - 1 > r->pos) {
293 r->pos++;
294 return 1;
297 return 0;
299 case WIDGET_CURSOR:
300 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
301 radio_callback (h, r, WIDGET_FOCUS, ' ');
302 widget_move (&r->widget, r->pos, 1);
303 break;
305 case WIDGET_UNFOCUS:
306 case WIDGET_FOCUS:
307 case WIDGET_DRAW:
308 for (i = 0; i < r->count; i++){
309 register unsigned char* cp;
310 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
311 widget_move (&r->widget, i, 0);
313 printw("(%c) ", (r->sel == i) ? '*' : ' ');
314 for (cp = r->texts[i]; *cp; cp++)
316 if (*cp == '&')
318 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
319 ? HOT_FOCUSC : HOT_NORMALC);
320 addch(*++cp);
321 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
323 else
324 addch(*cp);
327 return 1;
328 break;
330 return default_proc (h, Msg, Par);
333 static int
334 radio_event (Gpm_Event *event, WRadio *r)
336 if (event->type & (GPM_DOWN|GPM_UP)){
337 Dlg_head *h = r->widget.parent;
339 r->pos = event->y - 1;
340 dlg_select_widget (h, r);
341 if (event->type & GPM_UP){
342 radio_callback (h, r, WIDGET_KEY, ' ');
343 radio_callback (h, r, WIDGET_FOCUS, 0);
344 (*h->callback) (h, ' ', DLG_POST_KEY);
345 return MOU_NORMAL;
348 return MOU_NORMAL;
351 WRadio *
352 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
354 WRadio *r = g_new (WRadio, 1);
355 int i, max, m;
357 /* Compute the longest string */
358 max = 0;
359 for (i = 0; i < count; i++){
360 m = strlen (texts [i]);
361 if (m > max)
362 max = m;
365 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
366 0, (mouse_h) radio_event, tkname);
367 r->state = 1;
368 r->pos = 0;
369 r->sel = 0;
370 r->count = count;
371 r->texts = texts;
372 r->upper_letter_is_hotkey = use_hotkey;
373 widget_want_hotkey (r->widget, 1);
375 return r;
379 /* Checkbutton widget */
381 static int check_event (Gpm_Event *event, WCheck *b);
383 static int
384 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
386 switch (Msg) {
387 case WIDGET_INIT:
388 return 1;
390 case WIDGET_HOTKEY:
391 if (c->hotkey==Par ||
392 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
393 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
394 return 1;
395 } else
396 return 0;
398 case WIDGET_KEY:
399 if (Par != ' ')
400 break;
401 c->state ^= C_BOOL;
402 c->state ^= C_CHANGE;
403 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
404 check_callback (h, c, WIDGET_FOCUS, ' ');
405 return 1;
407 case WIDGET_CURSOR:
408 widget_move (&c->widget, 0, 1);
409 break;
411 case WIDGET_FOCUS:
412 case WIDGET_UNFOCUS:
413 case WIDGET_DRAW:
414 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
415 widget_move (&c->widget, 0, 0);
416 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
418 if (c->hotpos >= 0){
419 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
420 widget_move (&c->widget, 0, + c->hotpos+4);
421 addch ((unsigned char)c->text [c->hotpos]);
423 return 1;
425 return default_proc (h, Msg, Par);
428 static int
429 check_event (Gpm_Event *event, WCheck *c)
431 if (event->type & (GPM_DOWN|GPM_UP)){
432 Dlg_head *h = c->widget.parent;
434 dlg_select_widget (h, c);
435 if (event->type & GPM_UP){
436 check_callback (h, c, WIDGET_KEY, ' ');
437 check_callback (h, c, WIDGET_FOCUS, 0);
438 (*h->callback) (h, ' ', DLG_POST_KEY);
439 return MOU_NORMAL;
442 return MOU_NORMAL;
445 static void
446 check_destroy (WCheck *c)
448 g_free (c->text);
451 WCheck *
452 check_new (int y, int x, int state, char *text, char *tkname)
454 WCheck *c = g_new (WCheck, 1);
455 char *s, *t;
457 init_widget (&c->widget, y, x, 1, strlen (text),
458 (callback_fn)check_callback,
459 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
460 c->state = state ? C_BOOL : 0;
461 c->text = g_strdup (text);
462 c->hotkey = 0;
463 c->hotpos = -1;
464 widget_want_hotkey (c->widget, 1);
466 /* Scan for the hotkey */
467 for (s = text, t = c->text; *s; s++, t++){
468 if (*s != '&'){
469 *t = *s;
470 continue;
472 s++;
473 if (*s){
474 c->hotkey = tolower (*s);
475 c->hotpos = t - c->text;
477 *t = *s;
479 *t = 0;
480 return c;
484 /* Label widget */
486 static int
487 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
489 if (Msg == WIDGET_INIT)
490 return 1;
492 /* We don't want to get the focus */
493 if (Msg == WIDGET_FOCUS)
494 return 0;
495 if (Msg == WIDGET_DRAW && l->text){
496 char *p = l->text, *q, c = 0;
497 int y = 0;
498 if (l->transparent)
499 attrset (DEFAULT_COLOR);
500 else
501 attrset (NORMALC);
502 for (;;){
503 int xlen;
505 q = strchr (p, '\n');
506 if (q){
507 c = *q;
508 *q = 0;
510 widget_move (&l->widget, y, 0);
511 printw ("%s", p);
512 xlen = l->widget.cols - strlen (p);
513 if (xlen > 0)
514 printw ("%*s", xlen, " ");
515 if (!q)
516 break;
517 *q = c;
518 p = q + 1;
519 y++;
521 return 1;
523 return default_proc (h, Msg, Par);
526 void
527 label_set_text (WLabel *label, char *text)
529 int newcols = label->widget.cols;
531 if (label->text && text && !strcmp (label->text, text))
532 return; /* Flickering is not nice */
534 if (label->text){
535 g_free (label->text);
537 if (text){
538 label->text = g_strdup (text);
539 if (label->auto_adjust_cols) {
540 newcols = strlen (text);
541 if (newcols > label->widget.cols)
542 label->widget.cols = newcols;
544 } else
545 label->text = 0;
547 if (label->widget.parent)
548 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
550 if (newcols < label->widget.cols)
551 label->widget.cols = newcols;
554 static void
555 label_destroy (WLabel *l)
557 if (l->text)
558 g_free (l->text);
561 WLabel *
562 label_new (int y, int x, const char *text, char *tkname)
564 WLabel *l;
565 int width;
567 /* Multiline labels are immutable - no need to compute their sizes */
568 if (!text || strchr(text, '\n'))
569 width = 1;
570 else
571 width = strlen (text);
573 l = g_new (WLabel, 1);
574 init_widget (&l->widget, y, x, 1, width,
575 (callback_fn) label_callback,
576 (destroy_fn) label_destroy, NULL, tkname);
577 l->text = text ? g_strdup (text) : 0;
578 l->auto_adjust_cols = 1;
579 l->transparent = 0;
580 widget_want_cursor (l->widget, 0);
581 return l;
585 /* Gauge widget (progress indicator) */
586 /* Currently width is hardcoded here for text mode */
587 #define gauge_len 47
589 static int
590 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
593 if (Msg == WIDGET_INIT)
594 return 1;
596 /* We don't want to get the focus */
597 if (Msg == WIDGET_FOCUS)
598 return 0;
600 if (Msg == WIDGET_DRAW){
601 widget_move (&g->widget, 0, 0);
602 attrset (NORMALC);
603 if (!g->shown)
604 printw ("%*s", gauge_len, "");
605 else {
606 long percentage, columns;
607 long total = g->max, done = g->current;
609 if (total <= 0 || done < 0) {
610 done = 0;
611 total = 100;
613 if (done > total)
614 done = total;
615 while (total > 65535) {
616 total /= 256;
617 done /= 256;
619 percentage = (200 * done / total + 1) / 2;
620 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
621 addch ('[');
622 attrset (GAUGE_COLOR);
623 printw ("%*s", columns, "");
624 attrset (NORMALC);
625 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
627 return 1;
629 return default_proc (h, Msg, Par);
632 void
633 gauge_set_value (WGauge *g, int max, int current)
635 if (g->current == current && g->max == max)
636 return; /* Do not flicker */
637 if (max == 0)
638 max = 1; /* I do not like division by zero :) */
640 g->current = current;
641 g->max = max;
642 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
645 void
646 gauge_show (WGauge *g, int shown)
648 if (g->shown == shown)
649 return;
650 g->shown = shown;
651 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
654 static void
655 gauge_destroy (WGauge *g)
657 /* nothing */
660 WGauge *
661 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
663 WGauge *g = g_new (WGauge, 1);
665 init_widget (&g->widget, y, x, 1, gauge_len,
666 (callback_fn) gauge_callback,
667 (destroy_fn) gauge_destroy, NULL, tkname);
668 g->shown = shown;
669 if (max == 0)
670 max = 1; /* I do not like division by zero :) */
671 g->max = max;
672 g->current = current;
673 g->pixels = 0;
674 widget_want_cursor (g->widget, 0);
675 return g;
679 /* Input widget */
681 /* {{{ history button */
683 #define LARGE_HISTORY_BUTTON 1
685 #ifdef LARGE_HISTORY_BUTTON
686 # define HISTORY_BUTTON_WIDTH 3
687 #else
688 # define HISTORY_BUTTON_WIDTH 1
689 #endif
691 #define should_show_history_button(in) \
692 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
694 static void draw_history_button (WInput * in)
696 char c;
697 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
698 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
699 #ifdef LARGE_HISTORY_BUTTON
701 Dlg_head *h;
702 h = in->widget.parent;
703 #if 0
704 attrset (NORMALC); /* button has the same colour as other buttons */
705 addstr ("[ ]");
706 attrset (HOT_NORMALC);
707 #else
708 attrset (NORMAL_COLOR);
709 addstr ("[ ]");
710 /* Too distracting: attrset (MARKED_COLOR); */
711 #endif
712 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
713 addch (c);
715 #else
716 attrset (MARKED_COLOR);
717 addch (c);
718 #endif
721 /* }}} history button */
724 /* Input widgets now have a global kill ring */
725 /* Pointer to killed data */
726 static char *kill_buffer = 0;
728 void
729 update_input (WInput *in, int clear_first)
731 int has_history = 0;
732 int i, j;
733 unsigned char c;
734 int buf_len = strlen (in->buffer);
736 if (should_show_history_button (in))
737 has_history = HISTORY_BUTTON_WIDTH;
739 if (in->disable_update)
740 return;
742 /* Make the point visible */
743 if ((in->point < in->first_shown) ||
744 (in->point >= in->first_shown+in->field_len - has_history)){
745 in->first_shown = in->point - (in->field_len / 3);
746 if (in->first_shown < 0)
747 in->first_shown = 0;
750 /* Adjust the mark */
751 if (in->mark > buf_len)
752 in->mark = buf_len;
754 if (has_history)
755 draw_history_button (in);
757 attrset (in->color);
759 widget_move (&in->widget, 0, 0);
760 for (i = 0; i < in->field_len - has_history; i++)
761 addch (' ');
762 widget_move (&in->widget, 0, 0);
764 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
765 c = in->buffer [j++];
766 c = is_printable (c) ? c : '.';
767 if (in->is_password)
768 c = '*';
769 addch (c);
771 widget_move (&in->widget, 0, in->point - in->first_shown);
773 if (clear_first)
774 in->first = 0;
777 void
778 winput_set_origin (WInput *in, int x, int field_len)
780 in->widget.x = x;
781 in->field_len = in->widget.cols = field_len;
782 update_input (in, 0);
785 /* {{{ history saving and loading */
788 This loads and saves the history of an input line to and from the
789 widget. It is called with the widgets tk name on creation of the
790 widget, and returns the Hist list. It stores histories in the file
791 ~/.mc/history in using the profile code.
793 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
794 function) then input_new assigns the default text to be the last text
795 entered, or "" if not found.
798 int num_history_items_recorded = 60;
800 Hist *
801 history_get (char *input_name)
803 int i;
804 Hist *old, *new;
805 char *profile;
807 old = new = NULL;
809 if (!num_history_items_recorded) /* this is how to disable */
810 return 0;
811 if (!input_name)
812 return 0;
813 if (!*input_name)
814 return 0;
815 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
816 for (i = 0;; i++) {
817 char key_name[BUF_TINY];
818 char this_entry[BUF_LARGE];
819 g_snprintf (key_name, sizeof (key_name), "%d", i);
820 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
821 if (!*this_entry)
822 break;
823 new = g_new0 (Hist, 1);
824 new->text = g_strdup (this_entry);
825 new->prev = old; /* set up list pointers */
826 if (old)
827 old->next = new;
828 old = new;
830 g_free (profile);
831 return new; /* return pointer to last entry in list */
834 void
835 history_put (char *input_name, Hist *h)
837 int i;
838 char *profile;
840 if (!input_name)
841 return;
843 if (!*input_name)
844 return;
846 if (!h)
847 return;
849 if (!num_history_items_recorded) /* this is how to disable */
850 return;
852 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
854 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
855 close (i);
856 /* Just in case I forgot to strip passwords somewhere -- Norbert */
857 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
858 g_free (profile);
859 return;
862 while (h->next) /* go to end of list */
863 h = h->next;
865 /* go back 60 places */
866 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
867 h = h->prev;
869 if (input_name)
870 profile_clean_section (input_name, profile);
872 /* dump histories into profile */
873 for (i = 0; h; h = h->next){
874 if (h->text){
876 /* probably aren't any null entries, but lets be sure */
877 if (*(h->text)){
878 char key_name[BUF_TINY];
879 g_snprintf (key_name, sizeof(key_name), "%d", i++);
880 WritePrivateProfileString (input_name, key_name, h->text, profile);
884 g_free (profile);
887 /* }}} history saving and loading */
890 /* {{{ history display */
892 static char *
893 i18n_htitle (void)
895 static char *history_title = NULL;
897 if (history_title == NULL)
898 history_title = _(" History ");
899 return history_title;
902 static inline int listbox_fwd (WListbox *l);
904 char *
905 show_hist (Hist * history, int widget_x, int widget_y)
907 Hist *hi, *z;
908 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
909 int x, y, w, h;
910 char *q, *r = 0;
911 Dlg_head *query_dlg;
912 WListbox *query_list;
914 z = history;
915 if (!z)
916 return 0;
918 while (z->prev) /* goto first */
919 z = z->prev;
920 hi = z;
921 while (hi) {
922 if ((i = strlen (hi->text)) > maxlen)
923 maxlen = i;
924 count++;
925 hi = hi->next;
928 y = widget_y;
929 h = count + 2;
930 if (h <= y || y > LINES - 6) {
931 h = min (h, y - 1);
932 y -= h;
933 } else {
934 y++;
935 h = min (h, LINES - y);
938 if (widget_x > 2)
939 x = widget_x - 2;
940 else
941 x = 0;
942 if ((w = maxlen + 4) + x > COLS) {
943 w = min (w, COLS);
944 x = COLS - w;
947 query_dlg =
948 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
949 i18n_htitle (), DLG_COMPACT);
950 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
951 add_widget (query_dlg, query_list);
952 hi = z;
953 if (y < widget_y) {
954 while (hi) { /* traverse */
955 listbox_add_item (query_list, 0, 0, hi->text, NULL);
956 hi = hi->next;
958 while (listbox_fwd (query_list));
959 } else {
960 while (hi->next)
961 hi = hi->next;
962 while (hi) { /* traverse backwards */
963 listbox_add_item (query_list, 0, 0, hi->text, NULL);
964 hi = hi->prev;
967 run_dlg (query_dlg);
968 q = NULL;
969 if (query_dlg->ret_value != B_CANCEL) {
970 listbox_get_current (query_list, &q, NULL);
971 if (q)
972 r = g_strdup (q);
974 destroy_dlg (query_dlg);
975 return r;
978 static void do_show_hist (WInput * in)
980 char *r;
981 r = show_hist (in->history, in->widget.x, in->widget.y);
982 if (r) {
983 assign_text (in, r);
984 g_free (r);
988 /* }}} history display */
990 static void
991 input_destroy (WInput *in)
993 if (!in){
994 fprintf (stderr, "Internal error: null Input *\n");
995 exit (1);
998 new_input (in);
999 if (in->history){
1000 Hist *current, *old;
1002 if (!in->is_password) /* don't save passwords ;-) */
1003 history_put (in->history_name, in->history);
1005 current = in->history;
1006 while (current->next)
1007 current = current->next;
1008 while (current){
1009 old = current;
1010 current = current->prev;
1011 g_free (old->text);
1012 g_free (old);
1015 g_free (in->buffer);
1016 free_completions (in);
1017 if (in->history_name)
1018 g_free (in->history_name);
1021 static char disable_update = 0;
1023 void
1024 input_disable_update (WInput *in)
1026 in->disable_update++;
1029 void
1030 input_enable_update (WInput *in)
1032 in->disable_update--;
1033 update_input (in, 0);
1036 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1039 push_history (WInput *in, char *text)
1041 static int i18n;
1042 /* input widget where urls with passwords are entered without any
1043 vfs prefix */
1044 static const char *password_input_fields[] = {
1045 N_(" Link to a remote machine "),
1046 N_(" FTP to machine "),
1047 N_(" SMB link to machine ")
1049 Hist *new;
1050 char *p;
1051 int i;
1053 if (!i18n) {
1054 i18n = 1;
1055 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1056 password_input_fields[i] = _(password_input_fields[i]);
1059 for (p = text; *p == ' ' || *p == '\t'; p++);
1060 if (!*p)
1061 return 0;
1062 if (in->history){
1063 while (in->history->next)
1064 in->history = in->history->next;
1065 if (!strcmp (in->history->text, text))
1066 return 1;
1067 new = g_new (Hist, 1);
1068 in->history->next = new;
1069 } else
1070 new = g_new (Hist, 1);
1071 in->need_push = 0;
1072 new->next = 0;
1073 new->prev = in->history;
1074 new->text = g_strdup (text);
1075 if (in->history_name) {
1076 p = in->history_name + 3;
1077 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1078 if (strcmp (p, password_input_fields[i]) == 0)
1079 break;
1080 if (i < ELEMENTS(password_input_fields))
1081 strip_password (new->text, 0);
1082 else
1083 strip_password (new->text, 1);
1086 in->history = new;
1087 return 2;
1090 #undef ELEMENTS
1092 /* Cleans the input line and adds the current text to the history */
1093 void
1094 new_input (WInput *in)
1096 if (in->buffer)
1097 push_history (in, in->buffer);
1098 in->need_push = 1;
1099 in->buffer [0] = 0;
1100 in->point = 0;
1101 in->mark = 0;
1102 free_completions (in);
1103 update_input (in, 0);
1106 static int
1107 insert_char (WInput *in, int c_code)
1109 int i;
1111 if (c_code == -1)
1112 return 0;
1114 in->need_push = 1;
1115 if (strlen (in->buffer)+1 == in->current_max_len){
1116 /* Expand the buffer */
1117 char *narea = g_malloc (in->current_max_len + in->field_len);
1118 if (narea){
1119 char *p = in->buffer;
1121 strcpy (narea, in->buffer);
1122 in->buffer = narea;
1123 in->current_max_len += in->field_len;
1124 g_free (p);
1127 if (strlen (in->buffer)+1 < in->current_max_len){
1128 int l = strlen (&in->buffer [in->point]);
1129 for (i = l+1; i > 0; i--)
1130 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1131 in->buffer [in->point] = c_code;
1132 in->point++;
1134 return 1;
1137 static void
1138 beginning_of_line (WInput *in)
1140 in->point = 0;
1143 static void
1144 end_of_line (WInput *in)
1146 in->point = strlen (in->buffer);
1149 static void
1150 backward_char (WInput *in)
1152 if (in->point)
1153 in->point--;
1156 static void
1157 forward_char (WInput *in)
1159 if (in->buffer [in->point])
1160 in->point++;
1163 static void
1164 forward_word (WInput *in)
1166 unsigned char *p = in->buffer+in->point;
1168 while (*p && (isspace (*p) || ispunct (*p)))
1169 p++;
1170 while (*p && isalnum (*p))
1171 p++;
1172 in->point = p - in->buffer;
1175 static void
1176 backward_word (WInput *in)
1178 unsigned char *p = in->buffer+in->point;
1180 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1181 p--;
1182 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1183 p--;
1184 in->point = p - in->buffer;
1187 static void
1188 key_left (WInput *in)
1190 if (ctrl_pressed ())
1191 backward_word (in);
1192 else
1193 backward_char (in);
1196 static void
1197 key_right (WInput *in)
1199 if (ctrl_pressed ())
1200 forward_word (in);
1201 else
1202 forward_char (in);
1205 static void
1206 backward_delete (WInput *in)
1208 int i;
1210 if (!in->point)
1211 return;
1212 for (i = in->point; in->buffer [i-1]; i++)
1213 in->buffer [i-1] = in->buffer [i];
1214 in->need_push = 1;
1215 in->point--;
1218 static void
1219 delete_char (WInput *in)
1221 int i;
1223 for (i = in->point; in->buffer [i]; i++)
1224 in->buffer [i] = in->buffer [i+1];
1225 in->need_push = 1;
1228 static void
1229 copy_region (WInput *in, int x_first, int x_last)
1231 int first = min (x_first, x_last);
1232 int last = max (x_first, x_last);
1234 if (last == first)
1235 return;
1237 if (kill_buffer)
1238 g_free (kill_buffer);
1240 kill_buffer = g_malloc (last-first + 1);
1241 strncpy (kill_buffer, in->buffer+first, last-first);
1242 kill_buffer [last-first] = 0;
1245 static void
1246 delete_region (WInput *in, int x_first, int x_last)
1248 int first = min (x_first, x_last);
1249 int last = max (x_first, x_last);
1251 in->point = first;
1252 in->mark = first;
1253 strcpy (&in->buffer [first], &in->buffer [last]);
1254 in->need_push = 1;
1257 static void
1258 kill_word (WInput *in)
1260 int old_point = in->point;
1261 int new_point;
1263 forward_word (in);
1264 new_point = in->point;
1265 in->point = old_point;
1267 copy_region (in, old_point, new_point);
1268 delete_region (in, old_point, new_point);
1269 in->need_push = 1;
1272 static void
1273 back_kill_word (WInput *in)
1275 int old_point = in->point;
1276 int new_point;
1278 backward_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 set_mark (WInput *in)
1290 in->mark = in->point;
1293 static void
1294 kill_save (WInput *in)
1296 copy_region (in, in->mark, in->point);
1299 static void
1300 kill_region (WInput *in)
1302 kill_save (in);
1303 delete_region (in, in->point, in->mark);
1306 static void
1307 yank (WInput *in)
1309 char *p;
1311 if (!kill_buffer)
1312 return;
1313 for (p = kill_buffer; *p; p++)
1314 insert_char (in, *p);
1317 static void
1318 kill_line (WInput *in)
1320 if (kill_buffer)
1321 g_free (kill_buffer);
1322 kill_buffer = g_strdup (&in->buffer [in->point]);
1323 in->buffer [in->point] = 0;
1326 void
1327 assign_text (WInput *in, char *text)
1329 free_completions (in);
1330 g_free (in->buffer);
1331 in->buffer = g_strdup (text); /* was in->buffer->text */
1332 in->current_max_len = strlen (in->buffer) + 1;
1333 in->point = strlen (in->buffer);
1334 in->mark = 0;
1335 in->need_push = 1;
1338 static void
1339 hist_prev (WInput *in)
1341 if (!in->history)
1342 return;
1344 if (in->need_push) {
1345 switch (push_history (in, in->buffer)) {
1346 case 2: in->history = in->history->prev; break;
1347 case 1: if (in->history->prev) in->history = in->history->prev; break;
1348 case 0: break;
1350 } else if (in->history->prev)
1351 in->history = in->history->prev;
1352 else
1353 return;
1354 assign_text (in, in->history->text);
1355 in->need_push = 0;
1358 static void
1359 hist_next (WInput *in)
1361 if (in->need_push) {
1362 switch (push_history (in, in->buffer)) {
1363 case 2:
1364 assign_text (in, "");
1365 return;
1366 case 0:
1367 return;
1371 if (!in->history)
1372 return;
1374 if (!in->history->next) {
1375 assign_text (in, "");
1376 return;
1379 in->history = in->history->next;
1380 assign_text (in, in->history->text);
1381 in->need_push = 0;
1384 static const struct {
1385 int key_code;
1386 void (*fn)(WInput *in);
1387 } input_map [] = {
1388 /* Motion */
1389 { XCTRL('a'), beginning_of_line },
1390 { KEY_HOME, beginning_of_line },
1391 { KEY_A1, beginning_of_line },
1392 { XCTRL('e'), end_of_line },
1393 { KEY_END, end_of_line },
1394 { KEY_C1, end_of_line },
1395 { KEY_LEFT, key_left },
1396 { XCTRL('b'), backward_char },
1397 { ALT('b'), backward_word },
1398 { KEY_RIGHT, key_right },
1399 { XCTRL('f'), forward_char },
1400 { ALT('f'), forward_word },
1402 /* Editing */
1403 { 0177, backward_delete },
1404 { KEY_BACKSPACE, backward_delete },
1405 { XCTRL('h'), backward_delete },
1406 { KEY_DC, delete_char },
1407 { XCTRL('d'), delete_char },
1408 { ALT('d'), kill_word },
1409 { ALT(KEY_BACKSPACE), back_kill_word },
1410 { ALT(XCTRL('h')), back_kill_word },
1411 { ALT(127), back_kill_word },
1413 /* Region manipulation */
1414 { 0, set_mark },
1415 { XCTRL('w'), kill_region },
1416 { ALT('w'), kill_save },
1417 { XCTRL('y'), yank },
1418 { XCTRL('k'), kill_line },
1420 /* History */
1421 { ALT('p'), hist_prev },
1422 { ALT('n'), hist_next },
1423 { ALT('h'), do_show_hist },
1425 /* Completion */
1426 { ALT('\t'), complete },
1428 { 0, 0 }
1431 /* This function is a test for a special input key used in complete.c */
1432 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1433 and 2 if it is a complete key */
1435 is_in_input_map (WInput *in, int c_code)
1437 int i;
1439 for (i = 0; input_map [i].fn; i++)
1440 if (c_code == input_map [i].key_code) {
1441 if (input_map [i].fn == complete)
1442 return 2;
1443 else
1444 return 1;
1446 return 0;
1449 static void
1450 port_region_marked_for_delete (WInput *in)
1452 *in->buffer = 0;
1453 in->point = 0;
1454 in->first = 0;
1458 handle_char (WInput *in, int c_code)
1460 int i;
1461 int v;
1463 v = 0;
1465 if (quote){
1466 free_completions (in);
1467 v = insert_char (in, c_code);
1468 update_input (in, 1);
1469 quote = 0;
1470 return v;
1473 for (i = 0; input_map [i].fn; i++){
1474 if (c_code == input_map [i].key_code){
1475 if (input_map [i].fn != complete)
1476 free_completions (in);
1477 (*input_map [i].fn)(in);
1478 v = 1;
1479 break;
1482 if (!input_map [i].fn){
1483 if (c_code > 255 || !is_printable (c_code))
1484 return 0;
1485 if (in->first){
1486 port_region_marked_for_delete (in);
1488 free_completions (in);
1489 v = insert_char (in, c_code);
1490 in->inserted_one = c_code;
1492 if (!disable_update)
1493 update_input (in, 1);
1494 return v;
1497 /* Inserts text in input line */
1498 void
1499 stuff (WInput *in, char *text, int insert_extra_space)
1501 input_disable_update (in);
1502 while (*text)
1503 handle_char (in, *text++);
1504 if (insert_extra_space)
1505 handle_char (in, ' ');
1506 input_enable_update (in);
1507 update_input (in, 1);
1510 void
1511 input_set_point (WInput *in, int pos)
1513 if (pos > in->current_max_len)
1514 pos = in->current_max_len;
1515 if (pos != in->point)
1516 free_completions (in);
1517 in->point = pos;
1518 update_input (in, 1);
1521 static int
1522 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1524 switch (Msg){
1525 case WIDGET_INIT:
1526 return 1;
1528 case WIDGET_KEY:
1529 if (Par == XCTRL('q')){
1530 int v;
1532 quote = 1;
1533 v = handle_char (in, mi_getch ());
1534 quote = 0;
1535 return v;
1537 if (Par == KEY_UP || Par == KEY_DOWN ||
1538 Par == ESC_CHAR || Par == KEY_F(10) ||
1539 Par == XCTRL('g'))
1540 return 0; /* We don't handle up/down */
1542 if (Par == '\n'){
1543 dlg_one_down (h);
1544 return 1;
1546 return handle_char (in, Par);
1548 case WIDGET_FOCUS:
1549 case WIDGET_UNFOCUS:
1550 case WIDGET_DRAW:
1551 update_input (in, 0);
1552 break;
1553 case WIDGET_CURSOR:
1554 widget_move (&in->widget, 0, in->point - in->first_shown);
1555 return 1;
1558 return default_proc (h, Msg, Par);
1561 static int
1562 input_event (Gpm_Event * event, WInput * in)
1564 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1565 dlg_select_widget (in->widget.parent, in);
1567 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1568 && should_show_history_button (in)) {
1569 do_show_hist (in);
1570 } else {
1571 in->point = strlen (in->buffer);
1572 if (event->x - in->first_shown - 1 < in->point)
1573 in->point = event->x - in->first_shown - 1;
1574 if (in->point < 0)
1575 in->point = 0;
1577 update_input (in, 1);
1579 return MOU_NORMAL;
1582 WInput *
1583 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1585 WInput *in = g_new (WInput, 1);
1586 int initial_buffer_len;
1588 init_widget (&in->widget, y, x, 1, len,
1589 (callback_fn) input_callback,
1590 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1592 /* history setup */
1593 in->history = NULL;
1594 in->history_name = 0;
1595 if (tkname) {
1596 if (*tkname) {
1597 in->history_name = g_strdup (tkname);
1598 in->history = history_get (tkname);
1602 if (!def_text)
1603 def_text = "";
1605 if (def_text == INPUT_LAST_TEXT) {
1606 def_text = "";
1607 if (in->history)
1608 if (in->history->text)
1609 def_text = in->history->text;
1611 initial_buffer_len = 1 + max (len, strlen (def_text));
1612 in->widget.options |= W_IS_INPUT;
1613 in->completions = NULL;
1614 in->completion_flags =
1615 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1616 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1617 in->current_max_len = initial_buffer_len;
1618 in->buffer = g_malloc (initial_buffer_len);
1619 in->color = color;
1620 in->field_len = len;
1621 in->first = 1;
1622 in->first_shown = 0;
1623 in->disable_update = 0;
1624 in->mark = 0;
1625 in->need_push = 1;
1626 in->is_password = 0;
1628 strcpy (in->buffer, def_text);
1629 in->point = strlen (in->buffer);
1630 return in;
1634 /* Listbox widget */
1636 /* Should draw the scrollbar, but currently draws only
1637 * indications that there is more information
1639 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1641 static void
1642 listbox_drawscroll (WListbox *l)
1644 extern int slow_terminal;
1645 int line;
1646 int i, top;
1647 int max_line = l->height-1;
1649 /* Are we at the top? */
1650 widget_move (&l->widget, 0, l->width);
1651 if (l->list == l->top)
1652 one_vline ();
1653 else
1654 addch ('^');
1656 /* Are we at the bottom? */
1657 widget_move (&l->widget, max_line, l->width);
1658 top = listbox_cdiff (l->list, l->top);
1659 if ((top + l->height == l->count) || l->height >= l->count)
1660 one_vline ();
1661 else
1662 addch ('v');
1664 /* Now draw the nice relative pointer */
1665 if (l->count)
1666 line = 1+ ((l->pos * (l->height-2)) / l->count);
1667 else
1668 line = 0;
1670 for (i = 1; i < max_line; i++){
1671 widget_move (&l->widget, i, l->width);
1672 if (i != line)
1673 one_vline ();
1674 else
1675 addch ('*');
1679 static void
1680 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1682 WLEntry *e;
1683 int i;
1684 int sel_line;
1685 int normalc = NORMALC;
1686 int selc;
1687 char *text;
1689 if (focused){
1690 selc = FOCUSC;
1691 } else {
1692 selc = HOT_FOCUSC;
1694 sel_line = -1;
1696 for (e = l->top, i = 0; (i < l->height); i++){
1698 /* Display the entry */
1699 if (e == l->current && sel_line == -1){
1700 sel_line = i;
1701 attrset (selc);
1702 } else
1703 attrset (normalc);
1705 widget_move (&l->widget, i, 0);
1707 if ((i > 0 && e == l->list) || !l->list)
1708 text = "";
1709 else {
1710 text = e->text;
1711 e = e->next;
1713 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1715 l->cursor_y = sel_line;
1716 if (!l->scrollbar)
1717 return;
1718 attrset (normalc);
1719 listbox_drawscroll (l);
1722 /* Returns the number of items between s and e,
1723 must be on the same linked list */
1724 static int
1725 listbox_cdiff (WLEntry *s, WLEntry *e)
1727 int count;
1729 for (count = 0; s != e; count++)
1730 s = s->next;
1731 return count;
1734 static WLEntry *
1735 listbox_check_hotkey (WListbox *l, int key)
1737 int i;
1738 WLEntry *e;
1740 i = 0;
1741 e = l->list;
1742 if (!e)
1743 return 0;
1745 while (1){
1747 /* If we didn't find anything, return */
1748 if (i && e == l->list)
1749 return 0;
1751 if (e->hotkey == key)
1752 return e;
1754 i++;
1755 e = e->next;
1759 /* Used only for display updating, for avoiding line at a time scroll */
1760 void
1761 listbox_select_last (WListbox *l, int set_top)
1763 if (l->list){
1764 l->current = l->list->prev;
1765 l->pos = l->count - 1;
1766 if (set_top)
1767 l->top = l->list->prev;
1771 void
1772 listbox_remove_list (WListbox *l)
1774 WLEntry *p, *q;
1776 if (!l->count)
1777 return;
1779 p = l->list;
1781 while (l->count--) {
1782 q = p->next;
1783 g_free (p->text);
1784 g_free (p);
1785 p = q;
1787 l->pos = l->count = 0;
1788 l->list = l->top = l->current = 0;
1792 * bor 30.10.96: added force flag to remove *last* entry as well
1793 * bor 30.10.96: corrected selection bug if last entry was removed
1796 void
1797 listbox_remove_current (WListbox *l, int force)
1799 WLEntry *p;
1801 /* Ok, note: this won't allow for emtpy lists */
1802 if (!force && (!l->count || l->count == 1))
1803 return;
1805 l->count--;
1806 p = l->current;
1808 if (l->count) {
1809 l->current->next->prev = l->current->prev;
1810 l->current->prev->next = l->current->next;
1811 if (p->next == l->list) {
1812 l->current = p->prev;
1813 l->pos--;
1815 else
1816 l->current = p->next;
1818 if (p == l->list)
1819 l->list = l->top = p->next;
1820 } else {
1821 l->pos = 0;
1822 l->list = l->top = l->current = 0;
1825 g_free (p->text);
1826 g_free (p);
1829 /* Makes *e the selected entry (sets current and pos) */
1830 void
1831 listbox_select_entry (WListbox *l, WLEntry *dest)
1833 WLEntry *e;
1834 int pos;
1835 int top_seen;
1837 top_seen = 0;
1839 /* Special case */
1840 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1842 if (e == l->top)
1843 top_seen = 1;
1845 if (e == dest){
1846 l->current = e;
1847 if (top_seen){
1848 while (listbox_cdiff (l->top, l->current) >= l->height)
1849 l->top = l->top->next;
1850 } else {
1851 l->top = l->current;
1853 l->pos = pos;
1854 return;
1857 /* If we are unable to find it, set decent values */
1858 l->current = l->top = l->list;
1859 l->pos = 0;
1862 /* Selects from base the pos element */
1863 static WLEntry *
1864 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1866 WLEntry *last = l->list->prev;
1868 if (base == last)
1869 return last;
1870 while (pos--){
1871 base = base->next;
1872 if (base == last)
1873 break;
1875 return base;
1878 static inline int
1879 listbox_back (WListbox *l)
1881 if (l->pos){
1882 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1883 return 1;
1885 return 0;
1888 static inline int
1889 listbox_fwd (WListbox *l)
1891 if (l->current != l->list->prev){
1892 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1893 return 1;
1895 return 0;
1898 /* Returns 1 if we want a redraw */
1899 static int
1900 listbox_key (WListbox *l, int key)
1902 int i;
1903 int j = 0;
1905 if (!l->list)
1906 return 0;
1908 switch (key){
1909 case KEY_HOME:
1910 case KEY_A1:
1911 l->current = l->top = l->list;
1912 l->pos = 0;
1913 return 1;
1915 case KEY_END:
1916 case KEY_C1:
1917 l->current = l->top = l->list->prev;
1918 for (i = min (l->height - 1, l->count - 1); i; i--)
1919 l->top = l->top->prev;
1920 l->pos = l->count - 1;
1921 return 1;
1923 case XCTRL('p'):
1924 case KEY_UP:
1925 listbox_back (l);
1926 return 1;
1928 case XCTRL('n'):
1929 case KEY_DOWN:
1930 listbox_fwd (l);
1931 return 1;
1933 case KEY_NPAGE:
1934 case XCTRL('v'):
1935 for (i = 0; i < l->height-1; i++)
1936 j |= listbox_fwd (l);
1937 return j > 0;
1939 case KEY_PPAGE:
1940 case ALT('v'):
1941 for (i = 0; i < l->height-1; i++)
1942 j |= listbox_back (l);
1943 return j > 0;
1945 return 0;
1948 static int listbox_event (Gpm_Event *event, WListbox *l);
1949 static int
1950 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
1952 WLEntry *e;
1953 /* int selected_color; Never used */
1954 int ret_code;
1956 switch (msg){
1957 case WIDGET_INIT:
1958 return 1;
1960 case WIDGET_HOTKEY:
1961 if ((e = listbox_check_hotkey (l, par)) != NULL){
1962 listbox_select_entry (l, e);
1964 /* Take the appropriate action */
1965 if (l->action == listbox_finish){
1966 l->widget.parent->running = 0;
1967 l->widget.parent->ret_value = B_ENTER;
1968 } else if (l->action == listbox_cback){
1969 if ((*l->cback)(l) == listbox_finish){
1970 l->widget.parent->running = 0;
1971 l->widget.parent->ret_value = B_ENTER;
1974 return 1;
1975 } else
1976 return 0;
1978 case WIDGET_KEY:
1979 if ((ret_code = listbox_key (l, par)))
1980 listbox_draw (l, h, 1);
1981 return ret_code;
1983 case WIDGET_CURSOR:
1984 widget_move (&l->widget, l->cursor_y, 0);
1985 return 1;
1987 case WIDGET_FOCUS:
1988 case WIDGET_UNFOCUS:
1989 case WIDGET_DRAW:
1990 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
1991 return 1;
1993 return default_proc (h, msg, par);
1996 static int
1997 listbox_event (Gpm_Event *event, WListbox *l)
1999 int i;
2001 Dlg_head *h = l->widget.parent;
2003 /* Single click */
2004 if (event->type & GPM_DOWN)
2005 dlg_select_widget (l->widget.parent, l);
2006 if (!l->list)
2007 return MOU_NORMAL;
2008 if (event->type & (GPM_DOWN|GPM_DRAG)){
2009 if (event->x < 0 || event->x >= l->width)
2010 return MOU_REPEAT;
2011 if (event->y < 1)
2012 for (i = -event->y; i >= 0; i--)
2013 listbox_back (l);
2014 else if (event->y > l->height)
2015 for (i = event->y - l->height; i > 0; i--)
2016 listbox_fwd (l);
2017 else
2018 listbox_select_entry (l, listbox_select_pos (l, l->top,
2019 event->y - 1));
2021 /* We need to refresh ourselves since the dialog manager doesn't */
2022 /* know about this event */
2023 listbox_callback (h, l, WIDGET_DRAW, 0);
2024 mc_refresh ();
2025 return MOU_REPEAT;
2028 /* Double click */
2029 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2030 if (event->x < 0 || event->x >= l->width)
2031 return MOU_NORMAL;
2032 if (event->y < 1 || event->y > l->height)
2033 return MOU_NORMAL;
2035 dlg_select_widget (l->widget.parent, l);
2036 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2038 switch (l->action){
2039 case listbox_nothing:
2040 break;
2042 case listbox_finish:
2043 h->ret_value = B_ENTER;
2044 dlg_stop (h);
2045 return MOU_ENDLOOP;
2047 case listbox_cback:
2048 if ((*l->cback)(l) == listbox_finish)
2049 return MOU_ENDLOOP;
2052 return MOU_NORMAL;
2055 static void
2056 listbox_destroy (WListbox *l)
2058 WLEntry *n, *p = l->list;
2059 int i;
2061 for (i = 0; i < l->count; i++){
2062 n = p->next;
2063 g_free (p->text);
2064 g_free (p);
2065 p = n;
2069 WListbox *
2070 listbox_new (int y, int x, int width, int height,
2071 int action, lcback callback, char *tkname)
2073 WListbox *l = g_new (WListbox, 1);
2074 extern int slow_terminal;
2076 init_widget (&l->widget, y, x, height, width,
2077 (callback_fn)listbox_callback,
2078 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2080 l->list = l->top = l->current = 0;
2081 l->pos = 0;
2082 l->width = width;
2083 if (height <= 0)
2084 l->height = 1;
2085 else
2086 l->height = height;
2087 l->count = 0;
2088 l->top = 0;
2089 l->current= 0;
2090 l->cback = callback;
2091 l->action = action;
2092 l->allow_duplicates = 1;
2093 l->scrollbar = slow_terminal ? 0 : 1;
2094 widget_want_hotkey (l->widget, 1);
2096 return l;
2099 /* Listbox item adding function. They still lack a lot of functionality */
2100 /* any takers? */
2101 /* 1.11.96 bor: added pos argument to control placement of new entry */
2102 static void
2103 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2105 if (!l->list){
2106 l->list = e;
2107 l->top = e;
2108 l->current = e;
2109 e->next = l->list;
2110 e->prev = l->list;
2111 } else if (pos == LISTBOX_APPEND_AT_END) {
2112 e->next = l->list;
2113 e->prev = l->list->prev;
2114 l->list->prev->next = e;
2115 l->list->prev = e;
2116 } else if (pos == LISTBOX_APPEND_BEFORE){
2117 e->next = l->current;
2118 e->prev = l->current->prev;
2119 l->current->prev->next = e;
2120 l->current->prev = e;
2121 if (l->list == l->current) { /* move list one position down */
2122 l->list = e;
2123 l->top = e;
2125 } else if (pos == LISTBOX_APPEND_AFTER) {
2126 e->prev = l->current;
2127 e->next = l->current->next;
2128 l->current->next->prev = e;
2129 l->current->next = e;
2131 l->count++;
2134 char *
2135 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2136 void *data)
2138 WLEntry *entry;
2140 if (!l)
2141 return 0;
2143 if (!l->allow_duplicates)
2144 if (listbox_search_text (l, text))
2145 return 0;
2147 entry = g_new (WLEntry, 1);
2148 entry->text = g_strdup (text);
2149 entry->data = data;
2150 entry->hotkey = hotkey;
2152 listbox_append_item (l, entry, pos);
2154 return entry->text;
2157 /* Selects the nth entry in the listbox */
2158 void
2159 listbox_select_by_number (WListbox *l, int n)
2161 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2164 WLEntry *
2165 listbox_search_text (WListbox *l, char *text)
2167 WLEntry *e;
2169 e = l->list;
2170 if (!e)
2171 return NULL;
2173 do {
2174 if(!strcmp (e->text, text))
2175 return e;
2176 e = e->next;
2177 } while (e!=l->list);
2179 return NULL;
2182 /* Returns the current string text as well as the associated extra data */
2183 void
2184 listbox_get_current (WListbox *l, char **string, char **extra)
2186 if (!l->current){
2187 *string = 0;
2188 *extra = 0;
2190 if (string && l->current)
2191 *string = l->current->text;
2192 if (extra && l->current)
2193 *extra = l->current->data;
2196 static int
2197 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2199 int i;
2201 switch (msg){
2202 case WIDGET_INIT:
2203 return 1;
2205 case WIDGET_FOCUS:
2206 return 0;
2208 case WIDGET_HOTKEY:
2209 for (i = 0; i < 10; i++){
2210 if (par == KEY_F(i+1) && bb->labels [i].function){
2211 (*bb->labels [i].function)(bb->labels [i].data);
2212 return 1;
2215 return 0;
2217 case WIDGET_DRAW:
2218 if (!bb->visible)
2219 return 1;
2220 widget_move (&bb->widget, 0, 0);
2221 attrset (DEFAULT_COLOR);
2222 printw ("%-*s", bb->widget.cols, "");
2223 for (i = 0; i < COLS/8 && i < 10; i++){
2224 widget_move (&bb->widget, 0, i*8);
2225 attrset (DEFAULT_COLOR);
2226 printw ("%d", i+1);
2227 attrset (SELECTED_COLOR);
2228 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2229 bb->labels [i].text ? bb->labels [i].text : "");
2230 attrset (DEFAULT_COLOR);
2232 attrset (SELECTED_COLOR);
2233 return 1;
2235 return default_proc (h, msg, par);
2238 static void
2239 buttonbar_destroy (WButtonBar *bb)
2241 int i;
2243 for (i = 0; i < 10; i++){
2244 if (bb->labels [i].text)
2245 g_free (bb->labels [i].text);
2249 static int
2250 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2252 int button;
2254 if (!(event->type & GPM_UP))
2255 return MOU_NORMAL;
2256 if (event->y == 2)
2257 return MOU_NORMAL;
2258 button = event->x / 8;
2259 if (button < 10 && bb->labels [button].function)
2260 (*bb->labels [button].function)(bb->labels [button].data);
2261 return MOU_NORMAL;
2264 WButtonBar *
2265 buttonbar_new (int visible)
2267 int i;
2268 WButtonBar *bb = g_new (WButtonBar, 1);
2270 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2271 (callback_fn) buttonbar_callback,
2272 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2274 bb->visible = visible;
2275 for (i = 0; i < 10; i++){
2276 bb->labels [i].text = 0;
2277 bb->labels [i].function = 0;
2279 widget_want_hotkey (bb->widget, 1);
2280 widget_want_cursor (bb->widget, 0);
2282 return bb;
2285 static void
2286 set_label_text (WButtonBar * bb, int index, char *text)
2288 if (bb->labels[index - 1].text)
2289 g_free (bb->labels[index - 1].text);
2291 bb->labels[index - 1].text = g_strdup (text);
2294 /* paneletc is either the panel widget, or info or view or tree widget */
2295 static WButtonBar *
2296 find_buttonbar (Dlg_head * h, Widget * paneletc)
2298 WButtonBar *bb;
2299 Widget_Item *item;
2300 int i;
2302 bb = 0;
2303 for (i = 0, item = h->current; i < h->count; i++, item = item->next) {
2304 if (item->widget->callback == (callback_fn) buttonbar_callback) {
2305 bb = (WButtonBar *) item->widget;
2306 break;
2309 return bb;
2312 void
2313 define_label_data (Dlg_head * h, Widget * paneletc, int idx, char *text,
2314 buttonbarfn cback, void *data)
2316 WButtonBar *bb = find_buttonbar (h, paneletc);
2317 if (!bb)
2318 return;
2320 set_label_text (bb, idx, text);
2321 bb->labels[idx - 1].function = cback;
2322 bb->labels[idx - 1].data = data;
2325 void
2326 define_label (Dlg_head * h, Widget * paneletc, int idx, char *text,
2327 void (*cback) (void))
2329 define_label_data (h, paneletc, idx, text, (buttonbarfn) cback, 0);
2332 void
2333 redraw_labels (Dlg_head *h, Widget *paneletc)
2335 Widget_Item *item;
2336 int i;
2338 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2339 if (item->widget->callback == (callback_fn) buttonbar_callback){
2340 widget_redraw (h, item);
2341 return;