1999-09-09 Federico Mena Quintero <federico@redhat.com>
[midnight-commander.git] / src / widget.c
bloba7020dffa3f8f3feccd0eca84a9a3935857a01a9
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., 675 Mass Ave, Cambridge, MA 02139, USA.
26 /* "$Id$" */
28 #include <config.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include "tty.h"
36 #include <ctype.h>
37 #include "global.h"
38 #include "color.h"
39 #include "mouse.h"
40 #include "dlg.h"
41 #include "widget.h"
42 #include "win.h"
43 #include "complete.h"
44 #include "key.h" /* XCTRL and ALT macros */
45 #include "x.h"
46 #include "profile.h" /* for history loading and saving */
48 #ifndef HAVE_X
49 # define x_create_button(a,b,c) 1
50 # define x_create_radio(a,b,c) 1
51 # define x_create_check(a,b,c) 1
52 # define x_create_label(a,b,c) 1
53 # define x_create_input(a,b,c) 1
54 # define x_create_listbox(a,b,c) 1
55 # define x_create_buttonbar(a,b,c) 1
56 # define x_create_gauge(a,b,c) 1
57 # define x_listbox_select_nth(a,b)
58 # define x_list_insert(a,b,c)
59 # define x_redefine_label(a,b)
60 #endif
62 #ifndef PORT_HAS_DESTROY_CMD
63 # define x_destroy_cmd(w)
64 #endif
66 #ifndef PORT_HAS_RADIO_FOCUS_ITEM
67 # define x_radio_focus_item(r)
68 #endif
70 #ifndef PORT_HAS_RADIO_TOGGLE
71 # define x_radio_toggle
72 #endif
74 static int button_event (Gpm_Event *event, WButton *b);
76 int quote = 0;
78 static int
79 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
81 #ifndef HAVE_X
82 char *txt, buf[BUF_SMALL];
83 #endif
84 int stop = 0;
85 int off = 0;
87 switch (Msg){
88 case WIDGET_INIT:
89 return x_create_button (h, h->wdata, b);
91 case WIDGET_HOTKEY:
92 if (b->hotkey == Par || toupper(b->hotkey) == Par){
93 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
94 return 1;
95 } else
96 return 0;
98 case WIDGET_KEY:
99 if (Par != ' ' && Par != '\n')
100 break;
102 if (b->callback)
103 stop = (*b->callback)(b->action, b->callback_data);
104 if (!b->callback || stop){
105 h->ret_value = b->action;
106 dlg_stop (h);
108 return 1;
110 case WIDGET_CURSOR:
111 switch (b->flags) {
112 case DEFPUSH_BUTTON:
113 off = 3;
114 break;
115 case NORMAL_BUTTON:
116 off = 2;
117 break;
118 case NARROW_BUTTON:
119 off = 1;
120 break;
121 case HIDDEN_BUTTON:
122 default:
123 off = 0;
124 break;
126 widget_move (&b->widget, 0, b->hotpos + off);
127 return 1;
129 case WIDGET_UNFOCUS:
130 case WIDGET_FOCUS:
131 case WIDGET_DRAW:
132 #ifndef HAVE_X
133 if (Msg==WIDGET_UNFOCUS)
134 b->selected = 0;
135 else if (Msg==WIDGET_FOCUS)
136 b->selected = 1;
138 switch (b->flags){
139 case DEFPUSH_BUTTON:
140 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
141 off = 3;
142 break;
143 case NORMAL_BUTTON:
144 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
145 off = 2;
146 break;
147 case NARROW_BUTTON:
148 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
149 off = 1;
150 break;
151 case HIDDEN_BUTTON:
152 default:
153 buf[0] = '\0';
154 off = 0;
155 break;
157 txt = buf;
159 attrset ((b->selected) ? FOCUSC : NORMALC);
160 widget_move (&b->widget, 0, 0);
162 addstr (txt);
164 if (b->hotpos >= 0){
165 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
166 widget_move (&b->widget, 0, b->hotpos+off);
167 addch ((unsigned char)b->text [b->hotpos]);
169 #endif /* HAVE_X */
170 if (Msg == WIDGET_FOCUS)
171 break;
172 else
173 return 1;
174 break;
176 return default_proc (h, Msg, Par);
179 static int
180 button_event (Gpm_Event *event, WButton *b)
182 #ifndef HAVE_X
183 if (event->type & (GPM_DOWN|GPM_UP)){
184 Dlg_head *h=b->widget.parent;
185 dlg_select_widget (h, b);
186 if (event->type & GPM_UP){
187 button_callback (h, b, WIDGET_KEY, ' ');
188 (*h->callback) (h, ' ', DLG_POST_KEY);
189 return MOU_NORMAL;
192 #endif
193 return MOU_NORMAL;
196 static void
197 button_destroy (WButton *b)
199 x_destroy_cmd (b);
200 g_free (b->text);
203 static int
204 button_len (const char *text, unsigned int flags)
206 #ifndef HAVE_X
207 int ret = strlen (text);
208 switch (flags){
209 case DEFPUSH_BUTTON:
210 ret += 6;
211 break;
212 case NORMAL_BUTTON:
213 ret += 4;
214 break;
215 case NARROW_BUTTON:
216 ret += 2;
217 break;
218 case HIDDEN_BUTTON:
219 default:
220 return 0;
222 return ret;
223 #else
224 return strlen (text);
225 #endif
229 * Assuming that button text is malloc'ed, we may safely change it
230 * (as opposed to statically allocated); from other hand, excluding &
231 * and shifting data past it to the left results to one unused byte.
232 * This does not harm though :)
234 void
235 button_scan_hotkey(WButton* b)
237 char* cp = strchr (b->text, '&');
239 if (cp != NULL && cp[1] != '\0'){
240 strcpy (cp, cp+1);
241 b->hotkey = tolower (*cp);
242 b->hotpos = cp - b->text;
246 WButton *
247 button_new (int y, int x, int action, int flags, char *text,
248 int (*callback)(int, void *), void *callback_data, char *tkname)
250 WButton *b = g_new (WButton, 1);
252 init_widget (&b->widget, y, x, 1, button_len (text, flags),
253 (callback_fn) button_callback,
254 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
256 b->action = action;
257 b->flags = flags;
258 b->selected = 0;
259 b->text = g_strdup (text);
260 b->callback = callback;
261 b->callback_data = callback_data;
262 widget_want_hotkey (b->widget, 1);
263 b->hotkey = 0;
264 b->hotpos = -1;
266 button_scan_hotkey(b);
267 return b;
270 void
271 button_set_text (WButton *b, char *text)
273 g_free (b->text);
274 b->text = g_strdup (text);
275 b->widget.cols = button_len (text, b->flags);
276 button_scan_hotkey(b);
277 #ifdef HAVE_X
278 x_button_set (b, b->text);
279 #else
280 dlg_redraw (b->widget.parent);
281 #endif
285 /* Radio button widget */
286 static int radio_event (Gpm_Event *event, WRadio *r);
288 static int
289 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
291 #ifndef HAVE_X
292 int i;
293 #endif
295 switch (Msg) {
296 case WIDGET_INIT:
297 return x_create_radio (h, h->wdata, r);
299 case WIDGET_HOTKEY:
301 int i, lp = tolower(Par);
302 char *cp;
304 for (i = 0; i < r->count; i++){
305 cp = strchr (r->texts [i], '&');
306 if (cp != NULL && cp[1] != '\0'){
307 int c = tolower (cp [1]);
309 if (c != lp)
310 continue;
311 r->pos = i;
312 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
313 return 1;
317 return 0;
319 case WIDGET_KEY:
320 switch (Par){
321 case ' ':
322 r->sel = r->pos;
323 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
324 radio_callback (h, r, WIDGET_FOCUS, ' ');
325 x_radio_toggle (r);
326 return 1;
328 case KEY_UP:
329 case KEY_LEFT:
330 if (r->pos > 0){
331 r->pos--;
332 x_radio_focus_item (r);
333 return 1;
335 return 0;
337 case KEY_DOWN:
338 case KEY_RIGHT:
339 if (r->count - 1 > r->pos) {
340 r->pos++;
341 x_radio_focus_item (r);
342 return 1;
345 return 0;
347 #ifdef HAVE_X
348 case WIDGET_FOCUS:
349 case WIDGET_CURSOR:
350 x_radio_focus_item (r);
351 return 1;
352 #endif
354 #ifndef HAVE_X
355 case WIDGET_CURSOR:
356 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
357 radio_callback (h, r, WIDGET_FOCUS, ' ');
358 widget_move (&r->widget, r->pos, 1);
359 break;
361 case WIDGET_UNFOCUS:
362 case WIDGET_FOCUS:
363 case WIDGET_DRAW:
364 for (i = 0; i < r->count; i++){
365 register unsigned char* cp;
366 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
367 widget_move (&r->widget, i, 0);
369 printw("(%c) ", (r->sel == i) ? '*' : ' ');
370 for (cp = r->texts[i]; *cp; cp++)
372 if (*cp == '&')
374 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
375 ? HOT_FOCUSC : HOT_NORMALC);
376 addch(*++cp);
377 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
379 else
380 addch(*cp);
383 return 1;
384 break;
385 #endif
387 return default_proc (h, Msg, Par);
390 #ifdef HAVE_TK
391 static void Radio_destroy (WRadio *r)
393 x_destroy_cmd (r);
395 # define radio_destroy (destroy_fn) Radio_destroy
396 #else
397 # define radio_destroy 0
398 #endif
400 static int
401 radio_event (Gpm_Event *event, WRadio *r)
403 #ifndef HAVE_X
404 if (event->type & (GPM_DOWN|GPM_UP)){
405 Dlg_head *h = r->widget.parent;
407 r->pos = event->y - 1;
408 dlg_select_widget (h, r);
409 if (event->type & GPM_UP){
410 radio_callback (h, r, WIDGET_KEY, ' ');
411 radio_callback (h, r, WIDGET_FOCUS, 0);
412 (*h->callback) (h, ' ', DLG_POST_KEY);
413 return MOU_NORMAL;
416 #endif
417 return MOU_NORMAL;
420 WRadio *
421 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
423 WRadio *r = g_new (WRadio, 1);
424 int i, max, m;
426 /* Compute the longest string */
427 max = 0;
428 for (i = 0; i < count; i++){
429 m = strlen (texts [i]);
430 if (m > max)
431 max = m;
434 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
435 radio_destroy, (mouse_h) radio_event, tkname);
436 r->state = 1;
437 r->pos = 0;
438 r->sel = 0;
439 r->count = count;
440 r->texts = texts;
441 r->upper_letter_is_hotkey = use_hotkey;
442 widget_want_hotkey (r->widget, 1);
444 return r;
448 /* Checkbutton widget */
450 static int check_event (Gpm_Event *event, WCheck *b);
452 static int
453 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
455 switch (Msg) {
456 case WIDGET_INIT:
457 return x_create_check (h, h->wdata, c);
459 case WIDGET_HOTKEY:
460 if (c->hotkey==Par ||
461 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
462 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
463 return 1;
464 } else
465 return 0;
467 case WIDGET_KEY:
468 if (Par != ' ')
469 break;
470 c->state ^= C_BOOL;
471 c->state ^= C_CHANGE;
472 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
473 check_callback (h, c, WIDGET_FOCUS, ' ');
474 return 1;
476 #ifndef HAVE_X
477 case WIDGET_CURSOR:
478 widget_move (&c->widget, 0, 1);
479 break;
481 case WIDGET_FOCUS:
482 case WIDGET_UNFOCUS:
483 case WIDGET_DRAW:
484 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
485 widget_move (&c->widget, 0, 0);
486 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
488 if (c->hotpos >= 0){
489 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
490 widget_move (&c->widget, 0, + c->hotpos+4);
491 addch ((unsigned char)c->text [c->hotpos]);
493 return 1;
494 #endif /* !HAVE_X */
496 return default_proc (h, Msg, Par);
499 static int
500 check_event (Gpm_Event *event, WCheck *c)
502 #ifndef HAVE_X
503 if (event->type & (GPM_DOWN|GPM_UP)){
504 Dlg_head *h = c->widget.parent;
506 dlg_select_widget (h, c);
507 if (event->type & GPM_UP){
508 check_callback (h, c, WIDGET_KEY, ' ');
509 check_callback (h, c, WIDGET_FOCUS, 0);
510 (*h->callback) (h, ' ', DLG_POST_KEY);
511 return MOU_NORMAL;
514 #endif
515 return MOU_NORMAL;
518 static void
519 check_destroy (WCheck *c)
521 x_destroy_cmd (c);
522 g_free (c->text);
525 WCheck *
526 check_new (int y, int x, int state, char *text, char *tkname)
528 WCheck *c = g_new (WCheck, 1);
529 char *s, *t;
531 init_widget (&c->widget, y, x, 1, strlen (text),
532 (callback_fn)check_callback,
533 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
534 c->state = state ? C_BOOL : 0;
535 c->text = g_strdup (text);
536 c->hotkey = 0;
537 c->hotpos = -1;
538 widget_want_hotkey (c->widget, 1);
540 /* Scan for the hotkey */
541 for (s = text, t = c->text; *s; s++, t++){
542 if (*s != '&'){
543 *t = *s;
544 continue;
546 s++;
547 if (*s){
548 c->hotkey = tolower (*s);
549 c->hotpos = t - c->text;
551 *t = *s;
553 *t = 0;
554 return c;
558 /* Label widget */
560 static int
561 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
563 if (Msg == WIDGET_INIT)
564 return x_create_label (h, h->wdata, l);
566 /* We don't want to get the focus */
567 if (Msg == WIDGET_FOCUS)
568 return 0;
569 #ifndef HAVE_X
570 if (Msg == WIDGET_DRAW && l->text){
571 char *p = l->text, *q, c = 0;
572 int y = 0;
573 if (l->transparent)
574 attrset (DEFAULT_COLOR);
575 else
576 attrset (NORMALC);
577 for (;;){
578 int xlen;
580 q = strchr (p, '\n');
581 if (q){
582 c = *q;
583 *q = 0;
585 widget_move (&l->widget, y, 0);
586 printw ("%s", p);
587 xlen = l->widget.cols - strlen (p);
588 if (xlen > 0)
589 printw ("%*s", xlen, " ");
590 if (!q)
591 break;
592 *q = c;
593 p = q + 1;
594 y++;
596 return 1;
598 #endif
599 return default_proc (h, Msg, Par);
602 void
603 label_set_text (WLabel *label, char *text)
605 int newcols = label->widget.cols;
607 if (label->text && text && !strcmp (label->text, text))
608 return; /* Flickering is not nice */
610 if (label->text){
611 g_free (label->text);
613 if (text){
614 label->text = g_strdup (text);
615 if (label->auto_adjust_cols) {
616 newcols = strlen (text);
617 if (newcols > label->widget.cols)
618 label->widget.cols = newcols;
620 } else
621 label->text = 0;
623 if (label->widget.parent)
624 #ifdef HAVE_X
625 x_label_set_text (label, text);
626 #else
627 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
628 #endif
629 if (newcols < label->widget.cols)
630 label->widget.cols = newcols;
633 static void
634 label_destroy (WLabel *l)
636 x_destroy_cmd (l);
637 if (l->text)
638 g_free (l->text);
641 WLabel *
642 label_new (int y, int x, char *text, char *tkname)
644 WLabel *l = g_new (WLabel, 1);
646 init_widget (&l->widget, y, x, 1, 1,
647 (callback_fn) label_callback,
648 (destroy_fn) label_destroy, NULL, tkname);
649 l->text = text ? g_strdup (text) : 0;
650 l->auto_adjust_cols = 1;
651 l->transparent = 0;
652 widget_want_cursor (l->widget, 0);
653 return l;
657 /* Gauge widget (progress indicator) */
658 /* Currently width is hardcoded here for text mode */
659 #define gauge_len 47
661 static int
662 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
665 if (Msg == WIDGET_INIT)
666 return x_create_gauge (h, h->wdata, g);
668 /* We don't want to get the focus */
669 if (Msg == WIDGET_FOCUS)
670 return 0;
672 #ifndef HAVE_X
673 if (Msg == WIDGET_DRAW){
674 widget_move (&g->widget, 0, 0);
675 attrset (NORMALC);
676 if (!g->shown)
677 printw ("%*s", gauge_len, "");
678 else {
679 long percentage, columns;
680 long total = g->max, done = g->current;
682 if (total <= 0 || done < 0) {
683 done = 0;
684 total = 100;
686 if (done > total)
687 done = total;
688 while (total > 65535) {
689 total /= 256;
690 done /= 256;
692 percentage = (200 * done / total + 1) / 2;
693 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
694 addch ('[');
695 attrset (GAUGE_COLOR);
696 printw ("%*s", columns, "");
697 attrset (NORMALC);
698 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
700 return 1;
702 #endif
703 return default_proc (h, Msg, Par);
706 void
707 gauge_set_value (WGauge *g, int max, int current)
709 if (g->current == current && g->max == max)
710 return; /* Do not flicker */
711 if (max == 0)
712 max = 1; /* I do not like division by zero :) */
713 #ifdef HAVE_X
714 /* NOTE: x_gauge_set_value has to be called before we change actual
715 * max and current values in g, since it assumes g->max and
716 * g->current as the previous values and max and current
717 * as the new ones :) */
718 x_gauge_set_value (g, max, current);
719 #endif
720 g->current = current;
721 g->max = max;
722 #ifndef HAVE_X
723 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
724 #endif
727 void
728 gauge_show (WGauge *g, int shown)
730 if (g->shown == shown)
731 return;
732 g->shown = shown;
733 #ifdef HAVE_X
734 x_gauge_show (g);
735 #else
736 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
737 #endif
740 static void
741 gauge_destroy (WGauge *g)
743 /* nothing */
746 WGauge *
747 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
749 WGauge *g = g_new (WGauge, 1);
751 init_widget (&g->widget, y, x, 1, gauge_len,
752 (callback_fn) gauge_callback,
753 (destroy_fn) gauge_destroy, NULL, tkname);
754 g->shown = shown;
755 if (max == 0)
756 max = 1; /* I do not like division by zero :) */
757 g->max = max;
758 g->current = current;
759 g->pixels = 0;
760 widget_want_cursor (g->widget, 0);
761 return g;
765 /* Input widget */
767 /* {{{ history button */
769 #define LARGE_HISTORY_BUTTON 1
771 #ifdef LARGE_HISTORY_BUTTON
772 # define HISTORY_BUTTON_WIDTH 3
773 #else
774 # define HISTORY_BUTTON_WIDTH 1
775 #endif
777 #define should_show_history_button(in) \
778 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
780 #ifndef HAVE_X
781 static void draw_history_button (WInput * in)
783 char c;
784 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
785 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
786 #ifdef LARGE_HISTORY_BUTTON
788 Dlg_head *h;
789 h = in->widget.parent;
790 #if 0
791 attrset (NORMALC); /* button has the same colour as other buttons */
792 addstr ("[ ]");
793 attrset (HOT_NORMALC);
794 #else
795 attrset (NORMAL_COLOR);
796 addstr ("[ ]");
797 /* Too distracting: attrset (MARKED_COLOR); */
798 #endif
799 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
800 addch (c);
802 #else
803 attrset (MARKED_COLOR);
804 addch (c);
805 #endif
807 #endif
809 /* }}} history button */
812 /* Input widgets now have a global kill ring */
813 /* Pointer to killed data */
814 static char *kill_buffer = 0;
816 void
817 update_input (WInput *in, int clear_first)
819 int has_history = 0;
820 #ifndef HAVE_X
821 int i, j;
822 unsigned char c;
823 #endif
824 int buf_len = strlen (in->buffer);
826 if (should_show_history_button (in))
827 has_history = HISTORY_BUTTON_WIDTH;
829 if (in->disable_update)
830 return;
832 /* Make the point visible */
833 if ((in->point < in->first_shown) ||
834 (in->point >= in->first_shown+in->field_len - has_history)){
835 in->first_shown = in->point - (in->field_len / 3);
836 if (in->first_shown < 0)
837 in->first_shown = 0;
840 /* Adjust the mark */
841 if (in->mark > buf_len)
842 in->mark = buf_len;
844 #ifdef HAVE_X
845 if (clear_first && in->first)
846 in->first = -1;
847 x_update_input (in);
848 #else
850 if (has_history)
851 draw_history_button (in);
853 attrset (in->color);
855 widget_move (&in->widget, 0, 0);
856 for (i = 0; i < in->field_len - has_history; i++)
857 addch (' ');
858 widget_move (&in->widget, 0, 0);
860 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
861 c = in->buffer [j++];
862 c = is_printable (c) ? c : '.';
863 if (in->is_password)
864 c = '*';
865 addch (c);
867 widget_move (&in->widget, 0, in->point - in->first_shown);
869 if (clear_first)
870 in->first = 0;
871 #endif
874 void
875 winput_set_origin (WInput *in, int x, int field_len)
877 in->widget.x = x;
878 in->field_len = in->widget.cols = field_len;
879 update_input (in, 0);
882 /* {{{ history saving and loading */
885 This loads and saves the history of an input line to and from the
886 widget. It is called with the widgets tk name on creation of the
887 widget, and returns the Hist list. It stores histories in the file
888 ~/.mc/history in using the profile code.
890 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
891 function) then input_new assigns the default text to be the last text
892 entered, or "" if not found.
895 int num_history_items_recorded = 60;
897 Hist *history_get (char *input_name)
899 int i;
900 Hist *old, *new;
901 char *profile;
903 old = new = NULL;
905 if (!num_history_items_recorded) /* this is how to disable */
906 return 0;
907 if (!input_name)
908 return 0;
909 if (!*input_name)
910 return 0;
911 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
912 for (i = 0;; i++) {
913 char key_name[BUF_TINY];
914 char this_entry[BUF_LARGE];
915 g_snprintf (key_name, sizeof (key_name), "%d", i);
916 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
917 if (!*this_entry)
918 break;
919 new = g_new0 (Hist, 1);
920 new->text = g_strdup (this_entry);
921 new->prev = old; /* set up list pointers */
922 if (old)
923 old->next = new;
924 old = new;
926 g_free (profile);
927 return new; /* return pointer to last entry in list */
930 #ifdef PORT_WIDGET_WANTS_HISTORY
931 void history_put (char *input_name, Hist *h)
933 int i;
934 char *profile;
936 if (!input_name)
937 return;
939 if (!*input_name)
940 return;
942 if (!h)
943 return;
945 if (!num_history_items_recorded) /* this is how to disable */
946 return;
948 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
950 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
951 close (i);
952 /* Just in case I forgot to strip passwords somewhere -- Norbert */
953 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT)
954 return;
956 while (h->next) /* go to end of list */
957 h = h->next;
959 /* go back 60 places */
960 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
961 h = h->prev;
962 i = 0;
964 if (input_name)
965 profile_clean_section (input_name, profile);
967 /* dump histories into profile */
968 while (h){
969 if (h->text){
971 /* probably aren't any null entries, but lets be sure */
972 if (*(h->text)){
973 char key_name[BUF_TINY];
974 g_snprintf (key_name, sizeof(key_name), "%d", i++);
975 WritePrivateProfileString (input_name, key_name, h->text, profile);
978 h = h->next;
980 g_free (profile);
982 #else
983 void history_put (char *input_name, Hist *h)
986 #endif
988 /* }}} history saving and loading */
991 /* {{{ history display */
993 static char *
994 i18n_htitle (void)
996 static char *history_title = NULL;
998 if (history_title == NULL)
999 history_title = _(" History ");
1000 return history_title;
1003 static int
1004 history_callback (Dlg_head * h, int Par, int Msg)
1006 #ifndef HAVE_X
1007 switch (Msg) {
1008 case DLG_DRAW:
1009 attrset (COLOR_NORMAL);
1010 dlg_erase (h);
1011 draw_box (h, 0, 0, h->lines, h->cols);
1012 attrset (COLOR_HOT_NORMAL);
1013 dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
1014 printw (i18n_htitle());
1015 break;
1017 #endif
1018 return 0;
1021 static inline int listbox_fwd (WListbox *l);
1023 char *show_hist (Hist *history, int widget_x, int widget_y)
1025 Hist *hi, *z;
1026 size_t maxlen = strlen (i18n_htitle()), i, count = 0;
1027 int x, y, w, h;
1028 char *q, *r = 0;
1029 Dlg_head *query_dlg;
1030 WListbox *query_list;
1032 z = history;
1033 if (!z)
1034 return 0;
1036 while (z->prev) /* goto first */
1037 z = z->prev;
1038 hi = z;
1039 while (hi) {
1040 if ((i = strlen (hi->text)) > maxlen)
1041 maxlen = i;
1042 count++;
1043 hi = hi->next;
1046 y = widget_y;
1047 h = count + 2;
1048 if (h <= y || y > LINES - 6)
1050 h = min(h, y - 1);
1051 y -= h;
1053 else
1055 y++;
1056 h = min(h, LINES - y);
1059 if (widget_x > 2)
1060 x = widget_x - 2;
1061 else
1062 x = 0;
1063 if ((w = maxlen + 4) + x > COLS)
1065 w = min(w,COLS);
1066 x = COLS - w;
1069 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
1070 "[History-query]", "history", DLG_NONE);
1071 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
1072 add_widget (query_dlg, query_list);
1073 hi = z;
1074 if (y < widget_y) {
1075 while (hi) { /* traverse */
1076 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1077 hi = hi->next;
1079 while (listbox_fwd (query_list));
1080 } else {
1081 while (hi->next)
1082 hi = hi->next;
1083 while (hi) { /* traverse backwards */
1084 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1085 hi = hi->prev;
1088 run_dlg (query_dlg);
1089 q = NULL;
1090 if (query_dlg->ret_value != B_CANCEL) {
1091 listbox_get_current (query_list, &q, NULL);
1092 if (q)
1093 r = g_strdup (q);
1095 destroy_dlg (query_dlg);
1096 return r;
1099 static void do_show_hist (WInput * in)
1101 char *r;
1102 r = show_hist (in->history, in->widget.x, in->widget.y);
1103 if (r) {
1104 assign_text (in, r);
1105 g_free (r);
1109 /* }}} history display */
1111 static void
1112 input_destroy (WInput *in)
1114 if (!in){
1115 fprintf (stderr, "Internal error: null Input *\n");
1116 exit (1);
1119 new_input (in);
1120 if (in->history){
1121 Hist *current, *old;
1123 if (!in->is_password && PORT_WIDGET_WANTS_HISTORY) /* don't save passwords ;-) */
1124 history_put (in->history_name, in->history);
1126 current = in->history;
1127 while (current->next)
1128 current = current->next;
1129 while (current){
1130 old = current;
1131 current = current->prev;
1132 g_free (old->text);
1133 g_free (old);
1136 x_destroy_cmd (in);
1137 g_free (in->buffer);
1138 free_completions (in);
1139 if (in->history_name)
1140 g_free (in->history_name);
1143 static char disable_update = 0;
1145 void
1146 input_disable_update (WInput *in)
1148 in->disable_update++;
1151 void
1152 input_enable_update (WInput *in)
1154 in->disable_update--;
1155 update_input (in, 0);
1158 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1161 push_history (WInput *in, char *text)
1163 static int i18n;
1164 /* input widget where urls with passwords are entered without any
1165 vfs prefix */
1166 static const char *password_input_fields[] = {
1167 " Link to a remote machine ",
1168 " FTP to machine ",
1169 " SMB link to machine "
1171 Hist *new;
1172 char *p;
1173 int i;
1175 if (!i18n) {
1176 i18n = 1;
1177 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1178 password_input_fields[i] = _(password_input_fields[i]);
1181 for (p = text; *p == ' ' || *p == '\t'; p++);
1182 if (!*p)
1183 return 0;
1184 if (in->history){
1185 while (in->history->next)
1186 in->history = in->history->next;
1187 if (!strcmp (in->history->text, text))
1188 return 1;
1189 new = g_new (Hist, 1);
1190 in->history->next = new;
1191 } else
1192 new = g_new (Hist, 1);
1193 in->need_push = 0;
1194 new->next = 0;
1195 new->prev = in->history;
1196 new->text = g_strdup (text);
1197 if (in->history_name) {
1198 p = in->history_name + 3;
1199 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1200 if (strcmp (p, password_input_fields[i]) == 0)
1201 break;
1202 if (i < ELEMENTS(password_input_fields))
1203 strip_password (new->text, 0);
1204 else
1205 strip_password (new->text, 1);
1208 in->history = new;
1209 return 2;
1212 #undef ELEMENTS
1214 /* Cleans the input line and adds the current text to the history */
1215 void
1216 new_input (WInput *in)
1218 if (in->buffer)
1219 push_history (in, in->buffer);
1220 in->need_push = 1;
1221 in->buffer [0] = 0;
1222 in->point = 0;
1223 in->mark = 0;
1224 free_completions (in);
1225 update_input (in, 0);
1228 static int
1229 insert_char (WInput *in, int c_code)
1231 int i;
1233 if (c_code == -1)
1234 return 0;
1236 in->need_push = 1;
1237 if (strlen (in->buffer)+1 == in->current_max_len){
1238 /* Expand the buffer */
1239 char *narea = g_malloc (in->current_max_len + in->field_len);
1240 if (narea){
1241 char *p = in->buffer;
1243 strcpy (narea, in->buffer);
1244 in->buffer = narea;
1245 in->current_max_len += in->field_len;
1246 g_free (p);
1249 if (strlen (in->buffer)+1 < in->current_max_len){
1250 int l = strlen (&in->buffer [in->point]);
1251 for (i = l+1; i > 0; i--)
1252 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1253 in->buffer [in->point] = c_code;
1254 in->point++;
1256 return 1;
1259 static void
1260 beginning_of_line (WInput *in)
1262 in->point = 0;
1265 static void
1266 end_of_line (WInput *in)
1268 in->point = strlen (in->buffer);
1271 static void
1272 backward_char (WInput *in)
1274 if (in->point)
1275 in->point--;
1278 static void
1279 forward_char (WInput *in)
1281 if (in->buffer [in->point])
1282 in->point++;
1285 static void
1286 forward_word (WInput *in)
1288 char *p = in->buffer+in->point;
1290 while ((*p && isspace (*p)) || ispunct (*p))
1291 p++;
1292 while (*p && isalnum (*p))
1293 p++;
1294 in->point = p - in->buffer;
1297 static void
1298 backward_word (WInput *in)
1300 char *p = in->buffer+in->point;
1302 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1303 p--;
1304 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1305 p--;
1306 in->point = p - in->buffer;
1309 #ifdef __linux__
1310 static void
1311 key_left (WInput *in)
1313 if (ctrl_pressed ())
1314 backward_word (in);
1315 else
1316 backward_char (in);
1319 static void
1320 key_right (WInput *in)
1322 if (ctrl_pressed ())
1323 forward_word (in);
1324 else
1325 forward_char (in);
1327 #else
1328 #define key_left backward_char
1329 #define key_right forward_char
1330 #endif
1332 static void
1333 backward_delete (WInput *in)
1335 int i;
1337 if (!in->point)
1338 return;
1339 for (i = in->point; in->buffer [i-1]; i++)
1340 in->buffer [i-1] = in->buffer [i];
1341 in->need_push = 1;
1342 in->point--;
1345 static void
1346 delete_char (WInput *in)
1348 int i;
1350 for (i = in->point; in->buffer [i]; i++)
1351 in->buffer [i] = in->buffer [i+1];
1352 in->need_push = 1;
1355 static void
1356 copy_region (WInput *in, int x_first, int x_last)
1358 int first = min (x_first, x_last);
1359 int last = max (x_first, x_last);
1361 if (last == first)
1362 return;
1364 if (kill_buffer)
1365 g_free (kill_buffer);
1367 kill_buffer = g_malloc (last-first + 1);
1368 strncpy (kill_buffer, in->buffer+first, last-first);
1369 kill_buffer [last-first] = 0;
1372 static void
1373 delete_region (WInput *in, int x_first, int x_last)
1375 int first = min (x_first, x_last);
1376 int last = max (x_first, x_last);
1378 in->point = first;
1379 in->mark = first;
1380 strcpy (&in->buffer [first], &in->buffer [last]);
1381 in->need_push = 1;
1384 static void
1385 kill_word (WInput *in)
1387 int old_point = in->point;
1388 int new_point;
1390 forward_word (in);
1391 new_point = in->point;
1392 in->point = old_point;
1394 copy_region (in, old_point, new_point);
1395 delete_region (in, old_point, new_point);
1396 in->need_push = 1;
1399 static void
1400 back_kill_word (WInput *in)
1402 int old_point = in->point;
1403 int new_point;
1405 backward_word (in);
1406 new_point = in->point;
1407 in->point = old_point;
1409 copy_region (in, old_point, new_point);
1410 delete_region (in, old_point, new_point);
1411 in->need_push = 1;
1414 static void
1415 set_mark (WInput *in)
1417 in->mark = in->point;
1420 static void
1421 kill_save (WInput *in)
1423 copy_region (in, in->mark, in->point);
1426 static void
1427 kill_region (WInput *in)
1429 kill_save (in);
1430 delete_region (in, in->point, in->mark);
1433 static void
1434 yank (WInput *in)
1436 char *p;
1438 if (!kill_buffer)
1439 return;
1440 for (p = kill_buffer; *p; p++)
1441 insert_char (in, *p);
1444 static void
1445 kill_line (WInput *in)
1447 if (kill_buffer)
1448 g_free (kill_buffer);
1449 kill_buffer = g_strdup (&in->buffer [in->point]);
1450 in->buffer [in->point] = 0;
1453 void
1454 assign_text (WInput *in, char *text)
1456 free_completions (in);
1457 g_free (in->buffer);
1458 in->buffer = g_strdup (text); /* was in->buffer->text */
1459 in->current_max_len = strlen (in->buffer) + 1;
1460 in->point = strlen (in->buffer);
1461 in->mark = 0;
1462 in->need_push = 1;
1465 static void
1466 hist_prev (WInput *in)
1468 if (!in->history)
1469 return;
1471 if (in->need_push) {
1472 switch (push_history (in, in->buffer)) {
1473 case 2: in->history = in->history->prev; break;
1474 case 1: if (in->history->prev) in->history = in->history->prev; break;
1475 case 0: break;
1477 } else if (in->history->prev)
1478 in->history = in->history->prev;
1479 else
1480 return;
1481 assign_text (in, in->history->text);
1482 in->need_push = 0;
1485 static void
1486 hist_next (WInput *in)
1488 if (in->need_push) {
1489 switch (push_history (in, in->buffer)) {
1490 case 2:
1491 assign_text (in, "");
1492 return;
1493 case 0:
1494 return;
1498 if (!in->history)
1499 return;
1501 if (!in->history->next) {
1502 assign_text (in, "");
1503 return;
1506 in->history = in->history->next;
1507 assign_text (in, in->history->text);
1508 in->need_push = 0;
1511 static struct {
1512 int key_code;
1513 void (*fn)(WInput *in);
1514 } input_map [] = {
1515 /* Motion */
1516 { XCTRL('a'), beginning_of_line },
1517 { KEY_HOME, beginning_of_line },
1518 { KEY_A1, beginning_of_line },
1519 { XCTRL('e'), end_of_line },
1520 { KEY_END, end_of_line },
1521 { KEY_C1, end_of_line },
1522 { KEY_LEFT, key_left },
1523 { XCTRL('b'), backward_char },
1524 { ALT('b'), backward_word },
1525 { KEY_RIGHT, key_right },
1526 { XCTRL('f'), forward_char },
1527 { ALT('f'), forward_word },
1529 /* Editing */
1530 { 0177, backward_delete },
1531 { KEY_BACKSPACE, backward_delete },
1532 { XCTRL('h'), backward_delete },
1533 { KEY_DC, delete_char },
1534 { XCTRL('d'), delete_char },
1535 { ALT('d'), kill_word },
1536 { ALT(KEY_BACKSPACE), back_kill_word },
1537 { ALT(XCTRL('h')), back_kill_word },
1538 { ALT(127), back_kill_word },
1540 /* Region manipulation */
1541 { 0, set_mark },
1542 { XCTRL('w'), kill_region },
1543 { ALT('w'), kill_save },
1544 { XCTRL('y'), yank },
1545 { XCTRL('k'), kill_line },
1547 /* History */
1548 { ALT('p'), hist_prev },
1549 { ALT('n'), hist_next },
1550 { ALT('h'), do_show_hist },
1552 /* Completion */
1553 { ALT('\t'), complete },
1555 { 0, 0 }
1558 /* This function is a test for a special input key used in complete.c */
1559 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1560 and 2 if it is a complete key */
1562 is_in_input_map (WInput *in, int c_code)
1564 int i;
1566 for (i = 0; input_map [i].fn; i++)
1567 if (c_code == input_map [i].key_code) {
1568 if (input_map [i].fn == complete)
1569 return 2;
1570 else
1571 return 1;
1573 return 0;
1576 #ifdef PORT_WINPUT_DELETES_MARKED
1577 static void
1578 port_region_marked_for_delete (WInput *in)
1580 if (in->first == 1 && (in->point == in->mark))
1581 in->point = strlen (in->buffer);
1582 kill_region (in);
1583 in->first = 0;
1585 #else
1586 static void
1587 port_region_marked_for_delete (WInput *in)
1589 *in->buffer = 0;
1590 in->point = 0;
1591 in->first = 0;
1593 #endif
1596 handle_char (WInput *in, int c_code)
1598 int i;
1599 int v;
1601 v = 0;
1603 #ifdef HAVE_TK
1604 in->inserted_one = 0;
1605 #endif
1606 if (quote){
1607 free_completions (in);
1608 v = insert_char (in, c_code);
1609 update_input (in, 1);
1610 quote = 0;
1611 return v;
1614 for (i = 0; input_map [i].fn; i++){
1615 if (c_code == input_map [i].key_code){
1616 if (input_map [i].fn != complete)
1617 free_completions (in);
1618 (*input_map [i].fn)(in);
1619 v = 1;
1620 break;
1623 if (!input_map [i].fn){
1624 if (c_code > 255 || !is_printable (c_code))
1625 return 0;
1626 if (in->first){
1627 port_region_marked_for_delete (in);
1629 free_completions (in);
1630 v = insert_char (in, c_code);
1631 in->inserted_one = c_code;
1633 if (!disable_update)
1634 update_input (in, 1);
1635 return v;
1638 /* Inserts text in input line */
1639 void
1640 stuff (WInput *in, char *text, int insert_extra_space)
1642 input_disable_update (in);
1643 while (*text)
1644 handle_char (in, *text++);
1645 if (insert_extra_space)
1646 handle_char (in, ' ');
1647 input_enable_update (in);
1648 update_input (in, 1);
1651 void
1652 input_set_point (WInput *in, int pos)
1654 if (pos > in->current_max_len)
1655 pos = in->current_max_len;
1656 if (pos != in->point)
1657 free_completions (in);
1658 in->point = pos;
1659 update_input (in, 1);
1662 int input_event (Gpm_Event *event, WInput *b);
1664 static int
1665 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1667 switch (Msg){
1668 case WIDGET_INIT:
1669 return x_create_input (h, h->wdata, in);
1671 case WIDGET_KEY:
1672 if (Par == XCTRL('q')){
1673 int v;
1675 quote = 1;
1676 v = handle_char (in, mi_getch ());
1677 quote = 0;
1678 return v;
1680 if (Par == KEY_UP || Par == KEY_DOWN ||
1681 Par == ESC_CHAR || Par == KEY_F(10) ||
1682 Par == XCTRL('g'))
1683 return 0; /* We don't handle up/down */
1685 if (Par == '\n'){
1686 dlg_one_down (h);
1687 return 1;
1689 return handle_char (in, Par);
1691 case WIDGET_FOCUS:
1692 case WIDGET_UNFOCUS:
1693 case WIDGET_DRAW:
1694 update_input (in, 0);
1695 break;
1696 #ifndef HAVE_X
1697 case WIDGET_CURSOR:
1698 widget_move (&in->widget, 0, in->point - in->first_shown);
1699 return 1;
1700 #endif
1703 return default_proc (h, Msg, Par);
1706 /* Not declared static, since we check against this value in dlg.c */
1707 /* FIXME: Declare static again and provide an identification mechanism */
1708 int
1709 input_event (Gpm_Event *event, WInput *in)
1711 #ifndef HAVE_X
1712 if (event->type & (GPM_DOWN|GPM_DRAG)){
1713 dlg_select_widget (in->widget.parent, in);
1715 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
1716 do_show_hist (in);
1717 update_input (in, 1);
1718 } else {
1719 in->point = strlen (in->buffer);
1720 if (event->x - in->first_shown - 1 < in->point)
1721 in->point = event->x - in->first_shown - 1;
1722 if (in->point < 0)
1723 in->point = 0;
1725 update_input (in, 1);
1728 #endif
1729 return MOU_NORMAL;
1732 WInput *
1733 input_new (int y, int x, int color, int len, char *def_text, char *tkname)
1735 WInput *in = g_new (WInput, 1);
1736 int initial_buffer_len;
1738 init_widget (&in->widget, y, x, 1, len,
1739 (callback_fn) input_callback,
1740 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1742 /* history setup */
1743 in->history = NULL;
1744 in->history_name = 0;
1745 if (tkname && PORT_WIDGET_WANTS_HISTORY){
1746 if (*tkname) {
1747 in->history_name = g_strdup (tkname);
1748 in->history = history_get (tkname);
1751 if (def_text == INPUT_LAST_TEXT) {
1752 def_text = "";
1753 if (in->history)
1754 if (in->history->text)
1755 def_text = in->history->text;
1757 initial_buffer_len = 1 + max (len, strlen (def_text));
1758 in->widget.options |= W_IS_INPUT;
1759 in->completions = NULL;
1760 in->completion_flags =
1761 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1762 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1763 in->current_max_len = initial_buffer_len;
1764 in->buffer = g_malloc (initial_buffer_len);
1765 in->color = color;
1766 in->field_len = len;
1767 in->first = 1;
1768 in->first_shown = 0;
1769 in->disable_update = 0;
1770 in->mark = 0;
1771 in->need_push = 1;
1772 in->is_password = 0;
1774 strcpy (in->buffer, def_text);
1775 in->point = strlen (in->buffer);
1776 return in;
1780 /* Listbox widget */
1782 /* Should draw the scrollbar, but currently draws only
1783 * indications that there is more information
1785 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1787 #ifdef HAVE_X
1788 static void
1789 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1791 /* nothing */
1793 #else
1794 static void
1795 listbox_drawscroll (WListbox *l)
1797 extern int slow_terminal;
1798 int line;
1799 int i, top;
1800 int max_line = l->height-1;
1802 /* Are we at the top? */
1803 widget_move (&l->widget, 0, l->width);
1804 if (l->list == l->top)
1805 one_vline ();
1806 else
1807 addch ('^');
1809 /* Are we at the bottom? */
1810 widget_move (&l->widget, max_line, l->width);
1811 top = listbox_cdiff (l->list, l->top);
1812 if ((top + l->height == l->count) || l->height >= l->count)
1813 one_vline ();
1814 else
1815 addch ('v');
1817 /* Now draw the nice relative pointer */
1818 if (l->count)
1819 line = 1+ ((l->pos * (l->height-2)) / l->count);
1820 else
1821 line = 0;
1823 for (i = 1; i < max_line; i++){
1824 widget_move (&l->widget, i, l->width);
1825 if (i != line)
1826 one_vline ();
1827 else
1828 addch ('*');
1832 static void
1833 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1835 WLEntry *e;
1836 int i;
1837 int sel_line;
1838 int normalc, selc;
1839 char *text;
1841 if (focused){
1842 normalc = NORMALC;
1843 selc = FOCUSC;
1844 } else {
1845 normalc = NORMALC;
1846 selc = HOT_FOCUSC;
1848 sel_line = -1;
1850 for (e = l->top, i = 0; (i < l->height); i++){
1852 /* Display the entry */
1853 if (e == l->current && sel_line == -1){
1854 sel_line = i;
1855 attrset (selc);
1856 } else
1857 attrset (normalc);
1859 widget_move (&l->widget, i, 0);
1861 if ((i > 0 && e == l->list) || !l->list)
1862 text = "";
1863 else {
1864 text = e->text;
1865 e = e->next;
1867 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1869 l->cursor_y = sel_line;
1870 if (!l->scrollbar)
1871 return;
1872 attrset (normalc);
1873 listbox_drawscroll (l);
1875 #endif /* HAVE_X */
1877 /* Returns the number of items between s and e,
1878 must be on the same linked list */
1879 static int
1880 listbox_cdiff (WLEntry *s, WLEntry *e)
1882 int count;
1884 for (count = 0; s != e; count++)
1885 s = s->next;
1886 return count;
1889 static WLEntry *
1890 listbox_check_hotkey (WListbox *l, int key)
1892 int i;
1893 WLEntry *e;
1895 i = 0;
1896 e = l->list;
1897 if (!e)
1898 return 0;
1900 while (1){
1902 /* If we didn't find anything, return */
1903 if (i && e == l->list)
1904 return 0;
1906 if (e->hotkey == key)
1907 return e;
1909 i++;
1910 e = e->next;
1914 /* Used only for display updating, for avoiding line at a time scroll */
1915 void
1916 listbox_select_last (WListbox *l, int set_top)
1918 if (l->list){
1919 l->current = l->list->prev;
1920 l->pos = l->count - 1;
1921 if (set_top)
1922 l->top = l->list->prev;
1923 x_listbox_select_nth (l, l->pos);
1927 void
1928 listbox_remove_list (WListbox *l)
1930 WLEntry *p, *q;
1932 if (!l->count)
1933 return;
1935 #ifdef HAVE_X
1936 if (l->widget.wdata != (widget_data) NULL) {
1937 int i;
1938 for (i = 0; i < l->count; i++)
1939 x_listbox_delete_nth (l, i);
1941 #endif
1942 p = l->list;
1944 while (l->count--) {
1945 q = p->next;
1946 g_free (p->text);
1947 g_free (p);
1948 p = q;
1950 l->pos = l->count = 0;
1951 l->list = l->top = l->current = 0;
1955 * bor 30.10.96: added force flag to remove *last* entry as well
1956 * bor 30.10.96: corrected selection bug if last entry was removed
1959 void
1960 listbox_remove_current (WListbox *l, int force)
1962 WLEntry *p;
1964 /* Ok, note: this won't allow for emtpy lists */
1965 if (!force && (!l->count || l->count == 1))
1966 return;
1968 #ifdef HAVE_X
1969 if (l->widget.wdata != (widget_data) NULL) {
1970 x_listbox_delete_nth (l, l->pos);
1971 if (l->count > 1) {
1972 if (l->current->next != l->list)
1973 x_listbox_select_nth (l, l->pos);
1974 else if (l->current != l->list)
1975 x_listbox_select_nth (l, l->pos - 1);
1976 } else
1977 x_listbox_select_nth (l, 0);
1979 #endif
1980 l->count--;
1981 p = l->current;
1983 if (l->count) {
1984 l->current->next->prev = l->current->prev;
1985 l->current->prev->next = l->current->next;
1986 if (p->next == l->list) {
1987 l->current = p->prev;
1988 l->pos--;
1990 else
1991 l->current = p->next;
1993 if (p == l->list)
1994 l->list = l->top = p->next;
1995 } else {
1996 l->pos = 0;
1997 l->list = l->top = l->current = 0;
2000 g_free (p->text);
2001 g_free (p);
2004 /* Makes *e the selected entry (sets current and pos) */
2005 void
2006 listbox_select_entry (WListbox *l, WLEntry *dest)
2008 WLEntry *e;
2009 int pos;
2010 int top_seen;
2012 top_seen = 0;
2014 /* Special case */
2015 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
2017 if (e == l->top)
2018 top_seen = 1;
2020 if (e == dest){
2021 l->current = e;
2022 if (top_seen){
2023 while (listbox_cdiff (l->top, l->current) >= l->height)
2024 l->top = l->top->next;
2025 } else {
2026 l->top = l->current;
2028 l->pos = pos;
2029 x_listbox_select_nth (l, l->pos);
2030 return;
2033 /* If we are unable to find it, set decent values */
2034 l->current = l->top = l->list;
2035 l->pos = 0;
2036 x_listbox_select_nth (l, l->pos);
2039 /* Selects from base the pos element */
2040 static WLEntry *
2041 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2043 WLEntry *last = l->list->prev;
2045 if (base == last)
2046 return last;
2047 while (pos--){
2048 base = base->next;
2049 if (base == last)
2050 break;
2052 return base;
2055 static inline int
2056 listbox_back (WListbox *l)
2058 if (l->pos){
2059 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
2060 return 1;
2062 return 0;
2065 static inline int
2066 listbox_fwd (WListbox *l)
2068 if (l->current != l->list->prev){
2069 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
2070 return 1;
2072 return 0;
2075 /* Returns 1 if we want a redraw */
2076 static int
2077 listbox_key (WListbox *l, int key)
2079 int i;
2080 int j = 0;
2082 if (!l->list)
2083 return 0;
2085 switch (key){
2086 case KEY_HOME:
2087 case KEY_A1:
2088 l->current = l->top = l->list;
2089 l->pos = 0;
2090 return 1;
2092 case KEY_END:
2093 case KEY_C1:
2094 l->current = l->top = l->list->prev;
2095 for (i = min (l->height - 1, l->count - 1); i; i--)
2096 l->top = l->top->prev;
2097 l->pos = l->count - 1;
2098 return 1;
2100 case XCTRL('p'):
2101 case KEY_UP:
2102 listbox_back (l);
2103 return 1;
2105 case XCTRL('n'):
2106 case KEY_DOWN:
2107 listbox_fwd (l);
2108 return 1;
2110 case KEY_NPAGE:
2111 case XCTRL('v'):
2112 for (i = 0; i < l->height-1; i++)
2113 j |= listbox_fwd (l);
2114 return j > 0;
2116 case KEY_PPAGE:
2117 case ALT('v'):
2118 for (i = 0; i < l->height-1; i++)
2119 j |= listbox_back (l);
2120 return j > 0;
2122 return 0;
2125 static int listbox_event (Gpm_Event *event, WListbox *l);
2126 static int
2127 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
2129 WLEntry *e;
2130 /* int selected_color; Never used */
2131 int ret_code;
2133 switch (msg){
2134 case WIDGET_INIT:
2135 return x_create_listbox (h, h->wdata, l);
2137 case WIDGET_HOTKEY:
2138 if ((e = listbox_check_hotkey (l, par)) != NULL){
2139 listbox_select_entry (l, e);
2141 /* Take the appropriate action */
2142 if (l->action == listbox_finish){
2143 l->widget.parent->running = 0;
2144 l->widget.parent->ret_value = B_ENTER;
2145 } else if (l->action == listbox_cback){
2146 if ((*l->cback)(l) == listbox_finish){
2147 l->widget.parent->running = 0;
2148 l->widget.parent->ret_value = B_ENTER;
2151 return 1;
2152 } else
2153 return 0;
2155 case WIDGET_KEY:
2156 if ((ret_code = listbox_key (l, par)))
2157 listbox_draw (l, h, 1);
2158 return ret_code;
2160 #ifndef HAVE_X
2161 case WIDGET_CURSOR:
2162 widget_move (&l->widget, l->cursor_y, 0);
2163 return 1;
2165 case WIDGET_FOCUS:
2166 case WIDGET_UNFOCUS:
2167 case WIDGET_DRAW:
2168 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2169 return 1;
2170 #endif
2172 return default_proc (h, msg, par);
2175 static int
2176 listbox_event (Gpm_Event *event, WListbox *l)
2178 #ifndef HAVE_X
2179 int i;
2181 Dlg_head *h = l->widget.parent;
2183 /* Single click */
2184 if (event->type & GPM_DOWN)
2185 dlg_select_widget (l->widget.parent, l);
2186 if (!l->list)
2187 return MOU_NORMAL;
2188 if (event->type & (GPM_DOWN|GPM_DRAG)){
2189 if (event->x < 0 || event->x >= l->width)
2190 return MOU_REPEAT;
2191 if (event->y < 1)
2192 for (i = -event->y; i >= 0; i--)
2193 listbox_back (l);
2194 else if (event->y > l->height)
2195 for (i = event->y - l->height; i > 0; i--)
2196 listbox_fwd (l);
2197 else
2198 listbox_select_entry (l, listbox_select_pos (l, l->top,
2199 event->y - 1));
2201 /* We need to refresh ourselves since the dialog manager doesn't */
2202 /* know about this event */
2203 listbox_callback (h, l, WIDGET_DRAW, 0);
2204 mc_refresh ();
2205 return MOU_REPEAT;
2208 /* Double click */
2209 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2210 if (event->x < 0 || event->x >= l->width)
2211 return MOU_NORMAL;
2212 if (event->y < 1 || event->y > l->height)
2213 return MOU_NORMAL;
2215 dlg_select_widget (l->widget.parent, l);
2216 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2218 switch (l->action){
2219 case listbox_nothing:
2220 break;
2222 case listbox_finish:
2223 h->ret_value = B_ENTER;
2224 dlg_stop (h);
2225 return MOU_ENDLOOP;
2227 case listbox_cback:
2228 if ((*l->cback)(l) == listbox_finish)
2229 return MOU_ENDLOOP;
2232 #endif
2233 return MOU_NORMAL;
2236 static void
2237 listbox_destroy (WListbox *l)
2239 WLEntry *n, *p = l->list;
2240 int i;
2242 x_destroy_cmd (l);
2243 for (i = 0; i < l->count; i++){
2244 n = p->next;
2245 g_free (p->text);
2246 g_free (p);
2247 p = n;
2251 WListbox *
2252 listbox_new (int y, int x, int width, int height,
2253 int action, lcback callback, char *tkname)
2255 WListbox *l = g_new (WListbox, 1);
2256 extern int slow_terminal;
2258 init_widget (&l->widget, y, x, height, width,
2259 (callback_fn)listbox_callback,
2260 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2262 l->list = l->top = l->current = 0;
2263 l->pos = 0;
2264 l->width = width;
2265 if (height <= 0)
2266 l->height = 1;
2267 else
2268 l->height = height;
2269 l->count = 0;
2270 l->top = 0;
2271 l->current= 0;
2272 l->cback = callback;
2273 l->action = action;
2274 l->allow_duplicates = 1;
2275 l->scrollbar = slow_terminal ? 0 : 1;
2276 widget_want_hotkey (l->widget, 1);
2278 return l;
2281 /* Listbox item adding function. They still lack a lot of functionality */
2282 /* any takers? */
2283 /* 1.11.96 bor: added pos argument to control placement of new entry */
2284 static void
2285 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2287 if (!l->list){
2288 l->list = e;
2289 l->top = e;
2290 l->current = e;
2291 e->next = l->list;
2292 e->prev = l->list;
2293 } else if (pos == LISTBOX_APPEND_AT_END) {
2294 e->next = l->list;
2295 e->prev = l->list->prev;
2296 l->list->prev->next = e;
2297 l->list->prev = e;
2298 } else if (pos == LISTBOX_APPEND_BEFORE){
2299 e->next = l->current;
2300 e->prev = l->current->prev;
2301 l->current->prev->next = e;
2302 l->current->prev = e;
2303 if (l->list == l->current) { /* move list one position down */
2304 l->list = e;
2305 l->top = e;
2307 } else if (pos == LISTBOX_APPEND_AFTER) {
2308 e->prev = l->current;
2309 e->next = l->current->next;
2310 l->current->next->prev = e;
2311 l->current->next = e;
2313 x_list_insert (l, l->list, e);
2314 l->count++;
2317 char *
2318 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2319 void *data)
2321 WLEntry *entry;
2323 if (!l)
2324 return 0;
2326 if (!l->allow_duplicates)
2327 if (listbox_search_text (l, text))
2328 return 0;
2330 entry = g_new (WLEntry, 1);
2331 entry->text = g_strdup (text);
2332 entry->data = data;
2333 entry->hotkey = hotkey;
2335 listbox_append_item (l, entry, pos);
2337 return entry->text;
2340 /* Selects the nth entry in the listbox */
2341 void
2342 listbox_select_by_number (WListbox *l, int n)
2344 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2347 WLEntry *
2348 listbox_search_text (WListbox *l, char *text)
2350 WLEntry *e;
2352 e = l->list;
2353 if (!e)
2354 return NULL;
2356 do {
2357 if(!strcmp (e->text, text))
2358 return e;
2359 e = e->next;
2360 } while (e!=l->list);
2362 return NULL;
2365 /* Returns the current string text as well as the associated extra data */
2366 void
2367 listbox_get_current (WListbox *l, char **string, char **extra)
2369 if (!l->current){
2370 *string = 0;
2371 *extra = 0;
2373 if (string && l->current)
2374 *string = l->current->text;
2375 if (extra && l->current)
2376 *extra = l->current->data;
2379 static int
2380 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2382 int i;
2384 switch (msg){
2385 case WIDGET_INIT:
2386 return x_create_buttonbar (h, h->wdata, bb);
2388 case WIDGET_FOCUS:
2389 return 0;
2391 case WIDGET_HOTKEY:
2392 for (i = 0; i < 10; i++){
2393 if (par == KEY_F(i+1) && bb->labels [i].function){
2394 (*bb->labels [i].function)(bb->labels [i].data);
2395 return 1;
2398 return 0;
2400 #ifndef HAVE_X
2401 case WIDGET_DRAW:
2402 if (!bb->visible)
2403 return 1;
2404 widget_move (&bb->widget, 0, 0);
2405 attrset (DEFAULT_COLOR);
2406 printw ("%-*s", bb->widget.cols - 1, "");
2407 for (i = 0; i < COLS/8 && i < 10; i++){
2408 widget_move (&bb->widget, 0, i*8);
2409 attrset (DEFAULT_COLOR);
2410 printw ("%d", i+1);
2411 attrset (SELECTED_COLOR);
2412 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2413 bb->labels [i].text ? bb->labels [i].text : "");
2414 attrset (DEFAULT_COLOR);
2416 attrset (SELECTED_COLOR);
2417 return 1;
2418 #endif
2420 return default_proc (h, msg, par);
2423 static void
2424 buttonbar_destroy (WButtonBar *bb)
2426 int i;
2428 for (i = 0; i < 10; i++){
2429 if (bb->labels [i].text)
2430 g_free (bb->labels [i].text);
2434 static int
2435 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2437 #ifndef HAVE_X
2438 int button;
2440 if (!(event->type & GPM_UP))
2441 return MOU_NORMAL;
2442 if (event->y == 2)
2443 return MOU_NORMAL;
2444 button = event->x / 8;
2445 if (button < 10 && bb->labels [button].function)
2446 (*bb->labels [button].function)(bb->labels [button].data);
2447 #endif
2448 return MOU_NORMAL;
2451 WButtonBar *
2452 buttonbar_new (int visible)
2454 int i;
2455 WButtonBar *bb = g_new (WButtonBar, 1);
2457 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2458 (callback_fn) buttonbar_callback,
2459 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2461 bb->visible = visible;
2462 for (i = 0; i < 10; i++){
2463 bb->labels [i].text = 0;
2464 bb->labels [i].function = 0;
2466 widget_want_hotkey (bb->widget, 1);
2467 widget_want_cursor (bb->widget, 0);
2469 return bb;
2472 void
2473 set_label_text (WButtonBar *bb, int index, char *text)
2475 if (bb->labels [index-1].text)
2476 g_free (bb->labels [index-1].text);
2478 bb->labels [index-1].text = g_strdup (text);
2481 /* paneletc is either the panel widget, or info or view or tree widget */
2482 WButtonBar *
2483 find_buttonbar (Dlg_head *h, Widget *paneletc)
2485 WButtonBar *bb;
2486 Widget_Item *item;
2487 int i;
2489 bb = 0;
2490 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2491 if (item->widget->callback == (callback_fn) buttonbar_callback){
2492 bb = (WButtonBar *) item->widget;
2493 break;
2496 return bb;
2499 void
2500 define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
2501 buttonbarfn cback, void *data)
2503 WButtonBar *bb = find_buttonbar (h, paneletc);
2504 if (!bb)
2505 return;
2507 set_label_text (bb, idx, text);
2508 bb->labels [idx-1].function = (void (*)(void *)) cback;
2509 bb->labels [idx-1].data = data;
2510 x_redefine_label (bb, idx);
2513 void
2514 define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
2516 define_label_data (h, paneletc, idx, text, (void (*)(void *)) cback, 0);
2519 #ifdef HAVE_X
2520 void redraw_labels (Dlg_head *h, Widget *paneletc)
2524 #else
2525 void
2526 redraw_labels (Dlg_head *h, Widget *paneletc)
2528 Widget_Item *item;
2529 int i;
2531 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2532 if (item->widget->callback == (callback_fn) buttonbar_callback){
2533 widget_redraw (h, item);
2534 return;
2538 #endif