updated the .TP cleanup for coherency in the key description pages.
[midnight-commander.git] / src / widget.c
blob62720bf70c61dbe5a0add144175319678b2702d4
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996 the Free Software Foundation
5 Authors: 1994, 1995 Radek Doulik
6 1994, 1995 Miguel de Icaza
7 1995 Jakub Jelinek
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <config.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <ctype.h>
34 #include "global.h"
35 #include "tty.h"
36 #include "color.h"
37 #include "mouse.h"
38 #include "dlg.h"
39 #include "widget.h"
40 #include "win.h"
41 #include "complete.h"
42 #include "key.h" /* XCTRL and ALT macros */
43 #include "profile.h" /* for history loading and saving */
44 #include "wtools.h" /* For common_dialog_repaint() */
46 static int button_event (Gpm_Event *event, WButton *b);
48 int quote = 0;
50 static int
51 button_callback (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 *
800 history_get (char *input_name)
802 int i;
803 Hist *old, *new;
804 char *profile;
806 old = new = NULL;
808 if (!num_history_items_recorded) /* this is how to disable */
809 return 0;
810 if (!input_name)
811 return 0;
812 if (!*input_name)
813 return 0;
814 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
815 for (i = 0;; i++) {
816 char key_name[BUF_TINY];
817 char this_entry[BUF_LARGE];
818 g_snprintf (key_name, sizeof (key_name), "%d", i);
819 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
820 if (!*this_entry)
821 break;
822 new = g_new0 (Hist, 1);
823 new->text = g_strdup (this_entry);
824 new->prev = old; /* set up list pointers */
825 if (old)
826 old->next = new;
827 old = new;
829 g_free (profile);
830 return new; /* return pointer to last entry in list */
833 void
834 history_put (char *input_name, Hist *h)
836 int i;
837 char *profile;
839 if (!input_name)
840 return;
842 if (!*input_name)
843 return;
845 if (!h)
846 return;
848 if (!num_history_items_recorded) /* this is how to disable */
849 return;
851 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
853 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
854 close (i);
855 /* Just in case I forgot to strip passwords somewhere -- Norbert */
856 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
857 g_free (profile);
858 return;
861 while (h->next) /* go to end of list */
862 h = h->next;
864 /* go back 60 places */
865 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
866 h = h->prev;
868 if (input_name)
869 profile_clean_section (input_name, profile);
871 /* dump histories into profile */
872 for (i = 0; h; h = h->next){
873 if (h->text){
875 /* probably aren't any null entries, but lets be sure */
876 if (*(h->text)){
877 char key_name[BUF_TINY];
878 g_snprintf (key_name, sizeof(key_name), "%d", i++);
879 WritePrivateProfileString (input_name, key_name, h->text, profile);
883 g_free (profile);
886 /* }}} history saving and loading */
889 /* {{{ history display */
891 static char *
892 i18n_htitle (void)
894 static char *history_title = NULL;
896 if (history_title == NULL)
897 history_title = _(" History ");
898 return history_title;
901 static inline int listbox_fwd (WListbox *l);
903 char *
904 show_hist (Hist * history, int widget_x, int widget_y)
906 Hist *hi, *z;
907 size_t maxlen = strlen (i18n_htitle ()), i, count = 0;
908 int x, y, w, h;
909 char *q, *r = 0;
910 Dlg_head *query_dlg;
911 WListbox *query_list;
913 z = history;
914 if (!z)
915 return 0;
917 while (z->prev) /* goto first */
918 z = z->prev;
919 hi = z;
920 while (hi) {
921 if ((i = strlen (hi->text)) > maxlen)
922 maxlen = i;
923 count++;
924 hi = hi->next;
927 y = widget_y;
928 h = count + 2;
929 if (h <= y || y > LINES - 6) {
930 h = min (h, y - 1);
931 y -= h;
932 } else {
933 y++;
934 h = min (h, LINES - y);
937 if (widget_x > 2)
938 x = widget_x - 2;
939 else
940 x = 0;
941 if ((w = maxlen + 4) + x > COLS) {
942 w = min (w, COLS);
943 x = COLS - w;
946 query_dlg =
947 create_dlg (y, x, h, w, dialog_colors, NULL, "[History-query]",
948 i18n_htitle (), DLG_COMPACT);
949 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
950 add_widget (query_dlg, query_list);
951 hi = z;
952 if (y < widget_y) {
953 while (hi) { /* traverse */
954 listbox_add_item (query_list, 0, 0, hi->text, NULL);
955 hi = hi->next;
957 while (listbox_fwd (query_list));
958 } else {
959 while (hi->next)
960 hi = hi->next;
961 while (hi) { /* traverse backwards */
962 listbox_add_item (query_list, 0, 0, hi->text, NULL);
963 hi = hi->prev;
966 run_dlg (query_dlg);
967 q = NULL;
968 if (query_dlg->ret_value != B_CANCEL) {
969 listbox_get_current (query_list, &q, NULL);
970 if (q)
971 r = g_strdup (q);
973 destroy_dlg (query_dlg);
974 return r;
977 static void do_show_hist (WInput * in)
979 char *r;
980 r = show_hist (in->history, in->widget.x, in->widget.y);
981 if (r) {
982 assign_text (in, r);
983 g_free (r);
987 /* }}} history display */
989 static void
990 input_destroy (WInput *in)
992 if (!in){
993 fprintf (stderr, "Internal error: null Input *\n");
994 exit (1);
997 new_input (in);
998 if (in->history){
999 Hist *current, *old;
1001 if (!in->is_password) /* don't save passwords ;-) */
1002 history_put (in->history_name, in->history);
1004 current = in->history;
1005 while (current->next)
1006 current = current->next;
1007 while (current){
1008 old = current;
1009 current = current->prev;
1010 g_free (old->text);
1011 g_free (old);
1014 g_free (in->buffer);
1015 free_completions (in);
1016 if (in->history_name)
1017 g_free (in->history_name);
1020 static char disable_update = 0;
1022 void
1023 input_disable_update (WInput *in)
1025 in->disable_update++;
1028 void
1029 input_enable_update (WInput *in)
1031 in->disable_update--;
1032 update_input (in, 0);
1035 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1038 push_history (WInput *in, char *text)
1040 static int i18n;
1041 /* input widget where urls with passwords are entered without any
1042 vfs prefix */
1043 static const char *password_input_fields[] = {
1044 N_(" Link to a remote machine "),
1045 N_(" FTP to machine "),
1046 N_(" SMB link to machine ")
1048 Hist *new;
1049 char *p;
1050 int i;
1052 if (!i18n) {
1053 i18n = 1;
1054 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1055 password_input_fields[i] = _(password_input_fields[i]);
1058 for (p = text; *p == ' ' || *p == '\t'; p++);
1059 if (!*p)
1060 return 0;
1061 if (in->history){
1062 while (in->history->next)
1063 in->history = in->history->next;
1064 if (!strcmp (in->history->text, text))
1065 return 1;
1066 new = g_new (Hist, 1);
1067 in->history->next = new;
1068 } else
1069 new = g_new (Hist, 1);
1070 in->need_push = 0;
1071 new->next = 0;
1072 new->prev = in->history;
1073 new->text = g_strdup (text);
1074 if (in->history_name) {
1075 p = in->history_name + 3;
1076 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1077 if (strcmp (p, password_input_fields[i]) == 0)
1078 break;
1079 if (i < ELEMENTS(password_input_fields))
1080 strip_password (new->text, 0);
1081 else
1082 strip_password (new->text, 1);
1085 in->history = new;
1086 return 2;
1089 #undef ELEMENTS
1091 /* Cleans the input line and adds the current text to the history */
1092 void
1093 new_input (WInput *in)
1095 if (in->buffer)
1096 push_history (in, in->buffer);
1097 in->need_push = 1;
1098 in->buffer [0] = 0;
1099 in->point = 0;
1100 in->mark = 0;
1101 free_completions (in);
1102 update_input (in, 0);
1105 static int
1106 insert_char (WInput *in, int c_code)
1108 int i;
1110 if (c_code == -1)
1111 return 0;
1113 in->need_push = 1;
1114 if (strlen (in->buffer)+1 == in->current_max_len){
1115 /* Expand the buffer */
1116 char *narea = g_malloc (in->current_max_len + in->field_len);
1117 if (narea){
1118 char *p = in->buffer;
1120 strcpy (narea, in->buffer);
1121 in->buffer = narea;
1122 in->current_max_len += in->field_len;
1123 g_free (p);
1126 if (strlen (in->buffer)+1 < in->current_max_len){
1127 int l = strlen (&in->buffer [in->point]);
1128 for (i = l+1; i > 0; i--)
1129 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1130 in->buffer [in->point] = c_code;
1131 in->point++;
1133 return 1;
1136 static void
1137 beginning_of_line (WInput *in)
1139 in->point = 0;
1142 static void
1143 end_of_line (WInput *in)
1145 in->point = strlen (in->buffer);
1148 static void
1149 backward_char (WInput *in)
1151 if (in->point)
1152 in->point--;
1155 static void
1156 forward_char (WInput *in)
1158 if (in->buffer [in->point])
1159 in->point++;
1162 static void
1163 forward_word (WInput *in)
1165 unsigned char *p = in->buffer+in->point;
1167 while (*p && (isspace (*p) || ispunct (*p)))
1168 p++;
1169 while (*p && isalnum (*p))
1170 p++;
1171 in->point = p - in->buffer;
1174 static void
1175 backward_word (WInput *in)
1177 unsigned char *p = in->buffer+in->point;
1179 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1180 p--;
1181 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1182 p--;
1183 in->point = p - in->buffer;
1186 static void
1187 key_left (WInput *in)
1189 if (ctrl_pressed ())
1190 backward_word (in);
1191 else
1192 backward_char (in);
1195 static void
1196 key_right (WInput *in)
1198 if (ctrl_pressed ())
1199 forward_word (in);
1200 else
1201 forward_char (in);
1204 static void
1205 backward_delete (WInput *in)
1207 int i;
1209 if (!in->point)
1210 return;
1211 for (i = in->point; in->buffer [i-1]; i++)
1212 in->buffer [i-1] = in->buffer [i];
1213 in->need_push = 1;
1214 in->point--;
1217 static void
1218 delete_char (WInput *in)
1220 int i;
1222 for (i = in->point; in->buffer [i]; i++)
1223 in->buffer [i] = in->buffer [i+1];
1224 in->need_push = 1;
1227 static void
1228 copy_region (WInput *in, int x_first, int x_last)
1230 int first = min (x_first, x_last);
1231 int last = max (x_first, x_last);
1233 if (last == first)
1234 return;
1236 if (kill_buffer)
1237 g_free (kill_buffer);
1239 kill_buffer = g_malloc (last-first + 1);
1240 strncpy (kill_buffer, in->buffer+first, last-first);
1241 kill_buffer [last-first] = 0;
1244 static void
1245 delete_region (WInput *in, int x_first, int x_last)
1247 int first = min (x_first, x_last);
1248 int last = max (x_first, x_last);
1250 in->point = first;
1251 in->mark = first;
1252 strcpy (&in->buffer [first], &in->buffer [last]);
1253 in->need_push = 1;
1256 static void
1257 kill_word (WInput *in)
1259 int old_point = in->point;
1260 int new_point;
1262 forward_word (in);
1263 new_point = in->point;
1264 in->point = old_point;
1266 copy_region (in, old_point, new_point);
1267 delete_region (in, old_point, new_point);
1268 in->need_push = 1;
1271 static void
1272 back_kill_word (WInput *in)
1274 int old_point = in->point;
1275 int new_point;
1277 backward_word (in);
1278 new_point = in->point;
1279 in->point = old_point;
1281 copy_region (in, old_point, new_point);
1282 delete_region (in, old_point, new_point);
1283 in->need_push = 1;
1286 static void
1287 set_mark (WInput *in)
1289 in->mark = in->point;
1292 static void
1293 kill_save (WInput *in)
1295 copy_region (in, in->mark, in->point);
1298 static void
1299 kill_region (WInput *in)
1301 kill_save (in);
1302 delete_region (in, in->point, in->mark);
1305 static void
1306 yank (WInput *in)
1308 char *p;
1310 if (!kill_buffer)
1311 return;
1312 for (p = kill_buffer; *p; p++)
1313 insert_char (in, *p);
1316 static void
1317 kill_line (WInput *in)
1319 if (kill_buffer)
1320 g_free (kill_buffer);
1321 kill_buffer = g_strdup (&in->buffer [in->point]);
1322 in->buffer [in->point] = 0;
1325 void
1326 assign_text (WInput *in, char *text)
1328 free_completions (in);
1329 g_free (in->buffer);
1330 in->buffer = g_strdup (text); /* was in->buffer->text */
1331 in->current_max_len = strlen (in->buffer) + 1;
1332 in->point = strlen (in->buffer);
1333 in->mark = 0;
1334 in->need_push = 1;
1337 static void
1338 hist_prev (WInput *in)
1340 if (!in->history)
1341 return;
1343 if (in->need_push) {
1344 switch (push_history (in, in->buffer)) {
1345 case 2: in->history = in->history->prev; break;
1346 case 1: if (in->history->prev) in->history = in->history->prev; break;
1347 case 0: break;
1349 } else if (in->history->prev)
1350 in->history = in->history->prev;
1351 else
1352 return;
1353 assign_text (in, in->history->text);
1354 in->need_push = 0;
1357 static void
1358 hist_next (WInput *in)
1360 if (in->need_push) {
1361 switch (push_history (in, in->buffer)) {
1362 case 2:
1363 assign_text (in, "");
1364 return;
1365 case 0:
1366 return;
1370 if (!in->history)
1371 return;
1373 if (!in->history->next) {
1374 assign_text (in, "");
1375 return;
1378 in->history = in->history->next;
1379 assign_text (in, in->history->text);
1380 in->need_push = 0;
1383 static const struct {
1384 int key_code;
1385 void (*fn)(WInput *in);
1386 } input_map [] = {
1387 /* Motion */
1388 { XCTRL('a'), beginning_of_line },
1389 { KEY_HOME, beginning_of_line },
1390 { KEY_A1, beginning_of_line },
1391 { XCTRL('e'), end_of_line },
1392 { KEY_END, end_of_line },
1393 { KEY_C1, end_of_line },
1394 { KEY_LEFT, key_left },
1395 { XCTRL('b'), backward_char },
1396 { ALT('b'), backward_word },
1397 { KEY_RIGHT, key_right },
1398 { XCTRL('f'), forward_char },
1399 { ALT('f'), forward_word },
1401 /* Editing */
1402 { 0177, backward_delete },
1403 { KEY_BACKSPACE, backward_delete },
1404 { XCTRL('h'), backward_delete },
1405 { KEY_DC, delete_char },
1406 { XCTRL('d'), delete_char },
1407 { ALT('d'), kill_word },
1408 { ALT(KEY_BACKSPACE), back_kill_word },
1409 { ALT(XCTRL('h')), back_kill_word },
1410 { ALT(127), back_kill_word },
1412 /* Region manipulation */
1413 { 0, set_mark },
1414 { XCTRL('w'), kill_region },
1415 { ALT('w'), kill_save },
1416 { XCTRL('y'), yank },
1417 { XCTRL('k'), kill_line },
1419 /* History */
1420 { ALT('p'), hist_prev },
1421 { ALT('n'), hist_next },
1422 { ALT('h'), do_show_hist },
1424 /* Completion */
1425 { ALT('\t'), complete },
1427 { 0, 0 }
1430 /* This function is a test for a special input key used in complete.c */
1431 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1432 and 2 if it is a complete key */
1434 is_in_input_map (WInput *in, int c_code)
1436 int i;
1438 for (i = 0; input_map [i].fn; i++)
1439 if (c_code == input_map [i].key_code) {
1440 if (input_map [i].fn == complete)
1441 return 2;
1442 else
1443 return 1;
1445 return 0;
1448 static void
1449 port_region_marked_for_delete (WInput *in)
1451 *in->buffer = 0;
1452 in->point = 0;
1453 in->first = 0;
1457 handle_char (WInput *in, int c_code)
1459 int i;
1460 int v;
1462 v = 0;
1464 if (quote){
1465 free_completions (in);
1466 v = insert_char (in, c_code);
1467 update_input (in, 1);
1468 quote = 0;
1469 return v;
1472 for (i = 0; input_map [i].fn; i++){
1473 if (c_code == input_map [i].key_code){
1474 if (input_map [i].fn != complete)
1475 free_completions (in);
1476 (*input_map [i].fn)(in);
1477 v = 1;
1478 break;
1481 if (!input_map [i].fn){
1482 if (c_code > 255 || !is_printable (c_code))
1483 return 0;
1484 if (in->first){
1485 port_region_marked_for_delete (in);
1487 free_completions (in);
1488 v = insert_char (in, c_code);
1489 in->inserted_one = c_code;
1491 if (!disable_update)
1492 update_input (in, 1);
1493 return v;
1496 /* Inserts text in input line */
1497 void
1498 stuff (WInput *in, char *text, int insert_extra_space)
1500 input_disable_update (in);
1501 while (*text)
1502 handle_char (in, *text++);
1503 if (insert_extra_space)
1504 handle_char (in, ' ');
1505 input_enable_update (in);
1506 update_input (in, 1);
1509 void
1510 input_set_point (WInput *in, int pos)
1512 if (pos > in->current_max_len)
1513 pos = in->current_max_len;
1514 if (pos != in->point)
1515 free_completions (in);
1516 in->point = pos;
1517 update_input (in, 1);
1521 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1523 switch (Msg){
1524 case WIDGET_INIT:
1525 return 1;
1527 case WIDGET_KEY:
1528 if (Par == XCTRL('q')){
1529 int v;
1531 quote = 1;
1532 v = handle_char (in, mi_getch ());
1533 quote = 0;
1534 return v;
1536 if (Par == KEY_UP || Par == KEY_DOWN ||
1537 Par == ESC_CHAR || Par == KEY_F(10) ||
1538 Par == XCTRL('g'))
1539 return 0; /* We don't handle up/down */
1541 if (Par == '\n'){
1542 dlg_one_down (h);
1543 return 1;
1545 return handle_char (in, Par);
1547 case WIDGET_FOCUS:
1548 case WIDGET_UNFOCUS:
1549 case WIDGET_DRAW:
1550 update_input (in, 0);
1551 break;
1552 case WIDGET_CURSOR:
1553 widget_move (&in->widget, 0, in->point - in->first_shown);
1554 return 1;
1557 return default_proc (h, Msg, Par);
1560 static int
1561 input_event (Gpm_Event * event, WInput * in)
1563 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1564 dlg_select_widget (in->widget.parent, in);
1566 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1567 && should_show_history_button (in)) {
1568 do_show_hist (in);
1569 } else {
1570 in->point = strlen (in->buffer);
1571 if (event->x - in->first_shown - 1 < in->point)
1572 in->point = event->x - in->first_shown - 1;
1573 if (in->point < 0)
1574 in->point = 0;
1576 update_input (in, 1);
1578 return MOU_NORMAL;
1581 WInput *
1582 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1584 WInput *in = g_new (WInput, 1);
1585 int initial_buffer_len;
1587 init_widget (&in->widget, y, x, 1, len,
1588 (callback_fn) input_callback,
1589 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1591 /* history setup */
1592 in->history = NULL;
1593 in->history_name = 0;
1594 if (tkname) {
1595 if (*tkname) {
1596 in->history_name = g_strdup (tkname);
1597 in->history = history_get (tkname);
1601 if (!def_text)
1602 def_text = "";
1604 if (def_text == INPUT_LAST_TEXT) {
1605 def_text = "";
1606 if (in->history)
1607 if (in->history->text)
1608 def_text = in->history->text;
1610 initial_buffer_len = 1 + max (len, strlen (def_text));
1611 in->widget.options |= W_IS_INPUT;
1612 in->completions = NULL;
1613 in->completion_flags =
1614 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1615 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1616 in->current_max_len = initial_buffer_len;
1617 in->buffer = g_malloc (initial_buffer_len);
1618 in->color = color;
1619 in->field_len = len;
1620 in->first = 1;
1621 in->first_shown = 0;
1622 in->disable_update = 0;
1623 in->mark = 0;
1624 in->need_push = 1;
1625 in->is_password = 0;
1627 strcpy (in->buffer, def_text);
1628 in->point = strlen (in->buffer);
1629 return in;
1633 /* Listbox widget */
1635 /* Should draw the scrollbar, but currently draws only
1636 * indications that there is more information
1638 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1640 static void
1641 listbox_drawscroll (WListbox *l)
1643 extern int slow_terminal;
1644 int line;
1645 int i, top;
1646 int max_line = l->height-1;
1648 /* Are we at the top? */
1649 widget_move (&l->widget, 0, l->width);
1650 if (l->list == l->top)
1651 one_vline ();
1652 else
1653 addch ('^');
1655 /* Are we at the bottom? */
1656 widget_move (&l->widget, max_line, l->width);
1657 top = listbox_cdiff (l->list, l->top);
1658 if ((top + l->height == l->count) || l->height >= l->count)
1659 one_vline ();
1660 else
1661 addch ('v');
1663 /* Now draw the nice relative pointer */
1664 if (l->count)
1665 line = 1+ ((l->pos * (l->height-2)) / l->count);
1666 else
1667 line = 0;
1669 for (i = 1; i < max_line; i++){
1670 widget_move (&l->widget, i, l->width);
1671 if (i != line)
1672 one_vline ();
1673 else
1674 addch ('*');
1678 static void
1679 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1681 WLEntry *e;
1682 int i;
1683 int sel_line;
1684 int normalc = NORMALC;
1685 int selc;
1686 char *text;
1688 if (focused){
1689 selc = FOCUSC;
1690 } else {
1691 selc = HOT_FOCUSC;
1693 sel_line = -1;
1695 for (e = l->top, i = 0; (i < l->height); i++){
1697 /* Display the entry */
1698 if (e == l->current && sel_line == -1){
1699 sel_line = i;
1700 attrset (selc);
1701 } else
1702 attrset (normalc);
1704 widget_move (&l->widget, i, 0);
1706 if ((i > 0 && e == l->list) || !l->list)
1707 text = "";
1708 else {
1709 text = e->text;
1710 e = e->next;
1712 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1714 l->cursor_y = sel_line;
1715 if (!l->scrollbar)
1716 return;
1717 attrset (normalc);
1718 listbox_drawscroll (l);
1721 /* Returns the number of items between s and e,
1722 must be on the same linked list */
1723 static int
1724 listbox_cdiff (WLEntry *s, WLEntry *e)
1726 int count;
1728 for (count = 0; s != e; count++)
1729 s = s->next;
1730 return count;
1733 static WLEntry *
1734 listbox_check_hotkey (WListbox *l, int key)
1736 int i;
1737 WLEntry *e;
1739 i = 0;
1740 e = l->list;
1741 if (!e)
1742 return 0;
1744 while (1){
1746 /* If we didn't find anything, return */
1747 if (i && e == l->list)
1748 return 0;
1750 if (e->hotkey == key)
1751 return e;
1753 i++;
1754 e = e->next;
1758 /* Used only for display updating, for avoiding line at a time scroll */
1759 void
1760 listbox_select_last (WListbox *l, int set_top)
1762 if (l->list){
1763 l->current = l->list->prev;
1764 l->pos = l->count - 1;
1765 if (set_top)
1766 l->top = l->list->prev;
1770 void
1771 listbox_remove_list (WListbox *l)
1773 WLEntry *p, *q;
1775 if (!l->count)
1776 return;
1778 p = l->list;
1780 while (l->count--) {
1781 q = p->next;
1782 g_free (p->text);
1783 g_free (p);
1784 p = q;
1786 l->pos = l->count = 0;
1787 l->list = l->top = l->current = 0;
1791 * bor 30.10.96: added force flag to remove *last* entry as well
1792 * bor 30.10.96: corrected selection bug if last entry was removed
1795 void
1796 listbox_remove_current (WListbox *l, int force)
1798 WLEntry *p;
1800 /* Ok, note: this won't allow for emtpy lists */
1801 if (!force && (!l->count || l->count == 1))
1802 return;
1804 l->count--;
1805 p = l->current;
1807 if (l->count) {
1808 l->current->next->prev = l->current->prev;
1809 l->current->prev->next = l->current->next;
1810 if (p->next == l->list) {
1811 l->current = p->prev;
1812 l->pos--;
1814 else
1815 l->current = p->next;
1817 if (p == l->list)
1818 l->list = l->top = p->next;
1819 } else {
1820 l->pos = 0;
1821 l->list = l->top = l->current = 0;
1824 g_free (p->text);
1825 g_free (p);
1828 /* Makes *e the selected entry (sets current and pos) */
1829 void
1830 listbox_select_entry (WListbox *l, WLEntry *dest)
1832 WLEntry *e;
1833 int pos;
1834 int top_seen;
1836 top_seen = 0;
1838 /* Special case */
1839 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1841 if (e == l->top)
1842 top_seen = 1;
1844 if (e == dest){
1845 l->current = e;
1846 if (top_seen){
1847 while (listbox_cdiff (l->top, l->current) >= l->height)
1848 l->top = l->top->next;
1849 } else {
1850 l->top = l->current;
1852 l->pos = pos;
1853 return;
1856 /* If we are unable to find it, set decent values */
1857 l->current = l->top = l->list;
1858 l->pos = 0;
1861 /* Selects from base the pos element */
1862 static WLEntry *
1863 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1865 WLEntry *last = l->list->prev;
1867 if (base == last)
1868 return last;
1869 while (pos--){
1870 base = base->next;
1871 if (base == last)
1872 break;
1874 return base;
1877 static inline int
1878 listbox_back (WListbox *l)
1880 if (l->pos){
1881 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1882 return 1;
1884 return 0;
1887 static inline int
1888 listbox_fwd (WListbox *l)
1890 if (l->current != l->list->prev){
1891 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1892 return 1;
1894 return 0;
1897 /* Returns 1 if we want a redraw */
1898 static int
1899 listbox_key (WListbox *l, int key)
1901 int i;
1902 int j = 0;
1904 if (!l->list)
1905 return 0;
1907 switch (key){
1908 case KEY_HOME:
1909 case KEY_A1:
1910 l->current = l->top = l->list;
1911 l->pos = 0;
1912 return 1;
1914 case KEY_END:
1915 case KEY_C1:
1916 l->current = l->top = l->list->prev;
1917 for (i = min (l->height - 1, l->count - 1); i; i--)
1918 l->top = l->top->prev;
1919 l->pos = l->count - 1;
1920 return 1;
1922 case XCTRL('p'):
1923 case KEY_UP:
1924 listbox_back (l);
1925 return 1;
1927 case XCTRL('n'):
1928 case KEY_DOWN:
1929 listbox_fwd (l);
1930 return 1;
1932 case KEY_NPAGE:
1933 case XCTRL('v'):
1934 for (i = 0; i < l->height-1; i++)
1935 j |= listbox_fwd (l);
1936 return j > 0;
1938 case KEY_PPAGE:
1939 case ALT('v'):
1940 for (i = 0; i < l->height-1; i++)
1941 j |= listbox_back (l);
1942 return j > 0;
1944 return 0;
1947 static int listbox_event (Gpm_Event *event, WListbox *l);
1948 static int
1949 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
1951 WLEntry *e;
1952 /* int selected_color; Never used */
1953 int ret_code;
1955 switch (msg){
1956 case WIDGET_INIT:
1957 return 1;
1959 case WIDGET_HOTKEY:
1960 if ((e = listbox_check_hotkey (l, par)) != NULL){
1961 listbox_select_entry (l, e);
1963 /* Take the appropriate action */
1964 if (l->action == listbox_finish){
1965 l->widget.parent->running = 0;
1966 l->widget.parent->ret_value = B_ENTER;
1967 } else if (l->action == listbox_cback){
1968 if ((*l->cback)(l) == listbox_finish){
1969 l->widget.parent->running = 0;
1970 l->widget.parent->ret_value = B_ENTER;
1973 return 1;
1974 } else
1975 return 0;
1977 case WIDGET_KEY:
1978 if ((ret_code = listbox_key (l, par)))
1979 listbox_draw (l, h, 1);
1980 return ret_code;
1982 case WIDGET_CURSOR:
1983 widget_move (&l->widget, l->cursor_y, 0);
1984 return 1;
1986 case WIDGET_FOCUS:
1987 case WIDGET_UNFOCUS:
1988 case WIDGET_DRAW:
1989 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
1990 return 1;
1992 return default_proc (h, msg, par);
1995 static int
1996 listbox_event (Gpm_Event *event, WListbox *l)
1998 int i;
2000 Dlg_head *h = l->widget.parent;
2002 /* Single click */
2003 if (event->type & GPM_DOWN)
2004 dlg_select_widget (l->widget.parent, l);
2005 if (!l->list)
2006 return MOU_NORMAL;
2007 if (event->type & (GPM_DOWN|GPM_DRAG)){
2008 if (event->x < 0 || event->x >= l->width)
2009 return MOU_REPEAT;
2010 if (event->y < 1)
2011 for (i = -event->y; i >= 0; i--)
2012 listbox_back (l);
2013 else if (event->y > l->height)
2014 for (i = event->y - l->height; i > 0; i--)
2015 listbox_fwd (l);
2016 else
2017 listbox_select_entry (l, listbox_select_pos (l, l->top,
2018 event->y - 1));
2020 /* We need to refresh ourselves since the dialog manager doesn't */
2021 /* know about this event */
2022 listbox_callback (h, l, WIDGET_DRAW, 0);
2023 mc_refresh ();
2024 return MOU_REPEAT;
2027 /* Double click */
2028 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2029 if (event->x < 0 || event->x >= l->width)
2030 return MOU_NORMAL;
2031 if (event->y < 1 || event->y > l->height)
2032 return MOU_NORMAL;
2034 dlg_select_widget (l->widget.parent, l);
2035 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2037 switch (l->action){
2038 case listbox_nothing:
2039 break;
2041 case listbox_finish:
2042 h->ret_value = B_ENTER;
2043 dlg_stop (h);
2044 return MOU_NORMAL;
2046 case listbox_cback:
2047 if ((*l->cback)(l) == listbox_finish)
2048 return MOU_NORMAL;
2051 return MOU_NORMAL;
2054 static void
2055 listbox_destroy (WListbox *l)
2057 WLEntry *n, *p = l->list;
2058 int i;
2060 for (i = 0; i < l->count; i++){
2061 n = p->next;
2062 g_free (p->text);
2063 g_free (p);
2064 p = n;
2068 WListbox *
2069 listbox_new (int y, int x, int width, int height,
2070 int action, lcback callback, char *tkname)
2072 WListbox *l = g_new (WListbox, 1);
2073 extern int slow_terminal;
2075 init_widget (&l->widget, y, x, height, width,
2076 (callback_fn)listbox_callback,
2077 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2079 l->list = l->top = l->current = 0;
2080 l->pos = 0;
2081 l->width = width;
2082 if (height <= 0)
2083 l->height = 1;
2084 else
2085 l->height = height;
2086 l->count = 0;
2087 l->top = 0;
2088 l->current= 0;
2089 l->cback = callback;
2090 l->action = action;
2091 l->allow_duplicates = 1;
2092 l->scrollbar = slow_terminal ? 0 : 1;
2093 widget_want_hotkey (l->widget, 1);
2095 return l;
2098 /* Listbox item adding function. They still lack a lot of functionality */
2099 /* any takers? */
2100 /* 1.11.96 bor: added pos argument to control placement of new entry */
2101 static void
2102 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2104 if (!l->list){
2105 l->list = e;
2106 l->top = e;
2107 l->current = e;
2108 e->next = l->list;
2109 e->prev = l->list;
2110 } else if (pos == LISTBOX_APPEND_AT_END) {
2111 e->next = l->list;
2112 e->prev = l->list->prev;
2113 l->list->prev->next = e;
2114 l->list->prev = e;
2115 } else if (pos == LISTBOX_APPEND_BEFORE){
2116 e->next = l->current;
2117 e->prev = l->current->prev;
2118 l->current->prev->next = e;
2119 l->current->prev = e;
2120 if (l->list == l->current) { /* move list one position down */
2121 l->list = e;
2122 l->top = e;
2124 } else if (pos == LISTBOX_APPEND_AFTER) {
2125 e->prev = l->current;
2126 e->next = l->current->next;
2127 l->current->next->prev = e;
2128 l->current->next = e;
2130 l->count++;
2133 char *
2134 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2135 void *data)
2137 WLEntry *entry;
2139 if (!l)
2140 return 0;
2142 if (!l->allow_duplicates)
2143 if (listbox_search_text (l, text))
2144 return 0;
2146 entry = g_new (WLEntry, 1);
2147 entry->text = g_strdup (text);
2148 entry->data = data;
2149 entry->hotkey = hotkey;
2151 listbox_append_item (l, entry, pos);
2153 return entry->text;
2156 /* Selects the nth entry in the listbox */
2157 void
2158 listbox_select_by_number (WListbox *l, int n)
2160 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2163 WLEntry *
2164 listbox_search_text (WListbox *l, char *text)
2166 WLEntry *e;
2168 e = l->list;
2169 if (!e)
2170 return NULL;
2172 do {
2173 if(!strcmp (e->text, text))
2174 return e;
2175 e = e->next;
2176 } while (e!=l->list);
2178 return NULL;
2181 /* Returns the current string text as well as the associated extra data */
2182 void
2183 listbox_get_current (WListbox *l, char **string, char **extra)
2185 if (!l->current){
2186 *string = 0;
2187 *extra = 0;
2189 if (string && l->current)
2190 *string = l->current->text;
2191 if (extra && l->current)
2192 *extra = l->current->data;
2195 static int
2196 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2198 int i;
2200 switch (msg){
2201 case WIDGET_INIT:
2202 return 1;
2204 case WIDGET_FOCUS:
2205 return 0;
2207 case WIDGET_HOTKEY:
2208 for (i = 0; i < 10; i++){
2209 if (par == KEY_F(i+1) && bb->labels [i].function){
2210 (*bb->labels [i].function)(bb->labels [i].data);
2211 return 1;
2214 return 0;
2216 case WIDGET_DRAW:
2217 if (!bb->visible)
2218 return 1;
2219 widget_move (&bb->widget, 0, 0);
2220 attrset (DEFAULT_COLOR);
2221 printw ("%-*s", bb->widget.cols, "");
2222 for (i = 0; i < COLS/8 && i < 10; i++){
2223 widget_move (&bb->widget, 0, i*8);
2224 attrset (DEFAULT_COLOR);
2225 printw ("%d", i+1);
2226 attrset (SELECTED_COLOR);
2227 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2228 bb->labels [i].text ? bb->labels [i].text : "");
2229 attrset (DEFAULT_COLOR);
2231 attrset (SELECTED_COLOR);
2232 return 1;
2234 return default_proc (h, msg, par);
2237 static void
2238 buttonbar_destroy (WButtonBar *bb)
2240 int i;
2242 for (i = 0; i < 10; i++){
2243 if (bb->labels [i].text)
2244 g_free (bb->labels [i].text);
2248 static int
2249 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2251 int button;
2253 if (!(event->type & GPM_UP))
2254 return MOU_NORMAL;
2255 if (event->y == 2)
2256 return MOU_NORMAL;
2257 button = event->x / 8;
2258 if (button < 10 && bb->labels [button].function)
2259 (*bb->labels [button].function)(bb->labels [button].data);
2260 return MOU_NORMAL;
2263 WButtonBar *
2264 buttonbar_new (int visible)
2266 int i;
2267 WButtonBar *bb = g_new (WButtonBar, 1);
2269 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2270 (callback_fn) buttonbar_callback,
2271 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2273 bb->visible = visible;
2274 for (i = 0; i < 10; i++){
2275 bb->labels [i].text = 0;
2276 bb->labels [i].function = 0;
2278 widget_want_hotkey (bb->widget, 1);
2279 widget_want_cursor (bb->widget, 0);
2281 return bb;
2284 static void
2285 set_label_text (WButtonBar * bb, int index, char *text)
2287 if (bb->labels[index - 1].text)
2288 g_free (bb->labels[index - 1].text);
2290 bb->labels[index - 1].text = g_strdup (text);
2293 /* paneletc is either the panel widget, or info or view or tree widget */
2294 static WButtonBar *
2295 find_buttonbar (Dlg_head * h, Widget * paneletc)
2297 WButtonBar *bb;
2298 Widget_Item *item;
2299 int i;
2301 bb = 0;
2302 for (i = 0, item = h->current; i < h->count; i++, item = item->next) {
2303 if (item->widget->callback == (callback_fn) buttonbar_callback) {
2304 bb = (WButtonBar *) item->widget;
2305 break;
2308 return bb;
2311 void
2312 define_label_data (Dlg_head * h, Widget * paneletc, int idx, char *text,
2313 buttonbarfn cback, void *data)
2315 WButtonBar *bb = find_buttonbar (h, paneletc);
2316 if (!bb)
2317 return;
2319 set_label_text (bb, idx, text);
2320 bb->labels[idx - 1].function = cback;
2321 bb->labels[idx - 1].data = data;
2324 void
2325 define_label (Dlg_head * h, Widget * paneletc, int idx, char *text,
2326 void (*cback) (void))
2328 define_label_data (h, paneletc, idx, text, (buttonbarfn) cback, 0);
2331 void
2332 redraw_labels (Dlg_head *h, Widget *paneletc)
2334 Widget_Item *item;
2335 int i;
2337 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2338 if (item->widget->callback == (callback_fn) buttonbar_callback){
2339 widget_redraw (h, item);
2340 return;