Codepage messages related translated & other stuff...
[midnight-commander.git] / src / widget.c
blobc587a2e9b8f0797718227f41687a6edff153bbe8
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 "tty.h"
35 #include <ctype.h>
36 #include "global.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 "x.h"
45 #include "profile.h" /* for history loading and saving */
47 #ifndef HAVE_X
48 # define x_create_button(a,b,c) 1
49 # define x_create_radio(a,b,c) 1
50 # define x_create_check(a,b,c) 1
51 # define x_create_label(a,b,c) 1
52 # define x_create_input(a,b,c) 1
53 # define x_create_listbox(a,b,c) 1
54 # define x_create_buttonbar(a,b,c) 1
55 # define x_create_gauge(a,b,c) 1
56 # define x_listbox_select_nth(a,b)
57 # define x_list_insert(a,b,c)
58 # define x_redefine_label(a,b)
59 #endif
61 #ifndef PORT_HAS_DESTROY_CMD
62 # define x_destroy_cmd(w)
63 #endif
65 #ifndef PORT_HAS_RADIO_FOCUS_ITEM
66 # define x_radio_focus_item(r)
67 #endif
69 #ifndef PORT_HAS_RADIO_TOGGLE
70 # define x_radio_toggle(r)
71 #endif
73 static int button_event (Gpm_Event *event, WButton *b);
75 int quote = 0;
77 static int
78 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
80 #ifndef HAVE_X
81 char buf[BUF_SMALL];
82 #endif
83 int stop = 0;
84 int off = 0;
86 switch (Msg){
87 case WIDGET_INIT:
88 return x_create_button (h, h->wdata, b);
90 case WIDGET_HOTKEY:
91 if (b->hotkey == Par || toupper(b->hotkey) == Par){
92 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
93 return 1;
94 } else
95 return 0;
97 case WIDGET_KEY:
98 if (Par != ' ' && Par != '\n')
99 break;
101 if (b->callback)
102 stop = (*b->callback)(b->action, b->callback_data);
103 if (!b->callback || stop){
104 h->ret_value = b->action;
105 dlg_stop (h);
107 return 1;
109 case WIDGET_CURSOR:
110 switch (b->flags) {
111 case DEFPUSH_BUTTON:
112 off = 3;
113 break;
114 case NORMAL_BUTTON:
115 off = 2;
116 break;
117 case NARROW_BUTTON:
118 off = 1;
119 break;
120 case HIDDEN_BUTTON:
121 default:
122 off = 0;
123 break;
125 widget_move (&b->widget, 0, b->hotpos + off);
126 return 1;
128 case WIDGET_UNFOCUS:
129 case WIDGET_FOCUS:
130 case WIDGET_DRAW:
131 #ifndef HAVE_X
132 if (Msg==WIDGET_UNFOCUS)
133 b->selected = 0;
134 else if (Msg==WIDGET_FOCUS)
135 b->selected = 1;
137 switch (b->flags){
138 case DEFPUSH_BUTTON:
139 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
140 off = 3;
141 break;
142 case NORMAL_BUTTON:
143 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
144 off = 2;
145 break;
146 case NARROW_BUTTON:
147 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
148 off = 1;
149 break;
150 case HIDDEN_BUTTON:
151 default:
152 buf[0] = '\0';
153 off = 0;
154 break;
157 attrset ((b->selected) ? FOCUSC : NORMALC);
158 widget_move (&b->widget, 0, 0);
160 addstr (buf);
162 if (b->hotpos >= 0){
163 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
164 widget_move (&b->widget, 0, b->hotpos+off);
165 addch ((unsigned char)b->text [b->hotpos]);
167 #endif /* HAVE_X */
168 if (Msg == WIDGET_FOCUS)
169 break;
170 else
171 return 1;
172 break;
174 return default_proc (h, Msg, Par);
177 static int
178 button_event (Gpm_Event *event, WButton *b)
180 #ifndef HAVE_X
181 if (event->type & (GPM_DOWN|GPM_UP)){
182 Dlg_head *h=b->widget.parent;
183 dlg_select_widget (h, b);
184 if (event->type & GPM_UP){
185 button_callback (h, b, WIDGET_KEY, ' ');
186 (*h->callback) (h, ' ', DLG_POST_KEY);
187 return MOU_NORMAL;
190 #endif
191 return MOU_NORMAL;
194 static void
195 button_destroy (WButton *b)
197 x_destroy_cmd (b);
198 g_free (b->text);
201 static int
202 button_len (const char *text, unsigned int flags)
204 #ifndef HAVE_X
205 int ret = strlen (text);
206 switch (flags){
207 case DEFPUSH_BUTTON:
208 ret += 6;
209 break;
210 case NORMAL_BUTTON:
211 ret += 4;
212 break;
213 case NARROW_BUTTON:
214 ret += 2;
215 break;
216 case HIDDEN_BUTTON:
217 default:
218 return 0;
220 return ret;
221 #else
222 return strlen (text);
223 #endif
227 * Assuming that button text is malloc'ed, we may safely change it
228 * (as opposed to statically allocated); from other hand, excluding &
229 * and shifting data past it to the left results to one unused byte.
230 * This does not harm though :)
232 void
233 button_scan_hotkey(WButton* b)
235 char* cp = strchr (b->text, '&');
237 if (cp != NULL && cp[1] != '\0'){
238 strcpy (cp, cp+1);
239 b->hotkey = tolower (*cp);
240 b->hotpos = cp - b->text;
244 WButton *
245 button_new (int y, int x, int action, int flags, char *text,
246 int (*callback)(int, void *), void *callback_data, char *tkname)
248 WButton *b = g_new (WButton, 1);
250 init_widget (&b->widget, y, x, 1, button_len (text, flags),
251 (callback_fn) button_callback,
252 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
254 b->action = action;
255 b->flags = flags;
256 b->selected = 0;
257 b->text = g_strdup (text);
258 b->callback = callback;
259 b->callback_data = callback_data;
260 widget_want_hotkey (b->widget, 1);
261 b->hotkey = 0;
262 b->hotpos = -1;
264 button_scan_hotkey(b);
265 return b;
268 void
269 button_set_text (WButton *b, char *text)
271 g_free (b->text);
272 b->text = g_strdup (text);
273 b->widget.cols = button_len (text, b->flags);
274 button_scan_hotkey(b);
275 #ifdef HAVE_X
276 x_button_set (b, b->text);
277 #else
278 dlg_redraw (b->widget.parent);
279 #endif
283 /* Radio button widget */
284 static int radio_event (Gpm_Event *event, WRadio *r);
286 static int
287 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
289 #ifndef HAVE_X
290 int i;
291 #endif
293 switch (Msg) {
294 case WIDGET_INIT:
295 return x_create_radio (h, h->wdata, r);
297 case WIDGET_HOTKEY:
299 int i, lp = tolower(Par);
300 char *cp;
302 for (i = 0; i < r->count; i++){
303 cp = strchr (r->texts [i], '&');
304 if (cp != NULL && cp[1] != '\0'){
305 int c = tolower (cp [1]);
307 if (c != lp)
308 continue;
309 r->pos = i;
310 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
311 return 1;
315 return 0;
317 case WIDGET_KEY:
318 switch (Par){
319 case ' ':
320 r->sel = r->pos;
321 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
322 radio_callback (h, r, WIDGET_FOCUS, ' ');
323 x_radio_toggle (r);
324 return 1;
326 case KEY_UP:
327 case KEY_LEFT:
328 if (r->pos > 0){
329 r->pos--;
330 x_radio_focus_item (r);
331 return 1;
333 return 0;
335 case KEY_DOWN:
336 case KEY_RIGHT:
337 if (r->count - 1 > r->pos) {
338 r->pos++;
339 x_radio_focus_item (r);
340 return 1;
343 return 0;
345 #ifdef HAVE_X
346 case WIDGET_FOCUS:
347 case WIDGET_CURSOR:
348 x_radio_focus_item (r);
349 return 1;
350 #endif
352 #ifndef HAVE_X
353 case WIDGET_CURSOR:
354 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
355 radio_callback (h, r, WIDGET_FOCUS, ' ');
356 widget_move (&r->widget, r->pos, 1);
357 break;
359 case WIDGET_UNFOCUS:
360 case WIDGET_FOCUS:
361 case WIDGET_DRAW:
362 for (i = 0; i < r->count; i++){
363 register unsigned char* cp;
364 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
365 widget_move (&r->widget, i, 0);
367 printw("(%c) ", (r->sel == i) ? '*' : ' ');
368 for (cp = r->texts[i]; *cp; cp++)
370 if (*cp == '&')
372 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
373 ? HOT_FOCUSC : HOT_NORMALC);
374 addch(*++cp);
375 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
377 else
378 addch(*cp);
381 return 1;
382 break;
383 #endif
385 return default_proc (h, Msg, Par);
388 static int
389 radio_event (Gpm_Event *event, WRadio *r)
391 #ifndef HAVE_X
392 if (event->type & (GPM_DOWN|GPM_UP)){
393 Dlg_head *h = r->widget.parent;
395 r->pos = event->y - 1;
396 dlg_select_widget (h, r);
397 if (event->type & GPM_UP){
398 radio_callback (h, r, WIDGET_KEY, ' ');
399 radio_callback (h, r, WIDGET_FOCUS, 0);
400 (*h->callback) (h, ' ', DLG_POST_KEY);
401 return MOU_NORMAL;
404 #endif
405 return MOU_NORMAL;
408 WRadio *
409 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
411 WRadio *r = g_new (WRadio, 1);
412 int i, max, m;
414 /* Compute the longest string */
415 max = 0;
416 for (i = 0; i < count; i++){
417 m = strlen (texts [i]);
418 if (m > max)
419 max = m;
422 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
423 0, (mouse_h) radio_event, tkname);
424 r->state = 1;
425 r->pos = 0;
426 r->sel = 0;
427 r->count = count;
428 r->texts = texts;
429 r->upper_letter_is_hotkey = use_hotkey;
430 widget_want_hotkey (r->widget, 1);
432 return r;
436 /* Checkbutton widget */
438 static int check_event (Gpm_Event *event, WCheck *b);
440 static int
441 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
443 switch (Msg) {
444 case WIDGET_INIT:
445 return x_create_check (h, h->wdata, c);
447 case WIDGET_HOTKEY:
448 if (c->hotkey==Par ||
449 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
450 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
451 return 1;
452 } else
453 return 0;
455 case WIDGET_KEY:
456 if (Par != ' ')
457 break;
458 c->state ^= C_BOOL;
459 c->state ^= C_CHANGE;
460 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
461 check_callback (h, c, WIDGET_FOCUS, ' ');
462 return 1;
464 #ifndef HAVE_X
465 case WIDGET_CURSOR:
466 widget_move (&c->widget, 0, 1);
467 break;
469 case WIDGET_FOCUS:
470 case WIDGET_UNFOCUS:
471 case WIDGET_DRAW:
472 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
473 widget_move (&c->widget, 0, 0);
474 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
476 if (c->hotpos >= 0){
477 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
478 widget_move (&c->widget, 0, + c->hotpos+4);
479 addch ((unsigned char)c->text [c->hotpos]);
481 return 1;
482 #endif /* !HAVE_X */
484 return default_proc (h, Msg, Par);
487 static int
488 check_event (Gpm_Event *event, WCheck *c)
490 #ifndef HAVE_X
491 if (event->type & (GPM_DOWN|GPM_UP)){
492 Dlg_head *h = c->widget.parent;
494 dlg_select_widget (h, c);
495 if (event->type & GPM_UP){
496 check_callback (h, c, WIDGET_KEY, ' ');
497 check_callback (h, c, WIDGET_FOCUS, 0);
498 (*h->callback) (h, ' ', DLG_POST_KEY);
499 return MOU_NORMAL;
502 #endif
503 return MOU_NORMAL;
506 static void
507 check_destroy (WCheck *c)
509 x_destroy_cmd (c);
510 g_free (c->text);
513 WCheck *
514 check_new (int y, int x, int state, char *text, char *tkname)
516 WCheck *c = g_new (WCheck, 1);
517 char *s, *t;
519 init_widget (&c->widget, y, x, 1, strlen (text),
520 (callback_fn)check_callback,
521 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
522 c->state = state ? C_BOOL : 0;
523 c->text = g_strdup (text);
524 c->hotkey = 0;
525 c->hotpos = -1;
526 widget_want_hotkey (c->widget, 1);
528 /* Scan for the hotkey */
529 for (s = text, t = c->text; *s; s++, t++){
530 if (*s != '&'){
531 *t = *s;
532 continue;
534 s++;
535 if (*s){
536 c->hotkey = tolower (*s);
537 c->hotpos = t - c->text;
539 *t = *s;
541 *t = 0;
542 return c;
546 /* Label widget */
548 static int
549 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
551 if (Msg == WIDGET_INIT)
552 return x_create_label (h, h->wdata, l);
554 /* We don't want to get the focus */
555 if (Msg == WIDGET_FOCUS)
556 return 0;
557 #ifndef HAVE_X
558 if (Msg == WIDGET_DRAW && l->text){
559 char *p = l->text, *q, c = 0;
560 int y = 0;
561 if (l->transparent)
562 attrset (DEFAULT_COLOR);
563 else
564 attrset (NORMALC);
565 for (;;){
566 int xlen;
568 q = strchr (p, '\n');
569 if (q){
570 c = *q;
571 *q = 0;
573 widget_move (&l->widget, y, 0);
574 printw ("%s", p);
575 xlen = l->widget.cols - strlen (p);
576 if (xlen > 0)
577 printw ("%*s", xlen, " ");
578 if (!q)
579 break;
580 *q = c;
581 p = q + 1;
582 y++;
584 return 1;
586 #endif
587 return default_proc (h, Msg, Par);
590 void
591 label_set_text (WLabel *label, char *text)
593 int newcols = label->widget.cols;
595 if (label->text && text && !strcmp (label->text, text))
596 return; /* Flickering is not nice */
598 if (label->text){
599 g_free (label->text);
601 if (text){
602 label->text = g_strdup (text);
603 if (label->auto_adjust_cols) {
604 newcols = strlen (text);
605 if (newcols > label->widget.cols)
606 label->widget.cols = newcols;
608 } else
609 label->text = 0;
611 if (label->widget.parent)
612 #ifdef HAVE_X
613 x_label_set_text (label, text);
614 #else
615 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
616 #endif
617 if (newcols < label->widget.cols)
618 label->widget.cols = newcols;
621 static void
622 label_destroy (WLabel *l)
624 x_destroy_cmd (l);
625 if (l->text)
626 g_free (l->text);
629 WLabel *
630 label_new (int y, int x, char *text, char *tkname)
632 WLabel *l = g_new (WLabel, 1);
634 init_widget (&l->widget, y, x, 1, 1,
635 (callback_fn) label_callback,
636 (destroy_fn) label_destroy, NULL, tkname);
637 l->text = text ? g_strdup (text) : 0;
638 l->auto_adjust_cols = 1;
639 l->transparent = 0;
640 widget_want_cursor (l->widget, 0);
641 return l;
645 /* Gauge widget (progress indicator) */
646 /* Currently width is hardcoded here for text mode */
647 #define gauge_len 47
649 static int
650 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
653 if (Msg == WIDGET_INIT)
654 return x_create_gauge (h, h->wdata, g);
656 /* We don't want to get the focus */
657 if (Msg == WIDGET_FOCUS)
658 return 0;
660 #ifndef HAVE_X
661 if (Msg == WIDGET_DRAW){
662 widget_move (&g->widget, 0, 0);
663 attrset (NORMALC);
664 if (!g->shown)
665 printw ("%*s", gauge_len, "");
666 else {
667 long percentage, columns;
668 long total = g->max, done = g->current;
670 if (total <= 0 || done < 0) {
671 done = 0;
672 total = 100;
674 if (done > total)
675 done = total;
676 while (total > 65535) {
677 total /= 256;
678 done /= 256;
680 percentage = (200 * done / total + 1) / 2;
681 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
682 addch ('[');
683 attrset (GAUGE_COLOR);
684 printw ("%*s", columns, "");
685 attrset (NORMALC);
686 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
688 return 1;
690 #endif
691 return default_proc (h, Msg, Par);
694 void
695 gauge_set_value (WGauge *g, int max, int current)
697 if (g->current == current && g->max == max)
698 return; /* Do not flicker */
699 if (max == 0)
700 max = 1; /* I do not like division by zero :) */
701 #ifdef HAVE_X
702 /* NOTE: x_gauge_set_value has to be called before we change actual
703 * max and current values in g, since it assumes g->max and
704 * g->current as the previous values and max and current
705 * as the new ones :) */
706 x_gauge_set_value (g, max, current);
707 #endif
708 g->current = current;
709 g->max = max;
710 #ifndef HAVE_X
711 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
712 #endif
715 void
716 gauge_show (WGauge *g, int shown)
718 if (g->shown == shown)
719 return;
720 g->shown = shown;
721 #ifdef HAVE_X
722 x_gauge_show (g);
723 #else
724 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
725 #endif
728 static void
729 gauge_destroy (WGauge *g)
731 /* nothing */
734 WGauge *
735 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
737 WGauge *g = g_new (WGauge, 1);
739 init_widget (&g->widget, y, x, 1, gauge_len,
740 (callback_fn) gauge_callback,
741 (destroy_fn) gauge_destroy, NULL, tkname);
742 g->shown = shown;
743 if (max == 0)
744 max = 1; /* I do not like division by zero :) */
745 g->max = max;
746 g->current = current;
747 g->pixels = 0;
748 widget_want_cursor (g->widget, 0);
749 return g;
753 /* Input widget */
755 /* {{{ history button */
757 #define LARGE_HISTORY_BUTTON 1
759 #ifdef LARGE_HISTORY_BUTTON
760 # define HISTORY_BUTTON_WIDTH 3
761 #else
762 # define HISTORY_BUTTON_WIDTH 1
763 #endif
765 #define should_show_history_button(in) \
766 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
768 #ifndef HAVE_X
769 static void draw_history_button (WInput * in)
771 char c;
772 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
773 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
774 #ifdef LARGE_HISTORY_BUTTON
776 Dlg_head *h;
777 h = in->widget.parent;
778 #if 0
779 attrset (NORMALC); /* button has the same colour as other buttons */
780 addstr ("[ ]");
781 attrset (HOT_NORMALC);
782 #else
783 attrset (NORMAL_COLOR);
784 addstr ("[ ]");
785 /* Too distracting: attrset (MARKED_COLOR); */
786 #endif
787 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
788 addch (c);
790 #else
791 attrset (MARKED_COLOR);
792 addch (c);
793 #endif
795 #endif
797 /* }}} history button */
800 /* Input widgets now have a global kill ring */
801 /* Pointer to killed data */
802 static char *kill_buffer = 0;
804 void
805 update_input (WInput *in, int clear_first)
807 int has_history = 0;
808 #ifndef HAVE_X
809 int i, j;
810 unsigned char c;
811 #endif
812 int buf_len = strlen (in->buffer);
814 if (should_show_history_button (in))
815 has_history = HISTORY_BUTTON_WIDTH;
817 if (in->disable_update)
818 return;
820 /* Make the point visible */
821 if ((in->point < in->first_shown) ||
822 (in->point >= in->first_shown+in->field_len - has_history)){
823 in->first_shown = in->point - (in->field_len / 3);
824 if (in->first_shown < 0)
825 in->first_shown = 0;
828 /* Adjust the mark */
829 if (in->mark > buf_len)
830 in->mark = buf_len;
832 #ifdef HAVE_X
833 if (clear_first && in->first)
834 in->first = -1;
835 x_update_input (in);
836 #else
838 if (has_history)
839 draw_history_button (in);
841 attrset (in->color);
843 widget_move (&in->widget, 0, 0);
844 for (i = 0; i < in->field_len - has_history; i++)
845 addch (' ');
846 widget_move (&in->widget, 0, 0);
848 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
849 c = in->buffer [j++];
850 c = is_printable (c) ? c : '.';
851 if (in->is_password)
852 c = '*';
853 addch (c);
855 widget_move (&in->widget, 0, in->point - in->first_shown);
857 if (clear_first)
858 in->first = 0;
859 #endif
862 void
863 winput_set_origin (WInput *in, int x, int field_len)
865 in->widget.x = x;
866 in->field_len = in->widget.cols = field_len;
867 update_input (in, 0);
870 /* {{{ history saving and loading */
873 This loads and saves the history of an input line to and from the
874 widget. It is called with the widgets tk name on creation of the
875 widget, and returns the Hist list. It stores histories in the file
876 ~/.mc/history in using the profile code.
878 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
879 function) then input_new assigns the default text to be the last text
880 entered, or "" if not found.
883 int num_history_items_recorded = 60;
885 Hist *history_get (char *input_name)
887 int i;
888 Hist *old, *new;
889 char *profile;
891 old = new = NULL;
893 if (!num_history_items_recorded) /* this is how to disable */
894 return 0;
895 if (!input_name)
896 return 0;
897 if (!*input_name)
898 return 0;
899 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
900 for (i = 0;; i++) {
901 char key_name[BUF_TINY];
902 char this_entry[BUF_LARGE];
903 g_snprintf (key_name, sizeof (key_name), "%d", i);
904 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
905 if (!*this_entry)
906 break;
907 new = g_new0 (Hist, 1);
908 new->text = g_strdup (this_entry);
909 new->prev = old; /* set up list pointers */
910 if (old)
911 old->next = new;
912 old = new;
914 g_free (profile);
915 return new; /* return pointer to last entry in list */
918 void history_put (char *input_name, Hist *h)
920 #ifdef PORT_WIDGET_WANTS_HISTORY
921 int i;
922 char *profile;
924 if (!input_name)
925 return;
927 if (!*input_name)
928 return;
930 if (!h)
931 return;
933 if (!num_history_items_recorded) /* this is how to disable */
934 return;
936 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
938 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
939 close (i);
940 /* Just in case I forgot to strip passwords somewhere -- Norbert */
941 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
942 g_free (profile);
943 return;
946 while (h->next) /* go to end of list */
947 h = h->next;
949 /* go back 60 places */
950 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
951 h = h->prev;
953 if (input_name)
954 profile_clean_section (input_name, profile);
956 /* dump histories into profile */
957 for (i = 0; h; h = h->next){
958 if (h->text){
960 /* probably aren't any null entries, but lets be sure */
961 if (*(h->text)){
962 char key_name[BUF_TINY];
963 g_snprintf (key_name, sizeof(key_name), "%d", i++);
964 WritePrivateProfileString (input_name, key_name, h->text, profile);
968 g_free (profile);
969 #endif
972 /* }}} history saving and loading */
975 /* {{{ history display */
977 static char *
978 i18n_htitle (void)
980 static char *history_title = NULL;
982 if (history_title == NULL)
983 history_title = _(" History ");
984 return history_title;
987 static int
988 history_callback (Dlg_head * h, int Par, int Msg)
990 #ifndef HAVE_X
991 switch (Msg) {
992 case DLG_DRAW:
993 attrset (COLOR_NORMAL);
994 dlg_erase (h);
995 draw_box (h, 0, 0, h->lines, h->cols);
996 attrset (COLOR_HOT_NORMAL);
997 dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
998 printw (i18n_htitle());
999 break;
1001 #endif
1002 return 0;
1005 static inline int listbox_fwd (WListbox *l);
1007 char *show_hist (Hist *history, int widget_x, int widget_y)
1009 Hist *hi, *z;
1010 size_t maxlen = strlen (i18n_htitle()), i, count = 0;
1011 int x, y, w, h;
1012 char *q, *r = 0;
1013 Dlg_head *query_dlg;
1014 WListbox *query_list;
1016 z = history;
1017 if (!z)
1018 return 0;
1020 while (z->prev) /* goto first */
1021 z = z->prev;
1022 hi = z;
1023 while (hi) {
1024 if ((i = strlen (hi->text)) > maxlen)
1025 maxlen = i;
1026 count++;
1027 hi = hi->next;
1030 y = widget_y;
1031 h = count + 2;
1032 if (h <= y || y > LINES - 6)
1034 h = min(h, y - 1);
1035 y -= h;
1037 else
1039 y++;
1040 h = min(h, LINES - y);
1043 if (widget_x > 2)
1044 x = widget_x - 2;
1045 else
1046 x = 0;
1047 if ((w = maxlen + 4) + x > COLS)
1049 w = min(w,COLS);
1050 x = COLS - w;
1053 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
1054 "[History-query]", "history", DLG_NONE);
1055 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
1056 add_widget (query_dlg, query_list);
1057 hi = z;
1058 if (y < widget_y) {
1059 while (hi) { /* traverse */
1060 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1061 hi = hi->next;
1063 while (listbox_fwd (query_list));
1064 } else {
1065 while (hi->next)
1066 hi = hi->next;
1067 while (hi) { /* traverse backwards */
1068 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1069 hi = hi->prev;
1072 run_dlg (query_dlg);
1073 q = NULL;
1074 if (query_dlg->ret_value != B_CANCEL) {
1075 listbox_get_current (query_list, &q, NULL);
1076 if (q)
1077 r = g_strdup (q);
1079 destroy_dlg (query_dlg);
1080 return r;
1083 static void do_show_hist (WInput * in)
1085 char *r;
1086 r = show_hist (in->history, in->widget.x, in->widget.y);
1087 if (r) {
1088 assign_text (in, r);
1089 g_free (r);
1093 /* }}} history display */
1095 static void
1096 input_destroy (WInput *in)
1098 if (!in){
1099 fprintf (stderr, "Internal error: null Input *\n");
1100 exit (1);
1103 new_input (in);
1104 if (in->history){
1105 Hist *current, *old;
1107 if (!in->is_password && PORT_WIDGET_WANTS_HISTORY) /* don't save passwords ;-) */
1108 history_put (in->history_name, in->history);
1110 current = in->history;
1111 while (current->next)
1112 current = current->next;
1113 while (current){
1114 old = current;
1115 current = current->prev;
1116 g_free (old->text);
1117 g_free (old);
1120 x_destroy_cmd (in);
1121 g_free (in->buffer);
1122 free_completions (in);
1123 if (in->history_name)
1124 g_free (in->history_name);
1127 static char disable_update = 0;
1129 void
1130 input_disable_update (WInput *in)
1132 in->disable_update++;
1135 void
1136 input_enable_update (WInput *in)
1138 in->disable_update--;
1139 update_input (in, 0);
1142 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1145 push_history (WInput *in, char *text)
1147 static int i18n;
1148 /* input widget where urls with passwords are entered without any
1149 vfs prefix */
1150 static const char *password_input_fields[] = {
1151 N_(" Link to a remote machine "),
1152 N_(" FTP to machine "),
1153 N_(" SMB link to machine ")
1155 Hist *new;
1156 char *p;
1157 int i;
1159 if (!i18n) {
1160 i18n = 1;
1161 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1162 password_input_fields[i] = _(password_input_fields[i]);
1165 for (p = text; *p == ' ' || *p == '\t'; p++);
1166 if (!*p)
1167 return 0;
1168 if (in->history){
1169 while (in->history->next)
1170 in->history = in->history->next;
1171 if (!strcmp (in->history->text, text))
1172 return 1;
1173 new = g_new (Hist, 1);
1174 in->history->next = new;
1175 } else
1176 new = g_new (Hist, 1);
1177 in->need_push = 0;
1178 new->next = 0;
1179 new->prev = in->history;
1180 new->text = g_strdup (text);
1181 if (in->history_name) {
1182 p = in->history_name + 3;
1183 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1184 if (strcmp (p, password_input_fields[i]) == 0)
1185 break;
1186 if (i < ELEMENTS(password_input_fields))
1187 strip_password (new->text, 0);
1188 else
1189 strip_password (new->text, 1);
1192 in->history = new;
1193 return 2;
1196 #undef ELEMENTS
1198 /* Cleans the input line and adds the current text to the history */
1199 void
1200 new_input (WInput *in)
1202 if (in->buffer)
1203 push_history (in, in->buffer);
1204 in->need_push = 1;
1205 in->buffer [0] = 0;
1206 in->point = 0;
1207 in->mark = 0;
1208 free_completions (in);
1209 update_input (in, 0);
1212 static int
1213 insert_char (WInput *in, int c_code)
1215 int i;
1217 if (c_code == -1)
1218 return 0;
1220 in->need_push = 1;
1221 if (strlen (in->buffer)+1 == in->current_max_len){
1222 /* Expand the buffer */
1223 char *narea = g_malloc (in->current_max_len + in->field_len);
1224 if (narea){
1225 char *p = in->buffer;
1227 strcpy (narea, in->buffer);
1228 in->buffer = narea;
1229 in->current_max_len += in->field_len;
1230 g_free (p);
1233 if (strlen (in->buffer)+1 < in->current_max_len){
1234 int l = strlen (&in->buffer [in->point]);
1235 for (i = l+1; i > 0; i--)
1236 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1237 in->buffer [in->point] = c_code;
1238 in->point++;
1240 return 1;
1243 static void
1244 beginning_of_line (WInput *in)
1246 in->point = 0;
1249 static void
1250 end_of_line (WInput *in)
1252 in->point = strlen (in->buffer);
1255 static void
1256 backward_char (WInput *in)
1258 if (in->point)
1259 in->point--;
1262 static void
1263 forward_char (WInput *in)
1265 if (in->buffer [in->point])
1266 in->point++;
1269 static void
1270 forward_word (WInput *in)
1272 unsigned char *p = in->buffer+in->point;
1274 while (*p && (isspace (*p) || ispunct (*p)))
1275 p++;
1276 while (*p && isalnum (*p))
1277 p++;
1278 in->point = p - in->buffer;
1281 static void
1282 backward_word (WInput *in)
1284 unsigned char *p = in->buffer+in->point;
1286 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1287 p--;
1288 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1289 p--;
1290 in->point = p - in->buffer;
1293 #if defined(__linux__) && !defined(HAVE_X)
1294 static void
1295 key_left (WInput *in)
1297 if (ctrl_pressed ())
1298 backward_word (in);
1299 else
1300 backward_char (in);
1303 static void
1304 key_right (WInput *in)
1306 if (ctrl_pressed ())
1307 forward_word (in);
1308 else
1309 forward_char (in);
1311 #else
1312 #define key_left backward_char
1313 #define key_right forward_char
1314 #endif
1316 static void
1317 backward_delete (WInput *in)
1319 int i;
1321 if (!in->point)
1322 return;
1323 for (i = in->point; in->buffer [i-1]; i++)
1324 in->buffer [i-1] = in->buffer [i];
1325 in->need_push = 1;
1326 in->point--;
1329 static void
1330 delete_char (WInput *in)
1332 int i;
1334 for (i = in->point; in->buffer [i]; i++)
1335 in->buffer [i] = in->buffer [i+1];
1336 in->need_push = 1;
1339 static void
1340 copy_region (WInput *in, int x_first, int x_last)
1342 int first = min (x_first, x_last);
1343 int last = max (x_first, x_last);
1345 if (last == first)
1346 return;
1348 if (kill_buffer)
1349 g_free (kill_buffer);
1351 kill_buffer = g_malloc (last-first + 1);
1352 strncpy (kill_buffer, in->buffer+first, last-first);
1353 kill_buffer [last-first] = 0;
1356 static void
1357 delete_region (WInput *in, int x_first, int x_last)
1359 int first = min (x_first, x_last);
1360 int last = max (x_first, x_last);
1362 in->point = first;
1363 in->mark = first;
1364 strcpy (&in->buffer [first], &in->buffer [last]);
1365 in->need_push = 1;
1368 static void
1369 kill_word (WInput *in)
1371 int old_point = in->point;
1372 int new_point;
1374 forward_word (in);
1375 new_point = in->point;
1376 in->point = old_point;
1378 copy_region (in, old_point, new_point);
1379 delete_region (in, old_point, new_point);
1380 in->need_push = 1;
1383 static void
1384 back_kill_word (WInput *in)
1386 int old_point = in->point;
1387 int new_point;
1389 backward_word (in);
1390 new_point = in->point;
1391 in->point = old_point;
1393 copy_region (in, old_point, new_point);
1394 delete_region (in, old_point, new_point);
1395 in->need_push = 1;
1398 static void
1399 set_mark (WInput *in)
1401 in->mark = in->point;
1404 static void
1405 kill_save (WInput *in)
1407 copy_region (in, in->mark, in->point);
1410 static void
1411 kill_region (WInput *in)
1413 kill_save (in);
1414 delete_region (in, in->point, in->mark);
1417 static void
1418 yank (WInput *in)
1420 char *p;
1422 if (!kill_buffer)
1423 return;
1424 for (p = kill_buffer; *p; p++)
1425 insert_char (in, *p);
1428 static void
1429 kill_line (WInput *in)
1431 if (kill_buffer)
1432 g_free (kill_buffer);
1433 kill_buffer = g_strdup (&in->buffer [in->point]);
1434 in->buffer [in->point] = 0;
1437 void
1438 assign_text (WInput *in, char *text)
1440 free_completions (in);
1441 g_free (in->buffer);
1442 in->buffer = g_strdup (text); /* was in->buffer->text */
1443 in->current_max_len = strlen (in->buffer) + 1;
1444 in->point = strlen (in->buffer);
1445 in->mark = 0;
1446 in->need_push = 1;
1449 static void
1450 hist_prev (WInput *in)
1452 if (!in->history)
1453 return;
1455 if (in->need_push) {
1456 switch (push_history (in, in->buffer)) {
1457 case 2: in->history = in->history->prev; break;
1458 case 1: if (in->history->prev) in->history = in->history->prev; break;
1459 case 0: break;
1461 } else if (in->history->prev)
1462 in->history = in->history->prev;
1463 else
1464 return;
1465 assign_text (in, in->history->text);
1466 in->need_push = 0;
1469 static void
1470 hist_next (WInput *in)
1472 if (in->need_push) {
1473 switch (push_history (in, in->buffer)) {
1474 case 2:
1475 assign_text (in, "");
1476 return;
1477 case 0:
1478 return;
1482 if (!in->history)
1483 return;
1485 if (!in->history->next) {
1486 assign_text (in, "");
1487 return;
1490 in->history = in->history->next;
1491 assign_text (in, in->history->text);
1492 in->need_push = 0;
1495 static const struct {
1496 int key_code;
1497 void (*fn)(WInput *in);
1498 } input_map [] = {
1499 /* Motion */
1500 { XCTRL('a'), beginning_of_line },
1501 { KEY_HOME, beginning_of_line },
1502 { KEY_A1, beginning_of_line },
1503 { XCTRL('e'), end_of_line },
1504 { KEY_END, end_of_line },
1505 { KEY_C1, end_of_line },
1506 { KEY_LEFT, key_left },
1507 { XCTRL('b'), backward_char },
1508 { ALT('b'), backward_word },
1509 { KEY_RIGHT, key_right },
1510 { XCTRL('f'), forward_char },
1511 { ALT('f'), forward_word },
1513 /* Editing */
1514 { 0177, backward_delete },
1515 { KEY_BACKSPACE, backward_delete },
1516 { XCTRL('h'), backward_delete },
1517 { KEY_DC, delete_char },
1518 { XCTRL('d'), delete_char },
1519 { ALT('d'), kill_word },
1520 { ALT(KEY_BACKSPACE), back_kill_word },
1521 { ALT(XCTRL('h')), back_kill_word },
1522 { ALT(127), back_kill_word },
1524 /* Region manipulation */
1525 { 0, set_mark },
1526 { XCTRL('w'), kill_region },
1527 { ALT('w'), kill_save },
1528 { XCTRL('y'), yank },
1529 { XCTRL('k'), kill_line },
1531 /* History */
1532 { ALT('p'), hist_prev },
1533 { ALT('n'), hist_next },
1534 { ALT('h'), do_show_hist },
1536 /* Completion */
1537 { ALT('\t'), complete },
1539 { 0, 0 }
1542 /* This function is a test for a special input key used in complete.c */
1543 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1544 and 2 if it is a complete key */
1546 is_in_input_map (WInput *in, int c_code)
1548 int i;
1550 for (i = 0; input_map [i].fn; i++)
1551 if (c_code == input_map [i].key_code) {
1552 if (input_map [i].fn == complete)
1553 return 2;
1554 else
1555 return 1;
1557 return 0;
1560 #ifdef PORT_WINPUT_DELETES_MARKED
1561 static void
1562 port_region_marked_for_delete (WInput *in)
1564 if (in->first == 1 && (in->point == in->mark))
1565 in->point = strlen (in->buffer);
1566 kill_region (in);
1567 in->first = 0;
1569 #else
1570 static void
1571 port_region_marked_for_delete (WInput *in)
1573 *in->buffer = 0;
1574 in->point = 0;
1575 in->first = 0;
1577 #endif
1580 handle_char (WInput *in, int c_code)
1582 int i;
1583 int v;
1585 v = 0;
1587 if (quote){
1588 free_completions (in);
1589 v = insert_char (in, c_code);
1590 update_input (in, 1);
1591 quote = 0;
1592 return v;
1595 for (i = 0; input_map [i].fn; i++){
1596 if (c_code == input_map [i].key_code){
1597 if (input_map [i].fn != complete)
1598 free_completions (in);
1599 (*input_map [i].fn)(in);
1600 v = 1;
1601 break;
1604 if (!input_map [i].fn){
1605 if (c_code > 255 || !is_printable (c_code))
1606 return 0;
1607 if (in->first){
1608 port_region_marked_for_delete (in);
1610 free_completions (in);
1611 v = insert_char (in, c_code);
1612 in->inserted_one = c_code;
1614 if (!disable_update)
1615 update_input (in, 1);
1616 return v;
1619 /* Inserts text in input line */
1620 void
1621 stuff (WInput *in, char *text, int insert_extra_space)
1623 input_disable_update (in);
1624 while (*text)
1625 handle_char (in, *text++);
1626 if (insert_extra_space)
1627 handle_char (in, ' ');
1628 input_enable_update (in);
1629 update_input (in, 1);
1632 void
1633 input_set_point (WInput *in, int pos)
1635 if (pos > in->current_max_len)
1636 pos = in->current_max_len;
1637 if (pos != in->point)
1638 free_completions (in);
1639 in->point = pos;
1640 update_input (in, 1);
1643 int input_event (Gpm_Event *event, WInput *b);
1645 static int
1646 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1648 switch (Msg){
1649 case WIDGET_INIT:
1650 return x_create_input (h, h->wdata, in);
1652 case WIDGET_KEY:
1653 if (Par == XCTRL('q')){
1654 int v;
1656 quote = 1;
1657 v = handle_char (in, mi_getch ());
1658 quote = 0;
1659 return v;
1661 if (Par == KEY_UP || Par == KEY_DOWN ||
1662 Par == ESC_CHAR || Par == KEY_F(10) ||
1663 Par == XCTRL('g'))
1664 return 0; /* We don't handle up/down */
1666 if (Par == '\n'){
1667 dlg_one_down (h);
1668 return 1;
1670 return handle_char (in, Par);
1672 case WIDGET_FOCUS:
1673 case WIDGET_UNFOCUS:
1674 case WIDGET_DRAW:
1675 update_input (in, 0);
1676 break;
1677 #ifndef HAVE_X
1678 case WIDGET_CURSOR:
1679 widget_move (&in->widget, 0, in->point - in->first_shown);
1680 return 1;
1681 #endif
1684 return default_proc (h, Msg, Par);
1687 /* Not declared static, since we check against this value in dlg.c */
1688 /* FIXME: Declare static again and provide an identification mechanism */
1689 int
1690 input_event (Gpm_Event *event, WInput *in)
1692 #ifndef HAVE_X
1693 if (event->type & (GPM_DOWN|GPM_DRAG)){
1694 dlg_select_widget (in->widget.parent, in);
1696 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
1697 do_show_hist (in);
1698 } else {
1699 in->point = strlen (in->buffer);
1700 if (event->x - in->first_shown - 1 < in->point)
1701 in->point = event->x - in->first_shown - 1;
1702 if (in->point < 0)
1703 in->point = 0;
1705 update_input (in, 1);
1707 #endif
1708 return MOU_NORMAL;
1711 WInput *
1712 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1714 WInput *in = g_new (WInput, 1);
1715 int initial_buffer_len;
1717 init_widget (&in->widget, y, x, 1, len,
1718 (callback_fn) input_callback,
1719 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1721 /* history setup */
1722 in->history = NULL;
1723 in->history_name = 0;
1724 if (tkname && PORT_WIDGET_WANTS_HISTORY){
1725 if (*tkname) {
1726 in->history_name = g_strdup (tkname);
1727 in->history = history_get (tkname);
1731 if (!def_text)
1732 def_text = "";
1734 if (def_text == INPUT_LAST_TEXT) {
1735 def_text = "";
1736 if (in->history)
1737 if (in->history->text)
1738 def_text = in->history->text;
1740 initial_buffer_len = 1 + max (len, strlen (def_text));
1741 in->widget.options |= W_IS_INPUT;
1742 in->completions = NULL;
1743 in->completion_flags =
1744 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1745 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1746 in->current_max_len = initial_buffer_len;
1747 in->buffer = g_malloc (initial_buffer_len);
1748 in->color = color;
1749 in->field_len = len;
1750 in->first = 1;
1751 in->first_shown = 0;
1752 in->disable_update = 0;
1753 in->mark = 0;
1754 in->need_push = 1;
1755 in->is_password = 0;
1757 strcpy (in->buffer, def_text);
1758 in->point = strlen (in->buffer);
1759 return in;
1763 /* Listbox widget */
1765 /* Should draw the scrollbar, but currently draws only
1766 * indications that there is more information
1768 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1770 #ifdef HAVE_X
1771 static void
1772 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1774 /* nothing */
1776 #else
1777 static void
1778 listbox_drawscroll (WListbox *l)
1780 extern int slow_terminal;
1781 int line;
1782 int i, top;
1783 int max_line = l->height-1;
1785 /* Are we at the top? */
1786 widget_move (&l->widget, 0, l->width);
1787 if (l->list == l->top)
1788 one_vline ();
1789 else
1790 addch ('^');
1792 /* Are we at the bottom? */
1793 widget_move (&l->widget, max_line, l->width);
1794 top = listbox_cdiff (l->list, l->top);
1795 if ((top + l->height == l->count) || l->height >= l->count)
1796 one_vline ();
1797 else
1798 addch ('v');
1800 /* Now draw the nice relative pointer */
1801 if (l->count)
1802 line = 1+ ((l->pos * (l->height-2)) / l->count);
1803 else
1804 line = 0;
1806 for (i = 1; i < max_line; i++){
1807 widget_move (&l->widget, i, l->width);
1808 if (i != line)
1809 one_vline ();
1810 else
1811 addch ('*');
1815 static void
1816 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1818 WLEntry *e;
1819 int i;
1820 int sel_line;
1821 int normalc = NORMALC;
1822 int selc;
1823 char *text;
1825 if (focused){
1826 selc = FOCUSC;
1827 } else {
1828 selc = HOT_FOCUSC;
1830 sel_line = -1;
1832 for (e = l->top, i = 0; (i < l->height); i++){
1834 /* Display the entry */
1835 if (e == l->current && sel_line == -1){
1836 sel_line = i;
1837 attrset (selc);
1838 } else
1839 attrset (normalc);
1841 widget_move (&l->widget, i, 0);
1843 if ((i > 0 && e == l->list) || !l->list)
1844 text = "";
1845 else {
1846 text = e->text;
1847 e = e->next;
1849 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1851 l->cursor_y = sel_line;
1852 if (!l->scrollbar)
1853 return;
1854 attrset (normalc);
1855 listbox_drawscroll (l);
1857 #endif /* HAVE_X */
1859 /* Returns the number of items between s and e,
1860 must be on the same linked list */
1861 static int
1862 listbox_cdiff (WLEntry *s, WLEntry *e)
1864 int count;
1866 for (count = 0; s != e; count++)
1867 s = s->next;
1868 return count;
1871 static WLEntry *
1872 listbox_check_hotkey (WListbox *l, int key)
1874 int i;
1875 WLEntry *e;
1877 i = 0;
1878 e = l->list;
1879 if (!e)
1880 return 0;
1882 while (1){
1884 /* If we didn't find anything, return */
1885 if (i && e == l->list)
1886 return 0;
1888 if (e->hotkey == key)
1889 return e;
1891 i++;
1892 e = e->next;
1896 /* Used only for display updating, for avoiding line at a time scroll */
1897 void
1898 listbox_select_last (WListbox *l, int set_top)
1900 if (l->list){
1901 l->current = l->list->prev;
1902 l->pos = l->count - 1;
1903 if (set_top)
1904 l->top = l->list->prev;
1905 x_listbox_select_nth (l, l->pos);
1909 void
1910 listbox_remove_list (WListbox *l)
1912 WLEntry *p, *q;
1914 if (!l->count)
1915 return;
1917 #ifdef HAVE_X
1918 if (l->widget.wdata != (widget_data) NULL) {
1919 int i;
1920 for (i = 0; i < l->count; i++)
1921 x_listbox_delete_nth (l, i);
1923 #endif
1924 p = l->list;
1926 while (l->count--) {
1927 q = p->next;
1928 g_free (p->text);
1929 g_free (p);
1930 p = q;
1932 l->pos = l->count = 0;
1933 l->list = l->top = l->current = 0;
1937 * bor 30.10.96: added force flag to remove *last* entry as well
1938 * bor 30.10.96: corrected selection bug if last entry was removed
1941 void
1942 listbox_remove_current (WListbox *l, int force)
1944 WLEntry *p;
1946 /* Ok, note: this won't allow for emtpy lists */
1947 if (!force && (!l->count || l->count == 1))
1948 return;
1950 #ifdef HAVE_X
1951 if (l->widget.wdata != (widget_data) NULL) {
1952 x_listbox_delete_nth (l, l->pos);
1953 if (l->count > 1) {
1954 if (l->current->next != l->list)
1955 x_listbox_select_nth (l, l->pos);
1956 else if (l->current != l->list)
1957 x_listbox_select_nth (l, l->pos - 1);
1958 } else
1959 x_listbox_select_nth (l, 0);
1961 #endif
1962 l->count--;
1963 p = l->current;
1965 if (l->count) {
1966 l->current->next->prev = l->current->prev;
1967 l->current->prev->next = l->current->next;
1968 if (p->next == l->list) {
1969 l->current = p->prev;
1970 l->pos--;
1972 else
1973 l->current = p->next;
1975 if (p == l->list)
1976 l->list = l->top = p->next;
1977 } else {
1978 l->pos = 0;
1979 l->list = l->top = l->current = 0;
1982 g_free (p->text);
1983 g_free (p);
1986 /* Makes *e the selected entry (sets current and pos) */
1987 void
1988 listbox_select_entry (WListbox *l, WLEntry *dest)
1990 WLEntry *e;
1991 int pos;
1992 int top_seen;
1994 top_seen = 0;
1996 /* Special case */
1997 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1999 if (e == l->top)
2000 top_seen = 1;
2002 if (e == dest){
2003 l->current = e;
2004 if (top_seen){
2005 while (listbox_cdiff (l->top, l->current) >= l->height)
2006 l->top = l->top->next;
2007 } else {
2008 l->top = l->current;
2010 l->pos = pos;
2011 x_listbox_select_nth (l, l->pos);
2012 return;
2015 /* If we are unable to find it, set decent values */
2016 l->current = l->top = l->list;
2017 l->pos = 0;
2018 x_listbox_select_nth (l, l->pos);
2021 /* Selects from base the pos element */
2022 static WLEntry *
2023 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
2025 WLEntry *last = l->list->prev;
2027 if (base == last)
2028 return last;
2029 while (pos--){
2030 base = base->next;
2031 if (base == last)
2032 break;
2034 return base;
2037 static inline int
2038 listbox_back (WListbox *l)
2040 if (l->pos){
2041 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
2042 return 1;
2044 return 0;
2047 static inline int
2048 listbox_fwd (WListbox *l)
2050 if (l->current != l->list->prev){
2051 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
2052 return 1;
2054 return 0;
2057 /* Returns 1 if we want a redraw */
2058 static int
2059 listbox_key (WListbox *l, int key)
2061 int i;
2062 int j = 0;
2064 if (!l->list)
2065 return 0;
2067 switch (key){
2068 case KEY_HOME:
2069 case KEY_A1:
2070 l->current = l->top = l->list;
2071 l->pos = 0;
2072 return 1;
2074 case KEY_END:
2075 case KEY_C1:
2076 l->current = l->top = l->list->prev;
2077 for (i = min (l->height - 1, l->count - 1); i; i--)
2078 l->top = l->top->prev;
2079 l->pos = l->count - 1;
2080 return 1;
2082 case XCTRL('p'):
2083 case KEY_UP:
2084 listbox_back (l);
2085 return 1;
2087 case XCTRL('n'):
2088 case KEY_DOWN:
2089 listbox_fwd (l);
2090 return 1;
2092 case KEY_NPAGE:
2093 case XCTRL('v'):
2094 for (i = 0; i < l->height-1; i++)
2095 j |= listbox_fwd (l);
2096 return j > 0;
2098 case KEY_PPAGE:
2099 case ALT('v'):
2100 for (i = 0; i < l->height-1; i++)
2101 j |= listbox_back (l);
2102 return j > 0;
2104 return 0;
2107 static int listbox_event (Gpm_Event *event, WListbox *l);
2108 static int
2109 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
2111 WLEntry *e;
2112 /* int selected_color; Never used */
2113 int ret_code;
2115 switch (msg){
2116 case WIDGET_INIT:
2117 return x_create_listbox (h, h->wdata, l);
2119 case WIDGET_HOTKEY:
2120 if ((e = listbox_check_hotkey (l, par)) != NULL){
2121 listbox_select_entry (l, e);
2123 /* Take the appropriate action */
2124 if (l->action == listbox_finish){
2125 l->widget.parent->running = 0;
2126 l->widget.parent->ret_value = B_ENTER;
2127 } else if (l->action == listbox_cback){
2128 if ((*l->cback)(l) == listbox_finish){
2129 l->widget.parent->running = 0;
2130 l->widget.parent->ret_value = B_ENTER;
2133 return 1;
2134 } else
2135 return 0;
2137 case WIDGET_KEY:
2138 if ((ret_code = listbox_key (l, par)))
2139 listbox_draw (l, h, 1);
2140 return ret_code;
2142 #ifndef HAVE_X
2143 case WIDGET_CURSOR:
2144 widget_move (&l->widget, l->cursor_y, 0);
2145 return 1;
2147 case WIDGET_FOCUS:
2148 case WIDGET_UNFOCUS:
2149 case WIDGET_DRAW:
2150 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2151 return 1;
2152 #endif
2154 return default_proc (h, msg, par);
2157 static int
2158 listbox_event (Gpm_Event *event, WListbox *l)
2160 #ifndef HAVE_X
2161 int i;
2163 Dlg_head *h = l->widget.parent;
2165 /* Single click */
2166 if (event->type & GPM_DOWN)
2167 dlg_select_widget (l->widget.parent, l);
2168 if (!l->list)
2169 return MOU_NORMAL;
2170 if (event->type & (GPM_DOWN|GPM_DRAG)){
2171 if (event->x < 0 || event->x >= l->width)
2172 return MOU_REPEAT;
2173 if (event->y < 1)
2174 for (i = -event->y; i >= 0; i--)
2175 listbox_back (l);
2176 else if (event->y > l->height)
2177 for (i = event->y - l->height; i > 0; i--)
2178 listbox_fwd (l);
2179 else
2180 listbox_select_entry (l, listbox_select_pos (l, l->top,
2181 event->y - 1));
2183 /* We need to refresh ourselves since the dialog manager doesn't */
2184 /* know about this event */
2185 listbox_callback (h, l, WIDGET_DRAW, 0);
2186 mc_refresh ();
2187 return MOU_REPEAT;
2190 /* Double click */
2191 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2192 if (event->x < 0 || event->x >= l->width)
2193 return MOU_NORMAL;
2194 if (event->y < 1 || event->y > l->height)
2195 return MOU_NORMAL;
2197 dlg_select_widget (l->widget.parent, l);
2198 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2200 switch (l->action){
2201 case listbox_nothing:
2202 break;
2204 case listbox_finish:
2205 h->ret_value = B_ENTER;
2206 dlg_stop (h);
2207 return MOU_ENDLOOP;
2209 case listbox_cback:
2210 if ((*l->cback)(l) == listbox_finish)
2211 return MOU_ENDLOOP;
2214 #endif
2215 return MOU_NORMAL;
2218 static void
2219 listbox_destroy (WListbox *l)
2221 WLEntry *n, *p = l->list;
2222 int i;
2224 x_destroy_cmd (l);
2225 for (i = 0; i < l->count; i++){
2226 n = p->next;
2227 g_free (p->text);
2228 g_free (p);
2229 p = n;
2233 WListbox *
2234 listbox_new (int y, int x, int width, int height,
2235 int action, lcback callback, char *tkname)
2237 WListbox *l = g_new (WListbox, 1);
2238 extern int slow_terminal;
2240 init_widget (&l->widget, y, x, height, width,
2241 (callback_fn)listbox_callback,
2242 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2244 l->list = l->top = l->current = 0;
2245 l->pos = 0;
2246 l->width = width;
2247 if (height <= 0)
2248 l->height = 1;
2249 else
2250 l->height = height;
2251 l->count = 0;
2252 l->top = 0;
2253 l->current= 0;
2254 l->cback = callback;
2255 l->action = action;
2256 l->allow_duplicates = 1;
2257 l->scrollbar = slow_terminal ? 0 : 1;
2258 widget_want_hotkey (l->widget, 1);
2260 return l;
2263 /* Listbox item adding function. They still lack a lot of functionality */
2264 /* any takers? */
2265 /* 1.11.96 bor: added pos argument to control placement of new entry */
2266 static void
2267 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2269 if (!l->list){
2270 l->list = e;
2271 l->top = e;
2272 l->current = e;
2273 e->next = l->list;
2274 e->prev = l->list;
2275 } else if (pos == LISTBOX_APPEND_AT_END) {
2276 e->next = l->list;
2277 e->prev = l->list->prev;
2278 l->list->prev->next = e;
2279 l->list->prev = e;
2280 } else if (pos == LISTBOX_APPEND_BEFORE){
2281 e->next = l->current;
2282 e->prev = l->current->prev;
2283 l->current->prev->next = e;
2284 l->current->prev = e;
2285 if (l->list == l->current) { /* move list one position down */
2286 l->list = e;
2287 l->top = e;
2289 } else if (pos == LISTBOX_APPEND_AFTER) {
2290 e->prev = l->current;
2291 e->next = l->current->next;
2292 l->current->next->prev = e;
2293 l->current->next = e;
2295 x_list_insert (l, l->list, e);
2296 l->count++;
2299 char *
2300 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2301 void *data)
2303 WLEntry *entry;
2305 if (!l)
2306 return 0;
2308 if (!l->allow_duplicates)
2309 if (listbox_search_text (l, text))
2310 return 0;
2312 entry = g_new (WLEntry, 1);
2313 entry->text = g_strdup (text);
2314 entry->data = data;
2315 entry->hotkey = hotkey;
2317 listbox_append_item (l, entry, pos);
2319 return entry->text;
2322 /* Selects the nth entry in the listbox */
2323 void
2324 listbox_select_by_number (WListbox *l, int n)
2326 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2329 WLEntry *
2330 listbox_search_text (WListbox *l, char *text)
2332 WLEntry *e;
2334 e = l->list;
2335 if (!e)
2336 return NULL;
2338 do {
2339 if(!strcmp (e->text, text))
2340 return e;
2341 e = e->next;
2342 } while (e!=l->list);
2344 return NULL;
2347 /* Returns the current string text as well as the associated extra data */
2348 void
2349 listbox_get_current (WListbox *l, char **string, char **extra)
2351 if (!l->current){
2352 *string = 0;
2353 *extra = 0;
2355 if (string && l->current)
2356 *string = l->current->text;
2357 if (extra && l->current)
2358 *extra = l->current->data;
2361 static int
2362 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2364 int i;
2366 switch (msg){
2367 case WIDGET_INIT:
2368 return x_create_buttonbar (h, h->wdata, bb);
2370 case WIDGET_FOCUS:
2371 return 0;
2373 case WIDGET_HOTKEY:
2374 for (i = 0; i < 10; i++){
2375 if (par == KEY_F(i+1) && bb->labels [i].function){
2376 (*bb->labels [i].function)(bb->labels [i].data);
2377 return 1;
2380 return 0;
2382 #ifndef HAVE_X
2383 case WIDGET_DRAW:
2384 if (!bb->visible)
2385 return 1;
2386 widget_move (&bb->widget, 0, 0);
2387 attrset (DEFAULT_COLOR);
2388 printw ("%-*s", bb->widget.cols - 1, "");
2389 for (i = 0; i < COLS/8 && i < 10; i++){
2390 widget_move (&bb->widget, 0, i*8);
2391 attrset (DEFAULT_COLOR);
2392 printw ("%d", i+1);
2393 attrset (SELECTED_COLOR);
2394 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2395 bb->labels [i].text ? bb->labels [i].text : "");
2396 attrset (DEFAULT_COLOR);
2398 attrset (SELECTED_COLOR);
2399 return 1;
2400 #endif
2402 return default_proc (h, msg, par);
2405 static void
2406 buttonbar_destroy (WButtonBar *bb)
2408 int i;
2410 for (i = 0; i < 10; i++){
2411 if (bb->labels [i].text)
2412 g_free (bb->labels [i].text);
2416 static int
2417 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2419 #ifndef HAVE_X
2420 int button;
2422 if (!(event->type & GPM_UP))
2423 return MOU_NORMAL;
2424 if (event->y == 2)
2425 return MOU_NORMAL;
2426 button = event->x / 8;
2427 if (button < 10 && bb->labels [button].function)
2428 (*bb->labels [button].function)(bb->labels [button].data);
2429 #endif
2430 return MOU_NORMAL;
2433 WButtonBar *
2434 buttonbar_new (int visible)
2436 int i;
2437 WButtonBar *bb = g_new (WButtonBar, 1);
2439 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2440 (callback_fn) buttonbar_callback,
2441 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2443 bb->visible = visible;
2444 for (i = 0; i < 10; i++){
2445 bb->labels [i].text = 0;
2446 bb->labels [i].function = 0;
2448 widget_want_hotkey (bb->widget, 1);
2449 widget_want_cursor (bb->widget, 0);
2451 return bb;
2454 void
2455 set_label_text (WButtonBar *bb, int index, char *text)
2457 if (bb->labels [index-1].text)
2458 g_free (bb->labels [index-1].text);
2460 bb->labels [index-1].text = g_strdup (text);
2463 /* paneletc is either the panel widget, or info or view or tree widget */
2464 WButtonBar *
2465 find_buttonbar (Dlg_head *h, Widget *paneletc)
2467 WButtonBar *bb;
2468 Widget_Item *item;
2469 int i;
2471 bb = 0;
2472 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2473 if (item->widget->callback == (callback_fn) buttonbar_callback){
2474 bb = (WButtonBar *) item->widget;
2475 break;
2478 return bb;
2481 void
2482 define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
2483 buttonbarfn cback, void *data)
2485 WButtonBar *bb = find_buttonbar (h, paneletc);
2486 if (!bb)
2487 return;
2489 set_label_text (bb, idx, text);
2490 bb->labels [idx-1].function = (void (*)(void *)) cback;
2491 bb->labels [idx-1].data = data;
2492 x_redefine_label (bb, idx);
2495 void
2496 define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
2498 define_label_data (h, paneletc, idx, text, (void (*)(void *)) cback, 0);
2501 #ifdef HAVE_X
2502 void redraw_labels (Dlg_head *h, Widget *paneletc)
2506 #else
2507 void
2508 redraw_labels (Dlg_head *h, Widget *paneletc)
2510 Widget_Item *item;
2511 int i;
2513 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2514 if (item->widget->callback == (callback_fn) buttonbar_callback){
2515 widget_redraw (h, item);
2516 return;
2520 #endif