*** empty log message ***
[midnight-commander.git] / src / widget.c
blob4b3818ef49f64e3c76606dfde644afe91abf9a6d
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996 the Free Software Foundation
5 Authors: 1994, 1995 Radek Doulik
6 1994, 1995 Miguel de Icaza
7 1995 Jakub Jelinek
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <config.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <ctype.h>
35 #include "global.h"
36 #include "tty.h"
37 #include "color.h"
38 #include "mouse.h"
39 #include "dlg.h"
40 #include "widget.h"
41 #include "win.h"
42 #include "complete.h"
43 #include "key.h" /* XCTRL and ALT macros */
44 #include "profile.h" /* for history loading and saving */
46 static int button_event (Gpm_Event *event, WButton *b);
48 int quote = 0;
50 static int
51 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
53 char buf[BUF_SMALL];
54 int stop = 0;
55 int off = 0;
57 switch (Msg){
58 case WIDGET_INIT:
59 return 1;
61 case WIDGET_HOTKEY:
62 if (b->hotkey == Par || toupper(b->hotkey) == Par){
63 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
64 return 1;
65 } else
66 return 0;
68 case WIDGET_KEY:
69 if (Par != ' ' && Par != '\n')
70 break;
72 if (b->callback)
73 stop = (*b->callback)(b->action, b->callback_data);
74 if (!b->callback || stop){
75 h->ret_value = b->action;
76 dlg_stop (h);
78 return 1;
80 case WIDGET_CURSOR:
81 switch (b->flags) {
82 case DEFPUSH_BUTTON:
83 off = 3;
84 break;
85 case NORMAL_BUTTON:
86 off = 2;
87 break;
88 case NARROW_BUTTON:
89 off = 1;
90 break;
91 case HIDDEN_BUTTON:
92 default:
93 off = 0;
94 break;
96 widget_move (&b->widget, 0, b->hotpos + off);
97 return 1;
99 case WIDGET_UNFOCUS:
100 case WIDGET_FOCUS:
101 case WIDGET_DRAW:
102 if (Msg==WIDGET_UNFOCUS)
103 b->selected = 0;
104 else if (Msg==WIDGET_FOCUS)
105 b->selected = 1;
107 switch (b->flags){
108 case DEFPUSH_BUTTON:
109 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
110 off = 3;
111 break;
112 case NORMAL_BUTTON:
113 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
114 off = 2;
115 break;
116 case NARROW_BUTTON:
117 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
118 off = 1;
119 break;
120 case HIDDEN_BUTTON:
121 default:
122 buf[0] = '\0';
123 off = 0;
124 break;
127 attrset ((b->selected) ? FOCUSC : NORMALC);
128 widget_move (&b->widget, 0, 0);
130 addstr (buf);
132 if (b->hotpos >= 0){
133 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
134 widget_move (&b->widget, 0, b->hotpos+off);
135 addch ((unsigned char)b->text [b->hotpos]);
137 if (Msg == WIDGET_FOCUS)
138 break;
139 else
140 return 1;
141 break;
143 return default_proc (h, Msg, Par);
146 static int
147 button_event (Gpm_Event *event, WButton *b)
149 if (event->type & (GPM_DOWN|GPM_UP)){
150 Dlg_head *h=b->widget.parent;
151 dlg_select_widget (h, b);
152 if (event->type & GPM_UP){
153 button_callback (h, b, WIDGET_KEY, ' ');
154 (*h->callback) (h, ' ', DLG_POST_KEY);
155 return MOU_NORMAL;
158 return MOU_NORMAL;
161 static void
162 button_destroy (WButton *b)
164 g_free (b->text);
167 static int
168 button_len (const char *text, unsigned int flags)
170 int ret = strlen (text);
171 switch (flags){
172 case DEFPUSH_BUTTON:
173 ret += 6;
174 break;
175 case NORMAL_BUTTON:
176 ret += 4;
177 break;
178 case NARROW_BUTTON:
179 ret += 2;
180 break;
181 case HIDDEN_BUTTON:
182 default:
183 return 0;
185 return ret;
189 * Assuming that button text is malloc'ed, we may safely change it
190 * (as opposed to statically allocated); from other hand, excluding &
191 * and shifting data past it to the left results to one unused byte.
192 * This does not harm though :)
194 static void
195 button_scan_hotkey(WButton* b)
197 char* cp = strchr (b->text, '&');
199 if (cp != NULL && cp[1] != '\0'){
200 strcpy (cp, cp+1);
201 b->hotkey = tolower (*cp);
202 b->hotpos = cp - b->text;
206 WButton *
207 button_new (int y, int x, int action, int flags, char *text,
208 int (*callback)(int, void *), void *callback_data, char *tkname)
210 WButton *b = g_new (WButton, 1);
212 init_widget (&b->widget, y, x, 1, button_len (text, flags),
213 (callback_fn) button_callback,
214 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
216 b->action = action;
217 b->flags = flags;
218 b->selected = 0;
219 b->text = g_strdup (text);
220 b->callback = callback;
221 b->callback_data = callback_data;
222 widget_want_hotkey (b->widget, 1);
223 b->hotkey = 0;
224 b->hotpos = -1;
226 button_scan_hotkey(b);
227 return b;
230 void
231 button_set_text (WButton *b, char *text)
233 g_free (b->text);
234 b->text = g_strdup (text);
235 b->widget.cols = button_len (text, b->flags);
236 button_scan_hotkey(b);
237 dlg_redraw (b->widget.parent);
241 /* Radio button widget */
242 static int radio_event (Gpm_Event *event, WRadio *r);
244 static int
245 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
247 int i;
249 switch (Msg) {
250 case WIDGET_INIT:
251 return 1;
253 case WIDGET_HOTKEY:
255 int i, lp = tolower(Par);
256 char *cp;
258 for (i = 0; i < r->count; i++){
259 cp = strchr (r->texts [i], '&');
260 if (cp != NULL && cp[1] != '\0'){
261 int c = tolower (cp [1]);
263 if (c != lp)
264 continue;
265 r->pos = i;
266 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
267 return 1;
271 return 0;
273 case WIDGET_KEY:
274 switch (Par){
275 case ' ':
276 r->sel = r->pos;
277 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
278 radio_callback (h, r, WIDGET_FOCUS, ' ');
279 return 1;
281 case KEY_UP:
282 case KEY_LEFT:
283 if (r->pos > 0){
284 r->pos--;
285 return 1;
287 return 0;
289 case KEY_DOWN:
290 case KEY_RIGHT:
291 if (r->count - 1 > r->pos) {
292 r->pos++;
293 return 1;
296 return 0;
298 case WIDGET_CURSOR:
299 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
300 radio_callback (h, r, WIDGET_FOCUS, ' ');
301 widget_move (&r->widget, r->pos, 1);
302 break;
304 case WIDGET_UNFOCUS:
305 case WIDGET_FOCUS:
306 case WIDGET_DRAW:
307 for (i = 0; i < r->count; i++){
308 register unsigned char* cp;
309 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
310 widget_move (&r->widget, i, 0);
312 printw("(%c) ", (r->sel == i) ? '*' : ' ');
313 for (cp = r->texts[i]; *cp; cp++)
315 if (*cp == '&')
317 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
318 ? HOT_FOCUSC : HOT_NORMALC);
319 addch(*++cp);
320 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
322 else
323 addch(*cp);
326 return 1;
327 break;
329 return default_proc (h, Msg, Par);
332 static int
333 radio_event (Gpm_Event *event, WRadio *r)
335 if (event->type & (GPM_DOWN|GPM_UP)){
336 Dlg_head *h = r->widget.parent;
338 r->pos = event->y - 1;
339 dlg_select_widget (h, r);
340 if (event->type & GPM_UP){
341 radio_callback (h, r, WIDGET_KEY, ' ');
342 radio_callback (h, r, WIDGET_FOCUS, 0);
343 (*h->callback) (h, ' ', DLG_POST_KEY);
344 return MOU_NORMAL;
347 return MOU_NORMAL;
350 WRadio *
351 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
353 WRadio *r = g_new (WRadio, 1);
354 int i, max, m;
356 /* Compute the longest string */
357 max = 0;
358 for (i = 0; i < count; i++){
359 m = strlen (texts [i]);
360 if (m > max)
361 max = m;
364 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
365 0, (mouse_h) radio_event, tkname);
366 r->state = 1;
367 r->pos = 0;
368 r->sel = 0;
369 r->count = count;
370 r->texts = texts;
371 r->upper_letter_is_hotkey = use_hotkey;
372 widget_want_hotkey (r->widget, 1);
374 return r;
378 /* Checkbutton widget */
380 static int check_event (Gpm_Event *event, WCheck *b);
382 static int
383 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
385 switch (Msg) {
386 case WIDGET_INIT:
387 return 1;
389 case WIDGET_HOTKEY:
390 if (c->hotkey==Par ||
391 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
392 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
393 return 1;
394 } else
395 return 0;
397 case WIDGET_KEY:
398 if (Par != ' ')
399 break;
400 c->state ^= C_BOOL;
401 c->state ^= C_CHANGE;
402 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
403 check_callback (h, c, WIDGET_FOCUS, ' ');
404 return 1;
406 case WIDGET_CURSOR:
407 widget_move (&c->widget, 0, 1);
408 break;
410 case WIDGET_FOCUS:
411 case WIDGET_UNFOCUS:
412 case WIDGET_DRAW:
413 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
414 widget_move (&c->widget, 0, 0);
415 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
417 if (c->hotpos >= 0){
418 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
419 widget_move (&c->widget, 0, + c->hotpos+4);
420 addch ((unsigned char)c->text [c->hotpos]);
422 return 1;
424 return default_proc (h, Msg, Par);
427 static int
428 check_event (Gpm_Event *event, WCheck *c)
430 if (event->type & (GPM_DOWN|GPM_UP)){
431 Dlg_head *h = c->widget.parent;
433 dlg_select_widget (h, c);
434 if (event->type & GPM_UP){
435 check_callback (h, c, WIDGET_KEY, ' ');
436 check_callback (h, c, WIDGET_FOCUS, 0);
437 (*h->callback) (h, ' ', DLG_POST_KEY);
438 return MOU_NORMAL;
441 return MOU_NORMAL;
444 static void
445 check_destroy (WCheck *c)
447 g_free (c->text);
450 WCheck *
451 check_new (int y, int x, int state, char *text, char *tkname)
453 WCheck *c = g_new (WCheck, 1);
454 char *s, *t;
456 init_widget (&c->widget, y, x, 1, strlen (text),
457 (callback_fn)check_callback,
458 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
459 c->state = state ? C_BOOL : 0;
460 c->text = g_strdup (text);
461 c->hotkey = 0;
462 c->hotpos = -1;
463 widget_want_hotkey (c->widget, 1);
465 /* Scan for the hotkey */
466 for (s = text, t = c->text; *s; s++, t++){
467 if (*s != '&'){
468 *t = *s;
469 continue;
471 s++;
472 if (*s){
473 c->hotkey = tolower (*s);
474 c->hotpos = t - c->text;
476 *t = *s;
478 *t = 0;
479 return c;
483 /* Label widget */
485 static int
486 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
488 if (Msg == WIDGET_INIT)
489 return 1;
491 /* We don't want to get the focus */
492 if (Msg == WIDGET_FOCUS)
493 return 0;
494 if (Msg == WIDGET_DRAW && l->text){
495 char *p = l->text, *q, c = 0;
496 int y = 0;
497 if (l->transparent)
498 attrset (DEFAULT_COLOR);
499 else
500 attrset (NORMALC);
501 for (;;){
502 int xlen;
504 q = strchr (p, '\n');
505 if (q){
506 c = *q;
507 *q = 0;
509 widget_move (&l->widget, y, 0);
510 printw ("%s", p);
511 xlen = l->widget.cols - strlen (p);
512 if (xlen > 0)
513 printw ("%*s", xlen, " ");
514 if (!q)
515 break;
516 *q = c;
517 p = q + 1;
518 y++;
520 return 1;
522 return default_proc (h, Msg, Par);
525 void
526 label_set_text (WLabel *label, char *text)
528 int newcols = label->widget.cols;
530 if (label->text && text && !strcmp (label->text, text))
531 return; /* Flickering is not nice */
533 if (label->text){
534 g_free (label->text);
536 if (text){
537 label->text = g_strdup (text);
538 if (label->auto_adjust_cols) {
539 newcols = strlen (text);
540 if (newcols > label->widget.cols)
541 label->widget.cols = newcols;
543 } else
544 label->text = 0;
546 if (label->widget.parent)
547 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
549 if (newcols < label->widget.cols)
550 label->widget.cols = newcols;
553 static void
554 label_destroy (WLabel *l)
556 if (l->text)
557 g_free (l->text);
560 WLabel *
561 label_new (int y, int x, const char *text, char *tkname)
563 WLabel *l;
564 int width;
566 /* Multiline labels are immutable - no need to compute their sizes */
567 if (!text || strchr(text, '\n'))
568 width = 1;
569 else
570 width = strlen (text);
572 l = g_new (WLabel, 1);
573 init_widget (&l->widget, y, x, 1, width,
574 (callback_fn) label_callback,
575 (destroy_fn) label_destroy, NULL, tkname);
576 l->text = text ? g_strdup (text) : 0;
577 l->auto_adjust_cols = 1;
578 l->transparent = 0;
579 widget_want_cursor (l->widget, 0);
580 return l;
584 /* Gauge widget (progress indicator) */
585 /* Currently width is hardcoded here for text mode */
586 #define gauge_len 47
588 static int
589 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
592 if (Msg == WIDGET_INIT)
593 return 1;
595 /* We don't want to get the focus */
596 if (Msg == WIDGET_FOCUS)
597 return 0;
599 if (Msg == WIDGET_DRAW){
600 widget_move (&g->widget, 0, 0);
601 attrset (NORMALC);
602 if (!g->shown)
603 printw ("%*s", gauge_len, "");
604 else {
605 long percentage, columns;
606 long total = g->max, done = g->current;
608 if (total <= 0 || done < 0) {
609 done = 0;
610 total = 100;
612 if (done > total)
613 done = total;
614 while (total > 65535) {
615 total /= 256;
616 done /= 256;
618 percentage = (200 * done / total + 1) / 2;
619 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
620 addch ('[');
621 attrset (GAUGE_COLOR);
622 printw ("%*s", columns, "");
623 attrset (NORMALC);
624 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
626 return 1;
628 return default_proc (h, Msg, Par);
631 void
632 gauge_set_value (WGauge *g, int max, int current)
634 if (g->current == current && g->max == max)
635 return; /* Do not flicker */
636 if (max == 0)
637 max = 1; /* I do not like division by zero :) */
639 g->current = current;
640 g->max = max;
641 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
644 void
645 gauge_show (WGauge *g, int shown)
647 if (g->shown == shown)
648 return;
649 g->shown = shown;
650 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
653 static void
654 gauge_destroy (WGauge *g)
656 /* nothing */
659 WGauge *
660 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
662 WGauge *g = g_new (WGauge, 1);
664 init_widget (&g->widget, y, x, 1, gauge_len,
665 (callback_fn) gauge_callback,
666 (destroy_fn) gauge_destroy, NULL, tkname);
667 g->shown = shown;
668 if (max == 0)
669 max = 1; /* I do not like division by zero :) */
670 g->max = max;
671 g->current = current;
672 g->pixels = 0;
673 widget_want_cursor (g->widget, 0);
674 return g;
678 /* Input widget */
680 /* {{{ history button */
682 #define LARGE_HISTORY_BUTTON 1
684 #ifdef LARGE_HISTORY_BUTTON
685 # define HISTORY_BUTTON_WIDTH 3
686 #else
687 # define HISTORY_BUTTON_WIDTH 1
688 #endif
690 #define should_show_history_button(in) \
691 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
693 static void draw_history_button (WInput * in)
695 char c;
696 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
697 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
698 #ifdef LARGE_HISTORY_BUTTON
700 Dlg_head *h;
701 h = in->widget.parent;
702 #if 0
703 attrset (NORMALC); /* button has the same colour as other buttons */
704 addstr ("[ ]");
705 attrset (HOT_NORMALC);
706 #else
707 attrset (NORMAL_COLOR);
708 addstr ("[ ]");
709 /* Too distracting: attrset (MARKED_COLOR); */
710 #endif
711 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
712 addch (c);
714 #else
715 attrset (MARKED_COLOR);
716 addch (c);
717 #endif
720 /* }}} history button */
723 /* Input widgets now have a global kill ring */
724 /* Pointer to killed data */
725 static char *kill_buffer = 0;
727 void
728 update_input (WInput *in, int clear_first)
730 int has_history = 0;
731 int i, j;
732 unsigned char c;
733 int buf_len = strlen (in->buffer);
735 if (should_show_history_button (in))
736 has_history = HISTORY_BUTTON_WIDTH;
738 if (in->disable_update)
739 return;
741 /* Make the point visible */
742 if ((in->point < in->first_shown) ||
743 (in->point >= in->first_shown+in->field_len - has_history)){
744 in->first_shown = in->point - (in->field_len / 3);
745 if (in->first_shown < 0)
746 in->first_shown = 0;
749 /* Adjust the mark */
750 if (in->mark > buf_len)
751 in->mark = buf_len;
753 if (has_history)
754 draw_history_button (in);
756 attrset (in->color);
758 widget_move (&in->widget, 0, 0);
759 for (i = 0; i < in->field_len - has_history; i++)
760 addch (' ');
761 widget_move (&in->widget, 0, 0);
763 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
764 c = in->buffer [j++];
765 c = is_printable (c) ? c : '.';
766 if (in->is_password)
767 c = '*';
768 addch (c);
770 widget_move (&in->widget, 0, in->point - in->first_shown);
772 if (clear_first)
773 in->first = 0;
776 void
777 winput_set_origin (WInput *in, int x, int field_len)
779 in->widget.x = x;
780 in->field_len = in->widget.cols = field_len;
781 update_input (in, 0);
784 /* {{{ history saving and loading */
787 This loads and saves the history of an input line to and from the
788 widget. It is called with the widgets tk name on creation of the
789 widget, and returns the Hist list. It stores histories in the file
790 ~/.mc/history in using the profile code.
792 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
793 function) then input_new assigns the default text to be the last text
794 entered, or "" if not found.
797 int num_history_items_recorded = 60;
799 Hist *
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 int
902 history_callback (Dlg_head * h, int Par, int Msg)
904 switch (Msg) {
905 case DLG_DRAW:
906 attrset (COLOR_NORMAL);
907 dlg_erase (h);
908 draw_box (h, 0, 0, h->lines, h->cols);
909 attrset (COLOR_HOT_NORMAL);
910 dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
911 printw (i18n_htitle());
912 break;
914 return 0;
917 static inline int listbox_fwd (WListbox *l);
919 char *
920 show_hist (Hist *history, int widget_x, int widget_y)
922 Hist *hi, *z;
923 size_t maxlen = strlen (i18n_htitle()), i, count = 0;
924 int x, y, w, h;
925 char *q, *r = 0;
926 Dlg_head *query_dlg;
927 WListbox *query_list;
929 z = history;
930 if (!z)
931 return 0;
933 while (z->prev) /* goto first */
934 z = z->prev;
935 hi = z;
936 while (hi) {
937 if ((i = strlen (hi->text)) > maxlen)
938 maxlen = i;
939 count++;
940 hi = hi->next;
943 y = widget_y;
944 h = count + 2;
945 if (h <= y || y > LINES - 6)
947 h = min(h, y - 1);
948 y -= h;
950 else
952 y++;
953 h = min(h, LINES - y);
956 if (widget_x > 2)
957 x = widget_x - 2;
958 else
959 x = 0;
960 if ((w = maxlen + 4) + x > COLS)
962 w = min(w,COLS);
963 x = COLS - w;
966 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
967 "[History-query]", "history", DLG_NONE);
968 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
969 add_widget (query_dlg, query_list);
970 hi = z;
971 if (y < widget_y) {
972 while (hi) { /* traverse */
973 listbox_add_item (query_list, 0, 0, hi->text, NULL);
974 hi = hi->next;
976 while (listbox_fwd (query_list));
977 } else {
978 while (hi->next)
979 hi = hi->next;
980 while (hi) { /* traverse backwards */
981 listbox_add_item (query_list, 0, 0, hi->text, NULL);
982 hi = hi->prev;
985 run_dlg (query_dlg);
986 q = NULL;
987 if (query_dlg->ret_value != B_CANCEL) {
988 listbox_get_current (query_list, &q, NULL);
989 if (q)
990 r = g_strdup (q);
992 destroy_dlg (query_dlg);
993 return r;
996 static void do_show_hist (WInput * in)
998 char *r;
999 r = show_hist (in->history, in->widget.x, in->widget.y);
1000 if (r) {
1001 assign_text (in, r);
1002 g_free (r);
1006 /* }}} history display */
1008 static void
1009 input_destroy (WInput *in)
1011 if (!in){
1012 fprintf (stderr, "Internal error: null Input *\n");
1013 exit (1);
1016 new_input (in);
1017 if (in->history){
1018 Hist *current, *old;
1020 if (!in->is_password) /* don't save passwords ;-) */
1021 history_put (in->history_name, in->history);
1023 current = in->history;
1024 while (current->next)
1025 current = current->next;
1026 while (current){
1027 old = current;
1028 current = current->prev;
1029 g_free (old->text);
1030 g_free (old);
1033 g_free (in->buffer);
1034 free_completions (in);
1035 if (in->history_name)
1036 g_free (in->history_name);
1039 static char disable_update = 0;
1041 void
1042 input_disable_update (WInput *in)
1044 in->disable_update++;
1047 void
1048 input_enable_update (WInput *in)
1050 in->disable_update--;
1051 update_input (in, 0);
1054 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1057 push_history (WInput *in, char *text)
1059 static int i18n;
1060 /* input widget where urls with passwords are entered without any
1061 vfs prefix */
1062 static const char *password_input_fields[] = {
1063 N_(" Link to a remote machine "),
1064 N_(" FTP to machine "),
1065 N_(" SMB link to machine ")
1067 Hist *new;
1068 char *p;
1069 int i;
1071 if (!i18n) {
1072 i18n = 1;
1073 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1074 password_input_fields[i] = _(password_input_fields[i]);
1077 for (p = text; *p == ' ' || *p == '\t'; p++);
1078 if (!*p)
1079 return 0;
1080 if (in->history){
1081 while (in->history->next)
1082 in->history = in->history->next;
1083 if (!strcmp (in->history->text, text))
1084 return 1;
1085 new = g_new (Hist, 1);
1086 in->history->next = new;
1087 } else
1088 new = g_new (Hist, 1);
1089 in->need_push = 0;
1090 new->next = 0;
1091 new->prev = in->history;
1092 new->text = g_strdup (text);
1093 if (in->history_name) {
1094 p = in->history_name + 3;
1095 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1096 if (strcmp (p, password_input_fields[i]) == 0)
1097 break;
1098 if (i < ELEMENTS(password_input_fields))
1099 strip_password (new->text, 0);
1100 else
1101 strip_password (new->text, 1);
1104 in->history = new;
1105 return 2;
1108 #undef ELEMENTS
1110 /* Cleans the input line and adds the current text to the history */
1111 void
1112 new_input (WInput *in)
1114 if (in->buffer)
1115 push_history (in, in->buffer);
1116 in->need_push = 1;
1117 in->buffer [0] = 0;
1118 in->point = 0;
1119 in->mark = 0;
1120 free_completions (in);
1121 update_input (in, 0);
1124 static int
1125 insert_char (WInput *in, int c_code)
1127 int i;
1129 if (c_code == -1)
1130 return 0;
1132 in->need_push = 1;
1133 if (strlen (in->buffer)+1 == in->current_max_len){
1134 /* Expand the buffer */
1135 char *narea = g_malloc (in->current_max_len + in->field_len);
1136 if (narea){
1137 char *p = in->buffer;
1139 strcpy (narea, in->buffer);
1140 in->buffer = narea;
1141 in->current_max_len += in->field_len;
1142 g_free (p);
1145 if (strlen (in->buffer)+1 < in->current_max_len){
1146 int l = strlen (&in->buffer [in->point]);
1147 for (i = l+1; i > 0; i--)
1148 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1149 in->buffer [in->point] = c_code;
1150 in->point++;
1152 return 1;
1155 static void
1156 beginning_of_line (WInput *in)
1158 in->point = 0;
1161 static void
1162 end_of_line (WInput *in)
1164 in->point = strlen (in->buffer);
1167 static void
1168 backward_char (WInput *in)
1170 if (in->point)
1171 in->point--;
1174 static void
1175 forward_char (WInput *in)
1177 if (in->buffer [in->point])
1178 in->point++;
1181 static void
1182 forward_word (WInput *in)
1184 unsigned char *p = in->buffer+in->point;
1186 while (*p && (isspace (*p) || ispunct (*p)))
1187 p++;
1188 while (*p && isalnum (*p))
1189 p++;
1190 in->point = p - in->buffer;
1193 static void
1194 backward_word (WInput *in)
1196 unsigned char *p = in->buffer+in->point;
1198 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1199 p--;
1200 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1201 p--;
1202 in->point = p - in->buffer;
1205 static void
1206 key_left (WInput *in)
1208 if (ctrl_pressed ())
1209 backward_word (in);
1210 else
1211 backward_char (in);
1214 static void
1215 key_right (WInput *in)
1217 if (ctrl_pressed ())
1218 forward_word (in);
1219 else
1220 forward_char (in);
1223 static void
1224 backward_delete (WInput *in)
1226 int i;
1228 if (!in->point)
1229 return;
1230 for (i = in->point; in->buffer [i-1]; i++)
1231 in->buffer [i-1] = in->buffer [i];
1232 in->need_push = 1;
1233 in->point--;
1236 static void
1237 delete_char (WInput *in)
1239 int i;
1241 for (i = in->point; in->buffer [i]; i++)
1242 in->buffer [i] = in->buffer [i+1];
1243 in->need_push = 1;
1246 static void
1247 copy_region (WInput *in, int x_first, int x_last)
1249 int first = min (x_first, x_last);
1250 int last = max (x_first, x_last);
1252 if (last == first)
1253 return;
1255 if (kill_buffer)
1256 g_free (kill_buffer);
1258 kill_buffer = g_malloc (last-first + 1);
1259 strncpy (kill_buffer, in->buffer+first, last-first);
1260 kill_buffer [last-first] = 0;
1263 static void
1264 delete_region (WInput *in, int x_first, int x_last)
1266 int first = min (x_first, x_last);
1267 int last = max (x_first, x_last);
1269 in->point = first;
1270 in->mark = first;
1271 strcpy (&in->buffer [first], &in->buffer [last]);
1272 in->need_push = 1;
1275 static void
1276 kill_word (WInput *in)
1278 int old_point = in->point;
1279 int new_point;
1281 forward_word (in);
1282 new_point = in->point;
1283 in->point = old_point;
1285 copy_region (in, old_point, new_point);
1286 delete_region (in, old_point, new_point);
1287 in->need_push = 1;
1290 static void
1291 back_kill_word (WInput *in)
1293 int old_point = in->point;
1294 int new_point;
1296 backward_word (in);
1297 new_point = in->point;
1298 in->point = old_point;
1300 copy_region (in, old_point, new_point);
1301 delete_region (in, old_point, new_point);
1302 in->need_push = 1;
1305 static void
1306 set_mark (WInput *in)
1308 in->mark = in->point;
1311 static void
1312 kill_save (WInput *in)
1314 copy_region (in, in->mark, in->point);
1317 static void
1318 kill_region (WInput *in)
1320 kill_save (in);
1321 delete_region (in, in->point, in->mark);
1324 static void
1325 yank (WInput *in)
1327 char *p;
1329 if (!kill_buffer)
1330 return;
1331 for (p = kill_buffer; *p; p++)
1332 insert_char (in, *p);
1335 static void
1336 kill_line (WInput *in)
1338 if (kill_buffer)
1339 g_free (kill_buffer);
1340 kill_buffer = g_strdup (&in->buffer [in->point]);
1341 in->buffer [in->point] = 0;
1344 void
1345 assign_text (WInput *in, char *text)
1347 free_completions (in);
1348 g_free (in->buffer);
1349 in->buffer = g_strdup (text); /* was in->buffer->text */
1350 in->current_max_len = strlen (in->buffer) + 1;
1351 in->point = strlen (in->buffer);
1352 in->mark = 0;
1353 in->need_push = 1;
1356 static void
1357 hist_prev (WInput *in)
1359 if (!in->history)
1360 return;
1362 if (in->need_push) {
1363 switch (push_history (in, in->buffer)) {
1364 case 2: in->history = in->history->prev; break;
1365 case 1: if (in->history->prev) in->history = in->history->prev; break;
1366 case 0: break;
1368 } else if (in->history->prev)
1369 in->history = in->history->prev;
1370 else
1371 return;
1372 assign_text (in, in->history->text);
1373 in->need_push = 0;
1376 static void
1377 hist_next (WInput *in)
1379 if (in->need_push) {
1380 switch (push_history (in, in->buffer)) {
1381 case 2:
1382 assign_text (in, "");
1383 return;
1384 case 0:
1385 return;
1389 if (!in->history)
1390 return;
1392 if (!in->history->next) {
1393 assign_text (in, "");
1394 return;
1397 in->history = in->history->next;
1398 assign_text (in, in->history->text);
1399 in->need_push = 0;
1402 static const struct {
1403 int key_code;
1404 void (*fn)(WInput *in);
1405 } input_map [] = {
1406 /* Motion */
1407 { XCTRL('a'), beginning_of_line },
1408 { KEY_HOME, beginning_of_line },
1409 { KEY_A1, beginning_of_line },
1410 { XCTRL('e'), end_of_line },
1411 { KEY_END, end_of_line },
1412 { KEY_C1, end_of_line },
1413 { KEY_LEFT, key_left },
1414 { XCTRL('b'), backward_char },
1415 { ALT('b'), backward_word },
1416 { KEY_RIGHT, key_right },
1417 { XCTRL('f'), forward_char },
1418 { ALT('f'), forward_word },
1420 /* Editing */
1421 { 0177, backward_delete },
1422 { KEY_BACKSPACE, backward_delete },
1423 { XCTRL('h'), backward_delete },
1424 { KEY_DC, delete_char },
1425 { XCTRL('d'), delete_char },
1426 { ALT('d'), kill_word },
1427 { ALT(KEY_BACKSPACE), back_kill_word },
1428 { ALT(XCTRL('h')), back_kill_word },
1429 { ALT(127), back_kill_word },
1431 /* Region manipulation */
1432 { 0, set_mark },
1433 { XCTRL('w'), kill_region },
1434 { ALT('w'), kill_save },
1435 { XCTRL('y'), yank },
1436 { XCTRL('k'), kill_line },
1438 /* History */
1439 { ALT('p'), hist_prev },
1440 { ALT('n'), hist_next },
1441 { ALT('h'), do_show_hist },
1443 /* Completion */
1444 { ALT('\t'), complete },
1446 { 0, 0 }
1449 /* This function is a test for a special input key used in complete.c */
1450 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1451 and 2 if it is a complete key */
1453 is_in_input_map (WInput *in, int c_code)
1455 int i;
1457 for (i = 0; input_map [i].fn; i++)
1458 if (c_code == input_map [i].key_code) {
1459 if (input_map [i].fn == complete)
1460 return 2;
1461 else
1462 return 1;
1464 return 0;
1467 static void
1468 port_region_marked_for_delete (WInput *in)
1470 *in->buffer = 0;
1471 in->point = 0;
1472 in->first = 0;
1476 handle_char (WInput *in, int c_code)
1478 int i;
1479 int v;
1481 v = 0;
1483 if (quote){
1484 free_completions (in);
1485 v = insert_char (in, c_code);
1486 update_input (in, 1);
1487 quote = 0;
1488 return v;
1491 for (i = 0; input_map [i].fn; i++){
1492 if (c_code == input_map [i].key_code){
1493 if (input_map [i].fn != complete)
1494 free_completions (in);
1495 (*input_map [i].fn)(in);
1496 v = 1;
1497 break;
1500 if (!input_map [i].fn){
1501 if (c_code > 255 || !is_printable (c_code))
1502 return 0;
1503 if (in->first){
1504 port_region_marked_for_delete (in);
1506 free_completions (in);
1507 v = insert_char (in, c_code);
1508 in->inserted_one = c_code;
1510 if (!disable_update)
1511 update_input (in, 1);
1512 return v;
1515 /* Inserts text in input line */
1516 void
1517 stuff (WInput *in, char *text, int insert_extra_space)
1519 input_disable_update (in);
1520 while (*text)
1521 handle_char (in, *text++);
1522 if (insert_extra_space)
1523 handle_char (in, ' ');
1524 input_enable_update (in);
1525 update_input (in, 1);
1528 void
1529 input_set_point (WInput *in, int pos)
1531 if (pos > in->current_max_len)
1532 pos = in->current_max_len;
1533 if (pos != in->point)
1534 free_completions (in);
1535 in->point = pos;
1536 update_input (in, 1);
1539 static int
1540 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1542 switch (Msg){
1543 case WIDGET_INIT:
1544 return 1;
1546 case WIDGET_KEY:
1547 if (Par == XCTRL('q')){
1548 int v;
1550 quote = 1;
1551 v = handle_char (in, mi_getch ());
1552 quote = 0;
1553 return v;
1555 if (Par == KEY_UP || Par == KEY_DOWN ||
1556 Par == ESC_CHAR || Par == KEY_F(10) ||
1557 Par == XCTRL('g'))
1558 return 0; /* We don't handle up/down */
1560 if (Par == '\n'){
1561 dlg_one_down (h);
1562 return 1;
1564 return handle_char (in, Par);
1566 case WIDGET_FOCUS:
1567 case WIDGET_UNFOCUS:
1568 case WIDGET_DRAW:
1569 update_input (in, 0);
1570 break;
1571 case WIDGET_CURSOR:
1572 widget_move (&in->widget, 0, in->point - in->first_shown);
1573 return 1;
1576 return default_proc (h, Msg, Par);
1579 static int
1580 input_event (Gpm_Event * event, WInput * in)
1582 if (event->type & (GPM_DOWN | GPM_DRAG)) {
1583 dlg_select_widget (in->widget.parent, in);
1585 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1
1586 && should_show_history_button (in)) {
1587 do_show_hist (in);
1588 } else {
1589 in->point = strlen (in->buffer);
1590 if (event->x - in->first_shown - 1 < in->point)
1591 in->point = event->x - in->first_shown - 1;
1592 if (in->point < 0)
1593 in->point = 0;
1595 update_input (in, 1);
1597 return MOU_NORMAL;
1600 WInput *
1601 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1603 WInput *in = g_new (WInput, 1);
1604 int initial_buffer_len;
1606 init_widget (&in->widget, y, x, 1, len,
1607 (callback_fn) input_callback,
1608 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1610 /* history setup */
1611 in->history = NULL;
1612 in->history_name = 0;
1613 if (tkname) {
1614 if (*tkname) {
1615 in->history_name = g_strdup (tkname);
1616 in->history = history_get (tkname);
1620 if (!def_text)
1621 def_text = "";
1623 if (def_text == INPUT_LAST_TEXT) {
1624 def_text = "";
1625 if (in->history)
1626 if (in->history->text)
1627 def_text = in->history->text;
1629 initial_buffer_len = 1 + max (len, strlen (def_text));
1630 in->widget.options |= W_IS_INPUT;
1631 in->completions = NULL;
1632 in->completion_flags =
1633 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1634 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1635 in->current_max_len = initial_buffer_len;
1636 in->buffer = g_malloc (initial_buffer_len);
1637 in->color = color;
1638 in->field_len = len;
1639 in->first = 1;
1640 in->first_shown = 0;
1641 in->disable_update = 0;
1642 in->mark = 0;
1643 in->need_push = 1;
1644 in->is_password = 0;
1646 strcpy (in->buffer, def_text);
1647 in->point = strlen (in->buffer);
1648 return in;
1652 /* Listbox widget */
1654 /* Should draw the scrollbar, but currently draws only
1655 * indications that there is more information
1657 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1659 static void
1660 listbox_drawscroll (WListbox *l)
1662 extern int slow_terminal;
1663 int line;
1664 int i, top;
1665 int max_line = l->height-1;
1667 /* Are we at the top? */
1668 widget_move (&l->widget, 0, l->width);
1669 if (l->list == l->top)
1670 one_vline ();
1671 else
1672 addch ('^');
1674 /* Are we at the bottom? */
1675 widget_move (&l->widget, max_line, l->width);
1676 top = listbox_cdiff (l->list, l->top);
1677 if ((top + l->height == l->count) || l->height >= l->count)
1678 one_vline ();
1679 else
1680 addch ('v');
1682 /* Now draw the nice relative pointer */
1683 if (l->count)
1684 line = 1+ ((l->pos * (l->height-2)) / l->count);
1685 else
1686 line = 0;
1688 for (i = 1; i < max_line; i++){
1689 widget_move (&l->widget, i, l->width);
1690 if (i != line)
1691 one_vline ();
1692 else
1693 addch ('*');
1697 static void
1698 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1700 WLEntry *e;
1701 int i;
1702 int sel_line;
1703 int normalc = NORMALC;
1704 int selc;
1705 char *text;
1707 if (focused){
1708 selc = FOCUSC;
1709 } else {
1710 selc = HOT_FOCUSC;
1712 sel_line = -1;
1714 for (e = l->top, i = 0; (i < l->height); i++){
1716 /* Display the entry */
1717 if (e == l->current && sel_line == -1){
1718 sel_line = i;
1719 attrset (selc);
1720 } else
1721 attrset (normalc);
1723 widget_move (&l->widget, i, 0);
1725 if ((i > 0 && e == l->list) || !l->list)
1726 text = "";
1727 else {
1728 text = e->text;
1729 e = e->next;
1731 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1733 l->cursor_y = sel_line;
1734 if (!l->scrollbar)
1735 return;
1736 attrset (normalc);
1737 listbox_drawscroll (l);
1740 /* Returns the number of items between s and e,
1741 must be on the same linked list */
1742 static int
1743 listbox_cdiff (WLEntry *s, WLEntry *e)
1745 int count;
1747 for (count = 0; s != e; count++)
1748 s = s->next;
1749 return count;
1752 static WLEntry *
1753 listbox_check_hotkey (WListbox *l, int key)
1755 int i;
1756 WLEntry *e;
1758 i = 0;
1759 e = l->list;
1760 if (!e)
1761 return 0;
1763 while (1){
1765 /* If we didn't find anything, return */
1766 if (i && e == l->list)
1767 return 0;
1769 if (e->hotkey == key)
1770 return e;
1772 i++;
1773 e = e->next;
1777 /* Used only for display updating, for avoiding line at a time scroll */
1778 void
1779 listbox_select_last (WListbox *l, int set_top)
1781 if (l->list){
1782 l->current = l->list->prev;
1783 l->pos = l->count - 1;
1784 if (set_top)
1785 l->top = l->list->prev;
1789 void
1790 listbox_remove_list (WListbox *l)
1792 WLEntry *p, *q;
1794 if (!l->count)
1795 return;
1797 p = l->list;
1799 while (l->count--) {
1800 q = p->next;
1801 g_free (p->text);
1802 g_free (p);
1803 p = q;
1805 l->pos = l->count = 0;
1806 l->list = l->top = l->current = 0;
1810 * bor 30.10.96: added force flag to remove *last* entry as well
1811 * bor 30.10.96: corrected selection bug if last entry was removed
1814 void
1815 listbox_remove_current (WListbox *l, int force)
1817 WLEntry *p;
1819 /* Ok, note: this won't allow for emtpy lists */
1820 if (!force && (!l->count || l->count == 1))
1821 return;
1823 l->count--;
1824 p = l->current;
1826 if (l->count) {
1827 l->current->next->prev = l->current->prev;
1828 l->current->prev->next = l->current->next;
1829 if (p->next == l->list) {
1830 l->current = p->prev;
1831 l->pos--;
1833 else
1834 l->current = p->next;
1836 if (p == l->list)
1837 l->list = l->top = p->next;
1838 } else {
1839 l->pos = 0;
1840 l->list = l->top = l->current = 0;
1843 g_free (p->text);
1844 g_free (p);
1847 /* Makes *e the selected entry (sets current and pos) */
1848 void
1849 listbox_select_entry (WListbox *l, WLEntry *dest)
1851 WLEntry *e;
1852 int pos;
1853 int top_seen;
1855 top_seen = 0;
1857 /* Special case */
1858 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1860 if (e == l->top)
1861 top_seen = 1;
1863 if (e == dest){
1864 l->current = e;
1865 if (top_seen){
1866 while (listbox_cdiff (l->top, l->current) >= l->height)
1867 l->top = l->top->next;
1868 } else {
1869 l->top = l->current;
1871 l->pos = pos;
1872 return;
1875 /* If we are unable to find it, set decent values */
1876 l->current = l->top = l->list;
1877 l->pos = 0;
1880 /* Selects from base the pos element */
1881 static WLEntry *
1882 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1884 WLEntry *last = l->list->prev;
1886 if (base == last)
1887 return last;
1888 while (pos--){
1889 base = base->next;
1890 if (base == last)
1891 break;
1893 return base;
1896 static inline int
1897 listbox_back (WListbox *l)
1899 if (l->pos){
1900 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1901 return 1;
1903 return 0;
1906 static inline int
1907 listbox_fwd (WListbox *l)
1909 if (l->current != l->list->prev){
1910 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1911 return 1;
1913 return 0;
1916 /* Returns 1 if we want a redraw */
1917 static int
1918 listbox_key (WListbox *l, int key)
1920 int i;
1921 int j = 0;
1923 if (!l->list)
1924 return 0;
1926 switch (key){
1927 case KEY_HOME:
1928 case KEY_A1:
1929 l->current = l->top = l->list;
1930 l->pos = 0;
1931 return 1;
1933 case KEY_END:
1934 case KEY_C1:
1935 l->current = l->top = l->list->prev;
1936 for (i = min (l->height - 1, l->count - 1); i; i--)
1937 l->top = l->top->prev;
1938 l->pos = l->count - 1;
1939 return 1;
1941 case XCTRL('p'):
1942 case KEY_UP:
1943 listbox_back (l);
1944 return 1;
1946 case XCTRL('n'):
1947 case KEY_DOWN:
1948 listbox_fwd (l);
1949 return 1;
1951 case KEY_NPAGE:
1952 case XCTRL('v'):
1953 for (i = 0; i < l->height-1; i++)
1954 j |= listbox_fwd (l);
1955 return j > 0;
1957 case KEY_PPAGE:
1958 case ALT('v'):
1959 for (i = 0; i < l->height-1; i++)
1960 j |= listbox_back (l);
1961 return j > 0;
1963 return 0;
1966 static int listbox_event (Gpm_Event *event, WListbox *l);
1967 static int
1968 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
1970 WLEntry *e;
1971 /* int selected_color; Never used */
1972 int ret_code;
1974 switch (msg){
1975 case WIDGET_INIT:
1976 return 1;
1978 case WIDGET_HOTKEY:
1979 if ((e = listbox_check_hotkey (l, par)) != NULL){
1980 listbox_select_entry (l, e);
1982 /* Take the appropriate action */
1983 if (l->action == listbox_finish){
1984 l->widget.parent->running = 0;
1985 l->widget.parent->ret_value = B_ENTER;
1986 } else if (l->action == listbox_cback){
1987 if ((*l->cback)(l) == listbox_finish){
1988 l->widget.parent->running = 0;
1989 l->widget.parent->ret_value = B_ENTER;
1992 return 1;
1993 } else
1994 return 0;
1996 case WIDGET_KEY:
1997 if ((ret_code = listbox_key (l, par)))
1998 listbox_draw (l, h, 1);
1999 return ret_code;
2001 case WIDGET_CURSOR:
2002 widget_move (&l->widget, l->cursor_y, 0);
2003 return 1;
2005 case WIDGET_FOCUS:
2006 case WIDGET_UNFOCUS:
2007 case WIDGET_DRAW:
2008 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2009 return 1;
2011 return default_proc (h, msg, par);
2014 static int
2015 listbox_event (Gpm_Event *event, WListbox *l)
2017 int i;
2019 Dlg_head *h = l->widget.parent;
2021 /* Single click */
2022 if (event->type & GPM_DOWN)
2023 dlg_select_widget (l->widget.parent, l);
2024 if (!l->list)
2025 return MOU_NORMAL;
2026 if (event->type & (GPM_DOWN|GPM_DRAG)){
2027 if (event->x < 0 || event->x >= l->width)
2028 return MOU_REPEAT;
2029 if (event->y < 1)
2030 for (i = -event->y; i >= 0; i--)
2031 listbox_back (l);
2032 else if (event->y > l->height)
2033 for (i = event->y - l->height; i > 0; i--)
2034 listbox_fwd (l);
2035 else
2036 listbox_select_entry (l, listbox_select_pos (l, l->top,
2037 event->y - 1));
2039 /* We need to refresh ourselves since the dialog manager doesn't */
2040 /* know about this event */
2041 listbox_callback (h, l, WIDGET_DRAW, 0);
2042 mc_refresh ();
2043 return MOU_REPEAT;
2046 /* Double click */
2047 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2048 if (event->x < 0 || event->x >= l->width)
2049 return MOU_NORMAL;
2050 if (event->y < 1 || event->y > l->height)
2051 return MOU_NORMAL;
2053 dlg_select_widget (l->widget.parent, l);
2054 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2056 switch (l->action){
2057 case listbox_nothing:
2058 break;
2060 case listbox_finish:
2061 h->ret_value = B_ENTER;
2062 dlg_stop (h);
2063 return MOU_ENDLOOP;
2065 case listbox_cback:
2066 if ((*l->cback)(l) == listbox_finish)
2067 return MOU_ENDLOOP;
2070 return MOU_NORMAL;
2073 static void
2074 listbox_destroy (WListbox *l)
2076 WLEntry *n, *p = l->list;
2077 int i;
2079 for (i = 0; i < l->count; i++){
2080 n = p->next;
2081 g_free (p->text);
2082 g_free (p);
2083 p = n;
2087 WListbox *
2088 listbox_new (int y, int x, int width, int height,
2089 int action, lcback callback, char *tkname)
2091 WListbox *l = g_new (WListbox, 1);
2092 extern int slow_terminal;
2094 init_widget (&l->widget, y, x, height, width,
2095 (callback_fn)listbox_callback,
2096 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2098 l->list = l->top = l->current = 0;
2099 l->pos = 0;
2100 l->width = width;
2101 if (height <= 0)
2102 l->height = 1;
2103 else
2104 l->height = height;
2105 l->count = 0;
2106 l->top = 0;
2107 l->current= 0;
2108 l->cback = callback;
2109 l->action = action;
2110 l->allow_duplicates = 1;
2111 l->scrollbar = slow_terminal ? 0 : 1;
2112 widget_want_hotkey (l->widget, 1);
2114 return l;
2117 /* Listbox item adding function. They still lack a lot of functionality */
2118 /* any takers? */
2119 /* 1.11.96 bor: added pos argument to control placement of new entry */
2120 static void
2121 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2123 if (!l->list){
2124 l->list = e;
2125 l->top = e;
2126 l->current = e;
2127 e->next = l->list;
2128 e->prev = l->list;
2129 } else if (pos == LISTBOX_APPEND_AT_END) {
2130 e->next = l->list;
2131 e->prev = l->list->prev;
2132 l->list->prev->next = e;
2133 l->list->prev = e;
2134 } else if (pos == LISTBOX_APPEND_BEFORE){
2135 e->next = l->current;
2136 e->prev = l->current->prev;
2137 l->current->prev->next = e;
2138 l->current->prev = e;
2139 if (l->list == l->current) { /* move list one position down */
2140 l->list = e;
2141 l->top = e;
2143 } else if (pos == LISTBOX_APPEND_AFTER) {
2144 e->prev = l->current;
2145 e->next = l->current->next;
2146 l->current->next->prev = e;
2147 l->current->next = e;
2149 l->count++;
2152 char *
2153 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2154 void *data)
2156 WLEntry *entry;
2158 if (!l)
2159 return 0;
2161 if (!l->allow_duplicates)
2162 if (listbox_search_text (l, text))
2163 return 0;
2165 entry = g_new (WLEntry, 1);
2166 entry->text = g_strdup (text);
2167 entry->data = data;
2168 entry->hotkey = hotkey;
2170 listbox_append_item (l, entry, pos);
2172 return entry->text;
2175 /* Selects the nth entry in the listbox */
2176 void
2177 listbox_select_by_number (WListbox *l, int n)
2179 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2182 WLEntry *
2183 listbox_search_text (WListbox *l, char *text)
2185 WLEntry *e;
2187 e = l->list;
2188 if (!e)
2189 return NULL;
2191 do {
2192 if(!strcmp (e->text, text))
2193 return e;
2194 e = e->next;
2195 } while (e!=l->list);
2197 return NULL;
2200 /* Returns the current string text as well as the associated extra data */
2201 void
2202 listbox_get_current (WListbox *l, char **string, char **extra)
2204 if (!l->current){
2205 *string = 0;
2206 *extra = 0;
2208 if (string && l->current)
2209 *string = l->current->text;
2210 if (extra && l->current)
2211 *extra = l->current->data;
2214 static int
2215 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2217 int i;
2219 switch (msg){
2220 case WIDGET_INIT:
2221 return 1;
2223 case WIDGET_FOCUS:
2224 return 0;
2226 case WIDGET_HOTKEY:
2227 for (i = 0; i < 10; i++){
2228 if (par == KEY_F(i+1) && bb->labels [i].function){
2229 (*bb->labels [i].function)(bb->labels [i].data);
2230 return 1;
2233 return 0;
2235 case WIDGET_DRAW:
2236 if (!bb->visible)
2237 return 1;
2238 widget_move (&bb->widget, 0, 0);
2239 attrset (DEFAULT_COLOR);
2240 printw ("%-*s", bb->widget.cols, "");
2241 for (i = 0; i < COLS/8 && i < 10; i++){
2242 widget_move (&bb->widget, 0, i*8);
2243 attrset (DEFAULT_COLOR);
2244 printw ("%d", i+1);
2245 attrset (SELECTED_COLOR);
2246 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2247 bb->labels [i].text ? bb->labels [i].text : "");
2248 attrset (DEFAULT_COLOR);
2250 attrset (SELECTED_COLOR);
2251 return 1;
2253 return default_proc (h, msg, par);
2256 static void
2257 buttonbar_destroy (WButtonBar *bb)
2259 int i;
2261 for (i = 0; i < 10; i++){
2262 if (bb->labels [i].text)
2263 g_free (bb->labels [i].text);
2267 static int
2268 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2270 int button;
2272 if (!(event->type & GPM_UP))
2273 return MOU_NORMAL;
2274 if (event->y == 2)
2275 return MOU_NORMAL;
2276 button = event->x / 8;
2277 if (button < 10 && bb->labels [button].function)
2278 (*bb->labels [button].function)(bb->labels [button].data);
2279 return MOU_NORMAL;
2282 WButtonBar *
2283 buttonbar_new (int visible)
2285 int i;
2286 WButtonBar *bb = g_new (WButtonBar, 1);
2288 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2289 (callback_fn) buttonbar_callback,
2290 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2292 bb->visible = visible;
2293 for (i = 0; i < 10; i++){
2294 bb->labels [i].text = 0;
2295 bb->labels [i].function = 0;
2297 widget_want_hotkey (bb->widget, 1);
2298 widget_want_cursor (bb->widget, 0);
2300 return bb;
2303 static void
2304 set_label_text (WButtonBar * bb, int index, char *text)
2306 if (bb->labels[index - 1].text)
2307 g_free (bb->labels[index - 1].text);
2309 bb->labels[index - 1].text = g_strdup (text);
2312 /* paneletc is either the panel widget, or info or view or tree widget */
2313 static WButtonBar *
2314 find_buttonbar (Dlg_head * h, Widget * paneletc)
2316 WButtonBar *bb;
2317 Widget_Item *item;
2318 int i;
2320 bb = 0;
2321 for (i = 0, item = h->current; i < h->count; i++, item = item->next) {
2322 if (item->widget->callback == (callback_fn) buttonbar_callback) {
2323 bb = (WButtonBar *) item->widget;
2324 break;
2327 return bb;
2330 void
2331 define_label_data (Dlg_head * h, Widget * paneletc, int idx, char *text,
2332 buttonbarfn cback, void *data)
2334 WButtonBar *bb = find_buttonbar (h, paneletc);
2335 if (!bb)
2336 return;
2338 set_label_text (bb, idx, text);
2339 bb->labels[idx - 1].function = cback;
2340 bb->labels[idx - 1].data = data;
2343 void
2344 define_label (Dlg_head * h, Widget * paneletc, int idx, char *text,
2345 void (*cback) (void))
2347 define_label_data (h, paneletc, idx, text, (buttonbarfn) cback, 0);
2350 void
2351 redraw_labels (Dlg_head *h, Widget *paneletc)
2353 Widget_Item *item;
2354 int i;
2356 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2357 if (item->widget->callback == (callback_fn) buttonbar_callback){
2358 widget_redraw (h, item);
2359 return;