* widget.h: Use type "buttonbarfn" more consistently.
[midnight-commander.git] / src / widget.c
blob29e1d249752f83ab51898f52e61486eea90612ec
1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996 the Free Software Foundation
5 Authors: 1994, 1995 Radek Doulik
6 1994, 1995 Miguel de Icaza
7 1995 Jakub Jelinek
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <config.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <ctype.h>
35 #include "global.h"
36 #include "tty.h"
37 #include "color.h"
38 #include "mouse.h"
39 #include "dlg.h"
40 #include "widget.h"
41 #include "win.h"
42 #include "complete.h"
43 #include "key.h" /* XCTRL and ALT macros */
44 #include "profile.h" /* for history loading and saving */
46 #define x_create_button(a,b,c) 1
47 #define x_create_radio(a,b,c) 1
48 #define x_create_check(a,b,c) 1
49 #define x_create_label(a,b,c) 1
50 #define x_create_input(a,b,c) 1
51 #define x_create_listbox(a,b,c) 1
52 #define x_create_buttonbar(a,b,c) 1
53 #define x_create_gauge(a,b,c) 1
54 #define x_listbox_select_nth(a,b)
55 #define x_list_insert(a,b,c)
56 #define x_redefine_label(a,b)
58 #define x_destroy_cmd(w)
59 #define x_radio_focus_item(r)
60 #define x_radio_toggle(r)
62 static int button_event (Gpm_Event *event, WButton *b);
64 int quote = 0;
66 static int
67 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
69 char buf[BUF_SMALL];
70 int stop = 0;
71 int off = 0;
73 switch (Msg){
74 case WIDGET_INIT:
75 return x_create_button (h, h->wdata, b);
77 case WIDGET_HOTKEY:
78 if (b->hotkey == Par || toupper(b->hotkey) == Par){
79 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
80 return 1;
81 } else
82 return 0;
84 case WIDGET_KEY:
85 if (Par != ' ' && Par != '\n')
86 break;
88 if (b->callback)
89 stop = (*b->callback)(b->action, b->callback_data);
90 if (!b->callback || stop){
91 h->ret_value = b->action;
92 dlg_stop (h);
94 return 1;
96 case WIDGET_CURSOR:
97 switch (b->flags) {
98 case DEFPUSH_BUTTON:
99 off = 3;
100 break;
101 case NORMAL_BUTTON:
102 off = 2;
103 break;
104 case NARROW_BUTTON:
105 off = 1;
106 break;
107 case HIDDEN_BUTTON:
108 default:
109 off = 0;
110 break;
112 widget_move (&b->widget, 0, b->hotpos + off);
113 return 1;
115 case WIDGET_UNFOCUS:
116 case WIDGET_FOCUS:
117 case WIDGET_DRAW:
118 if (Msg==WIDGET_UNFOCUS)
119 b->selected = 0;
120 else if (Msg==WIDGET_FOCUS)
121 b->selected = 1;
123 switch (b->flags){
124 case DEFPUSH_BUTTON:
125 g_snprintf (buf, sizeof(buf), "[< %s >]", b->text);
126 off = 3;
127 break;
128 case NORMAL_BUTTON:
129 g_snprintf (buf, sizeof(buf), "[ %s ]", b->text);
130 off = 2;
131 break;
132 case NARROW_BUTTON:
133 g_snprintf (buf, sizeof(buf), "[%s]", b->text);
134 off = 1;
135 break;
136 case HIDDEN_BUTTON:
137 default:
138 buf[0] = '\0';
139 off = 0;
140 break;
143 attrset ((b->selected) ? FOCUSC : NORMALC);
144 widget_move (&b->widget, 0, 0);
146 addstr (buf);
148 if (b->hotpos >= 0){
149 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
150 widget_move (&b->widget, 0, b->hotpos+off);
151 addch ((unsigned char)b->text [b->hotpos]);
153 if (Msg == WIDGET_FOCUS)
154 break;
155 else
156 return 1;
157 break;
159 return default_proc (h, Msg, Par);
162 static int
163 button_event (Gpm_Event *event, WButton *b)
165 if (event->type & (GPM_DOWN|GPM_UP)){
166 Dlg_head *h=b->widget.parent;
167 dlg_select_widget (h, b);
168 if (event->type & GPM_UP){
169 button_callback (h, b, WIDGET_KEY, ' ');
170 (*h->callback) (h, ' ', DLG_POST_KEY);
171 return MOU_NORMAL;
174 return MOU_NORMAL;
177 static void
178 button_destroy (WButton *b)
180 x_destroy_cmd (b);
181 g_free (b->text);
184 static int
185 button_len (const char *text, unsigned int flags)
187 int ret = strlen (text);
188 switch (flags){
189 case DEFPUSH_BUTTON:
190 ret += 6;
191 break;
192 case NORMAL_BUTTON:
193 ret += 4;
194 break;
195 case NARROW_BUTTON:
196 ret += 2;
197 break;
198 case HIDDEN_BUTTON:
199 default:
200 return 0;
202 return ret;
206 * Assuming that button text is malloc'ed, we may safely change it
207 * (as opposed to statically allocated); from other hand, excluding &
208 * and shifting data past it to the left results to one unused byte.
209 * This does not harm though :)
211 static void
212 button_scan_hotkey(WButton* b)
214 char* cp = strchr (b->text, '&');
216 if (cp != NULL && cp[1] != '\0'){
217 strcpy (cp, cp+1);
218 b->hotkey = tolower (*cp);
219 b->hotpos = cp - b->text;
223 WButton *
224 button_new (int y, int x, int action, int flags, char *text,
225 int (*callback)(int, void *), void *callback_data, char *tkname)
227 WButton *b = g_new (WButton, 1);
229 init_widget (&b->widget, y, x, 1, button_len (text, flags),
230 (callback_fn) button_callback,
231 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
233 b->action = action;
234 b->flags = flags;
235 b->selected = 0;
236 b->text = g_strdup (text);
237 b->callback = callback;
238 b->callback_data = callback_data;
239 widget_want_hotkey (b->widget, 1);
240 b->hotkey = 0;
241 b->hotpos = -1;
243 button_scan_hotkey(b);
244 return b;
247 void
248 button_set_text (WButton *b, char *text)
250 g_free (b->text);
251 b->text = g_strdup (text);
252 b->widget.cols = button_len (text, b->flags);
253 button_scan_hotkey(b);
254 dlg_redraw (b->widget.parent);
258 /* Radio button widget */
259 static int radio_event (Gpm_Event *event, WRadio *r);
261 static int
262 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
264 int i;
266 switch (Msg) {
267 case WIDGET_INIT:
268 return x_create_radio (h, h->wdata, r);
270 case WIDGET_HOTKEY:
272 int i, lp = tolower(Par);
273 char *cp;
275 for (i = 0; i < r->count; i++){
276 cp = strchr (r->texts [i], '&');
277 if (cp != NULL && cp[1] != '\0'){
278 int c = tolower (cp [1]);
280 if (c != lp)
281 continue;
282 r->pos = i;
283 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
284 return 1;
288 return 0;
290 case WIDGET_KEY:
291 switch (Par){
292 case ' ':
293 r->sel = r->pos;
294 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
295 radio_callback (h, r, WIDGET_FOCUS, ' ');
296 x_radio_toggle (r);
297 return 1;
299 case KEY_UP:
300 case KEY_LEFT:
301 if (r->pos > 0){
302 r->pos--;
303 x_radio_focus_item (r);
304 return 1;
306 return 0;
308 case KEY_DOWN:
309 case KEY_RIGHT:
310 if (r->count - 1 > r->pos) {
311 r->pos++;
312 x_radio_focus_item (r);
313 return 1;
316 return 0;
318 case WIDGET_CURSOR:
319 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
320 radio_callback (h, r, WIDGET_FOCUS, ' ');
321 widget_move (&r->widget, r->pos, 1);
322 break;
324 case WIDGET_UNFOCUS:
325 case WIDGET_FOCUS:
326 case WIDGET_DRAW:
327 for (i = 0; i < r->count; i++){
328 register unsigned char* cp;
329 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
330 widget_move (&r->widget, i, 0);
332 printw("(%c) ", (r->sel == i) ? '*' : ' ');
333 for (cp = r->texts[i]; *cp; cp++)
335 if (*cp == '&')
337 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
338 ? HOT_FOCUSC : HOT_NORMALC);
339 addch(*++cp);
340 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
342 else
343 addch(*cp);
346 return 1;
347 break;
349 return default_proc (h, Msg, Par);
352 static int
353 radio_event (Gpm_Event *event, WRadio *r)
355 if (event->type & (GPM_DOWN|GPM_UP)){
356 Dlg_head *h = r->widget.parent;
358 r->pos = event->y - 1;
359 dlg_select_widget (h, r);
360 if (event->type & GPM_UP){
361 radio_callback (h, r, WIDGET_KEY, ' ');
362 radio_callback (h, r, WIDGET_FOCUS, 0);
363 (*h->callback) (h, ' ', DLG_POST_KEY);
364 return MOU_NORMAL;
367 return MOU_NORMAL;
370 WRadio *
371 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
373 WRadio *r = g_new (WRadio, 1);
374 int i, max, m;
376 /* Compute the longest string */
377 max = 0;
378 for (i = 0; i < count; i++){
379 m = strlen (texts [i]);
380 if (m > max)
381 max = m;
384 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
385 0, (mouse_h) radio_event, tkname);
386 r->state = 1;
387 r->pos = 0;
388 r->sel = 0;
389 r->count = count;
390 r->texts = texts;
391 r->upper_letter_is_hotkey = use_hotkey;
392 widget_want_hotkey (r->widget, 1);
394 return r;
398 /* Checkbutton widget */
400 static int check_event (Gpm_Event *event, WCheck *b);
402 static int
403 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
405 switch (Msg) {
406 case WIDGET_INIT:
407 return x_create_check (h, h->wdata, c);
409 case WIDGET_HOTKEY:
410 if (c->hotkey==Par ||
411 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
412 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
413 return 1;
414 } else
415 return 0;
417 case WIDGET_KEY:
418 if (Par != ' ')
419 break;
420 c->state ^= C_BOOL;
421 c->state ^= C_CHANGE;
422 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
423 check_callback (h, c, WIDGET_FOCUS, ' ');
424 return 1;
426 case WIDGET_CURSOR:
427 widget_move (&c->widget, 0, 1);
428 break;
430 case WIDGET_FOCUS:
431 case WIDGET_UNFOCUS:
432 case WIDGET_DRAW:
433 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
434 widget_move (&c->widget, 0, 0);
435 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
437 if (c->hotpos >= 0){
438 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
439 widget_move (&c->widget, 0, + c->hotpos+4);
440 addch ((unsigned char)c->text [c->hotpos]);
442 return 1;
444 return default_proc (h, Msg, Par);
447 static int
448 check_event (Gpm_Event *event, WCheck *c)
450 if (event->type & (GPM_DOWN|GPM_UP)){
451 Dlg_head *h = c->widget.parent;
453 dlg_select_widget (h, c);
454 if (event->type & GPM_UP){
455 check_callback (h, c, WIDGET_KEY, ' ');
456 check_callback (h, c, WIDGET_FOCUS, 0);
457 (*h->callback) (h, ' ', DLG_POST_KEY);
458 return MOU_NORMAL;
461 return MOU_NORMAL;
464 static void
465 check_destroy (WCheck *c)
467 x_destroy_cmd (c);
468 g_free (c->text);
471 WCheck *
472 check_new (int y, int x, int state, char *text, char *tkname)
474 WCheck *c = g_new (WCheck, 1);
475 char *s, *t;
477 init_widget (&c->widget, y, x, 1, strlen (text),
478 (callback_fn)check_callback,
479 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
480 c->state = state ? C_BOOL : 0;
481 c->text = g_strdup (text);
482 c->hotkey = 0;
483 c->hotpos = -1;
484 widget_want_hotkey (c->widget, 1);
486 /* Scan for the hotkey */
487 for (s = text, t = c->text; *s; s++, t++){
488 if (*s != '&'){
489 *t = *s;
490 continue;
492 s++;
493 if (*s){
494 c->hotkey = tolower (*s);
495 c->hotpos = t - c->text;
497 *t = *s;
499 *t = 0;
500 return c;
504 /* Label widget */
506 static int
507 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
509 if (Msg == WIDGET_INIT)
510 return x_create_label (h, h->wdata, l);
512 /* We don't want to get the focus */
513 if (Msg == WIDGET_FOCUS)
514 return 0;
515 if (Msg == WIDGET_DRAW && l->text){
516 char *p = l->text, *q, c = 0;
517 int y = 0;
518 if (l->transparent)
519 attrset (DEFAULT_COLOR);
520 else
521 attrset (NORMALC);
522 for (;;){
523 int xlen;
525 q = strchr (p, '\n');
526 if (q){
527 c = *q;
528 *q = 0;
530 widget_move (&l->widget, y, 0);
531 printw ("%s", p);
532 xlen = l->widget.cols - strlen (p);
533 if (xlen > 0)
534 printw ("%*s", xlen, " ");
535 if (!q)
536 break;
537 *q = c;
538 p = q + 1;
539 y++;
541 return 1;
543 return default_proc (h, Msg, Par);
546 void
547 label_set_text (WLabel *label, char *text)
549 int newcols = label->widget.cols;
551 if (label->text && text && !strcmp (label->text, text))
552 return; /* Flickering is not nice */
554 if (label->text){
555 g_free (label->text);
557 if (text){
558 label->text = g_strdup (text);
559 if (label->auto_adjust_cols) {
560 newcols = strlen (text);
561 if (newcols > label->widget.cols)
562 label->widget.cols = newcols;
564 } else
565 label->text = 0;
567 if (label->widget.parent)
568 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
570 if (newcols < label->widget.cols)
571 label->widget.cols = newcols;
574 static void
575 label_destroy (WLabel *l)
577 x_destroy_cmd (l);
578 if (l->text)
579 g_free (l->text);
582 WLabel *
583 label_new (int y, int x, const char *text, char *tkname)
585 WLabel *l;
586 int width;
588 /* Multiline labels are immutable - no need to compute their sizes */
589 if (!text || strchr(text, '\n'))
590 width = 1;
591 else
592 width = strlen (text);
594 l = g_new (WLabel, 1);
595 init_widget (&l->widget, y, x, 1, width,
596 (callback_fn) label_callback,
597 (destroy_fn) label_destroy, NULL, tkname);
598 l->text = text ? g_strdup (text) : 0;
599 l->auto_adjust_cols = 1;
600 l->transparent = 0;
601 widget_want_cursor (l->widget, 0);
602 return l;
606 /* Gauge widget (progress indicator) */
607 /* Currently width is hardcoded here for text mode */
608 #define gauge_len 47
610 static int
611 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
614 if (Msg == WIDGET_INIT)
615 return x_create_gauge (h, h->wdata, g);
617 /* We don't want to get the focus */
618 if (Msg == WIDGET_FOCUS)
619 return 0;
621 if (Msg == WIDGET_DRAW){
622 widget_move (&g->widget, 0, 0);
623 attrset (NORMALC);
624 if (!g->shown)
625 printw ("%*s", gauge_len, "");
626 else {
627 long percentage, columns;
628 long total = g->max, done = g->current;
630 if (total <= 0 || done < 0) {
631 done = 0;
632 total = 100;
634 if (done > total)
635 done = total;
636 while (total > 65535) {
637 total /= 256;
638 done /= 256;
640 percentage = (200 * done / total + 1) / 2;
641 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
642 addch ('[');
643 attrset (GAUGE_COLOR);
644 printw ("%*s", columns, "");
645 attrset (NORMALC);
646 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
648 return 1;
650 return default_proc (h, Msg, Par);
653 void
654 gauge_set_value (WGauge *g, int max, int current)
656 if (g->current == current && g->max == max)
657 return; /* Do not flicker */
658 if (max == 0)
659 max = 1; /* I do not like division by zero :) */
661 g->current = current;
662 g->max = max;
663 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
666 void
667 gauge_show (WGauge *g, int shown)
669 if (g->shown == shown)
670 return;
671 g->shown = shown;
672 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
675 static void
676 gauge_destroy (WGauge *g)
678 /* nothing */
681 WGauge *
682 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
684 WGauge *g = g_new (WGauge, 1);
686 init_widget (&g->widget, y, x, 1, gauge_len,
687 (callback_fn) gauge_callback,
688 (destroy_fn) gauge_destroy, NULL, tkname);
689 g->shown = shown;
690 if (max == 0)
691 max = 1; /* I do not like division by zero :) */
692 g->max = max;
693 g->current = current;
694 g->pixels = 0;
695 widget_want_cursor (g->widget, 0);
696 return g;
700 /* Input widget */
702 /* {{{ history button */
704 #define LARGE_HISTORY_BUTTON 1
706 #ifdef LARGE_HISTORY_BUTTON
707 # define HISTORY_BUTTON_WIDTH 3
708 #else
709 # define HISTORY_BUTTON_WIDTH 1
710 #endif
712 #define should_show_history_button(in) \
713 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
715 static void draw_history_button (WInput * in)
717 char c;
718 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
719 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
720 #ifdef LARGE_HISTORY_BUTTON
722 Dlg_head *h;
723 h = in->widget.parent;
724 #if 0
725 attrset (NORMALC); /* button has the same colour as other buttons */
726 addstr ("[ ]");
727 attrset (HOT_NORMALC);
728 #else
729 attrset (NORMAL_COLOR);
730 addstr ("[ ]");
731 /* Too distracting: attrset (MARKED_COLOR); */
732 #endif
733 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
734 addch (c);
736 #else
737 attrset (MARKED_COLOR);
738 addch (c);
739 #endif
742 /* }}} history button */
745 /* Input widgets now have a global kill ring */
746 /* Pointer to killed data */
747 static char *kill_buffer = 0;
749 void
750 update_input (WInput *in, int clear_first)
752 int has_history = 0;
753 int i, j;
754 unsigned char c;
755 int buf_len = strlen (in->buffer);
757 if (should_show_history_button (in))
758 has_history = HISTORY_BUTTON_WIDTH;
760 if (in->disable_update)
761 return;
763 /* Make the point visible */
764 if ((in->point < in->first_shown) ||
765 (in->point >= in->first_shown+in->field_len - has_history)){
766 in->first_shown = in->point - (in->field_len / 3);
767 if (in->first_shown < 0)
768 in->first_shown = 0;
771 /* Adjust the mark */
772 if (in->mark > buf_len)
773 in->mark = buf_len;
775 if (has_history)
776 draw_history_button (in);
778 attrset (in->color);
780 widget_move (&in->widget, 0, 0);
781 for (i = 0; i < in->field_len - has_history; i++)
782 addch (' ');
783 widget_move (&in->widget, 0, 0);
785 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
786 c = in->buffer [j++];
787 c = is_printable (c) ? c : '.';
788 if (in->is_password)
789 c = '*';
790 addch (c);
792 widget_move (&in->widget, 0, in->point - in->first_shown);
794 if (clear_first)
795 in->first = 0;
798 void
799 winput_set_origin (WInput *in, int x, int field_len)
801 in->widget.x = x;
802 in->field_len = in->widget.cols = field_len;
803 update_input (in, 0);
806 /* {{{ history saving and loading */
809 This loads and saves the history of an input line to and from the
810 widget. It is called with the widgets tk name on creation of the
811 widget, and returns the Hist list. It stores histories in the file
812 ~/.mc/history in using the profile code.
814 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
815 function) then input_new assigns the default text to be the last text
816 entered, or "" if not found.
819 int num_history_items_recorded = 60;
821 Hist *history_get (char *input_name)
823 int i;
824 Hist *old, *new;
825 char *profile;
827 old = new = NULL;
829 if (!num_history_items_recorded) /* this is how to disable */
830 return 0;
831 if (!input_name)
832 return 0;
833 if (!*input_name)
834 return 0;
835 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
836 for (i = 0;; i++) {
837 char key_name[BUF_TINY];
838 char this_entry[BUF_LARGE];
839 g_snprintf (key_name, sizeof (key_name), "%d", i);
840 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
841 if (!*this_entry)
842 break;
843 new = g_new0 (Hist, 1);
844 new->text = g_strdup (this_entry);
845 new->prev = old; /* set up list pointers */
846 if (old)
847 old->next = new;
848 old = new;
850 g_free (profile);
851 return new; /* return pointer to last entry in list */
854 void history_put (char *input_name, Hist *h)
856 int i;
857 char *profile;
859 if (!input_name)
860 return;
862 if (!*input_name)
863 return;
865 if (!h)
866 return;
868 if (!num_history_items_recorded) /* this is how to disable */
869 return;
871 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
873 if ((i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) != -1)
874 close (i);
875 /* Just in case I forgot to strip passwords somewhere -- Norbert */
876 if (chmod (profile, S_IRUSR | S_IWUSR) == -1 && errno != ENOENT){
877 g_free (profile);
878 return;
881 while (h->next) /* go to end of list */
882 h = h->next;
884 /* go back 60 places */
885 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
886 h = h->prev;
888 if (input_name)
889 profile_clean_section (input_name, profile);
891 /* dump histories into profile */
892 for (i = 0; h; h = h->next){
893 if (h->text){
895 /* probably aren't any null entries, but lets be sure */
896 if (*(h->text)){
897 char key_name[BUF_TINY];
898 g_snprintf (key_name, sizeof(key_name), "%d", i++);
899 WritePrivateProfileString (input_name, key_name, h->text, profile);
903 g_free (profile);
906 /* }}} history saving and loading */
909 /* {{{ history display */
911 static char *
912 i18n_htitle (void)
914 static char *history_title = NULL;
916 if (history_title == NULL)
917 history_title = _(" History ");
918 return history_title;
921 static int
922 history_callback (Dlg_head * h, int Par, int Msg)
924 switch (Msg) {
925 case DLG_DRAW:
926 attrset (COLOR_NORMAL);
927 dlg_erase (h);
928 draw_box (h, 0, 0, h->lines, h->cols);
929 attrset (COLOR_HOT_NORMAL);
930 dlg_move (h, 0, (h->cols - strlen (i18n_htitle())) / 2);
931 printw (i18n_htitle());
932 break;
934 return 0;
937 static inline int listbox_fwd (WListbox *l);
939 char *show_hist (Hist *history, int widget_x, int widget_y)
941 Hist *hi, *z;
942 size_t maxlen = strlen (i18n_htitle()), i, count = 0;
943 int x, y, w, h;
944 char *q, *r = 0;
945 Dlg_head *query_dlg;
946 WListbox *query_list;
948 z = history;
949 if (!z)
950 return 0;
952 while (z->prev) /* goto first */
953 z = z->prev;
954 hi = z;
955 while (hi) {
956 if ((i = strlen (hi->text)) > maxlen)
957 maxlen = i;
958 count++;
959 hi = hi->next;
962 y = widget_y;
963 h = count + 2;
964 if (h <= y || y > LINES - 6)
966 h = min(h, y - 1);
967 y -= h;
969 else
971 y++;
972 h = min(h, LINES - y);
975 if (widget_x > 2)
976 x = widget_x - 2;
977 else
978 x = 0;
979 if ((w = maxlen + 4) + x > COLS)
981 w = min(w,COLS);
982 x = COLS - w;
985 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
986 "[History-query]", "history", DLG_NONE);
987 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
988 add_widget (query_dlg, query_list);
989 hi = z;
990 if (y < widget_y) {
991 while (hi) { /* traverse */
992 listbox_add_item (query_list, 0, 0, hi->text, NULL);
993 hi = hi->next;
995 while (listbox_fwd (query_list));
996 } else {
997 while (hi->next)
998 hi = hi->next;
999 while (hi) { /* traverse backwards */
1000 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1001 hi = hi->prev;
1004 run_dlg (query_dlg);
1005 q = NULL;
1006 if (query_dlg->ret_value != B_CANCEL) {
1007 listbox_get_current (query_list, &q, NULL);
1008 if (q)
1009 r = g_strdup (q);
1011 destroy_dlg (query_dlg);
1012 return r;
1015 static void do_show_hist (WInput * in)
1017 char *r;
1018 r = show_hist (in->history, in->widget.x, in->widget.y);
1019 if (r) {
1020 assign_text (in, r);
1021 g_free (r);
1025 /* }}} history display */
1027 static void
1028 input_destroy (WInput *in)
1030 if (!in){
1031 fprintf (stderr, "Internal error: null Input *\n");
1032 exit (1);
1035 new_input (in);
1036 if (in->history){
1037 Hist *current, *old;
1039 if (!in->is_password) /* don't save passwords ;-) */
1040 history_put (in->history_name, in->history);
1042 current = in->history;
1043 while (current->next)
1044 current = current->next;
1045 while (current){
1046 old = current;
1047 current = current->prev;
1048 g_free (old->text);
1049 g_free (old);
1052 x_destroy_cmd (in);
1053 g_free (in->buffer);
1054 free_completions (in);
1055 if (in->history_name)
1056 g_free (in->history_name);
1059 static char disable_update = 0;
1061 void
1062 input_disable_update (WInput *in)
1064 in->disable_update++;
1067 void
1068 input_enable_update (WInput *in)
1070 in->disable_update--;
1071 update_input (in, 0);
1074 #define ELEMENTS(a) (sizeof(a)/sizeof(a[0]))
1077 push_history (WInput *in, char *text)
1079 static int i18n;
1080 /* input widget where urls with passwords are entered without any
1081 vfs prefix */
1082 static const char *password_input_fields[] = {
1083 N_(" Link to a remote machine "),
1084 N_(" FTP to machine "),
1085 N_(" SMB link to machine ")
1087 Hist *new;
1088 char *p;
1089 int i;
1091 if (!i18n) {
1092 i18n = 1;
1093 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1094 password_input_fields[i] = _(password_input_fields[i]);
1097 for (p = text; *p == ' ' || *p == '\t'; p++);
1098 if (!*p)
1099 return 0;
1100 if (in->history){
1101 while (in->history->next)
1102 in->history = in->history->next;
1103 if (!strcmp (in->history->text, text))
1104 return 1;
1105 new = g_new (Hist, 1);
1106 in->history->next = new;
1107 } else
1108 new = g_new (Hist, 1);
1109 in->need_push = 0;
1110 new->next = 0;
1111 new->prev = in->history;
1112 new->text = g_strdup (text);
1113 if (in->history_name) {
1114 p = in->history_name + 3;
1115 for (i = 0; i < ELEMENTS(password_input_fields); i++)
1116 if (strcmp (p, password_input_fields[i]) == 0)
1117 break;
1118 if (i < ELEMENTS(password_input_fields))
1119 strip_password (new->text, 0);
1120 else
1121 strip_password (new->text, 1);
1124 in->history = new;
1125 return 2;
1128 #undef ELEMENTS
1130 /* Cleans the input line and adds the current text to the history */
1131 void
1132 new_input (WInput *in)
1134 if (in->buffer)
1135 push_history (in, in->buffer);
1136 in->need_push = 1;
1137 in->buffer [0] = 0;
1138 in->point = 0;
1139 in->mark = 0;
1140 free_completions (in);
1141 update_input (in, 0);
1144 static int
1145 insert_char (WInput *in, int c_code)
1147 int i;
1149 if (c_code == -1)
1150 return 0;
1152 in->need_push = 1;
1153 if (strlen (in->buffer)+1 == in->current_max_len){
1154 /* Expand the buffer */
1155 char *narea = g_malloc (in->current_max_len + in->field_len);
1156 if (narea){
1157 char *p = in->buffer;
1159 strcpy (narea, in->buffer);
1160 in->buffer = narea;
1161 in->current_max_len += in->field_len;
1162 g_free (p);
1165 if (strlen (in->buffer)+1 < in->current_max_len){
1166 int l = strlen (&in->buffer [in->point]);
1167 for (i = l+1; i > 0; i--)
1168 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1169 in->buffer [in->point] = c_code;
1170 in->point++;
1172 return 1;
1175 static void
1176 beginning_of_line (WInput *in)
1178 in->point = 0;
1181 static void
1182 end_of_line (WInput *in)
1184 in->point = strlen (in->buffer);
1187 static void
1188 backward_char (WInput *in)
1190 if (in->point)
1191 in->point--;
1194 static void
1195 forward_char (WInput *in)
1197 if (in->buffer [in->point])
1198 in->point++;
1201 static void
1202 forward_word (WInput *in)
1204 unsigned char *p = in->buffer+in->point;
1206 while (*p && (isspace (*p) || ispunct (*p)))
1207 p++;
1208 while (*p && isalnum (*p))
1209 p++;
1210 in->point = p - in->buffer;
1213 static void
1214 backward_word (WInput *in)
1216 unsigned char *p = in->buffer+in->point;
1218 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1219 p--;
1220 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1221 p--;
1222 in->point = p - in->buffer;
1225 static void
1226 key_left (WInput *in)
1228 if (ctrl_pressed ())
1229 backward_word (in);
1230 else
1231 backward_char (in);
1234 static void
1235 key_right (WInput *in)
1237 if (ctrl_pressed ())
1238 forward_word (in);
1239 else
1240 forward_char (in);
1243 static void
1244 backward_delete (WInput *in)
1246 int i;
1248 if (!in->point)
1249 return;
1250 for (i = in->point; in->buffer [i-1]; i++)
1251 in->buffer [i-1] = in->buffer [i];
1252 in->need_push = 1;
1253 in->point--;
1256 static void
1257 delete_char (WInput *in)
1259 int i;
1261 for (i = in->point; in->buffer [i]; i++)
1262 in->buffer [i] = in->buffer [i+1];
1263 in->need_push = 1;
1266 static void
1267 copy_region (WInput *in, int x_first, int x_last)
1269 int first = min (x_first, x_last);
1270 int last = max (x_first, x_last);
1272 if (last == first)
1273 return;
1275 if (kill_buffer)
1276 g_free (kill_buffer);
1278 kill_buffer = g_malloc (last-first + 1);
1279 strncpy (kill_buffer, in->buffer+first, last-first);
1280 kill_buffer [last-first] = 0;
1283 static void
1284 delete_region (WInput *in, int x_first, int x_last)
1286 int first = min (x_first, x_last);
1287 int last = max (x_first, x_last);
1289 in->point = first;
1290 in->mark = first;
1291 strcpy (&in->buffer [first], &in->buffer [last]);
1292 in->need_push = 1;
1295 static void
1296 kill_word (WInput *in)
1298 int old_point = in->point;
1299 int new_point;
1301 forward_word (in);
1302 new_point = in->point;
1303 in->point = old_point;
1305 copy_region (in, old_point, new_point);
1306 delete_region (in, old_point, new_point);
1307 in->need_push = 1;
1310 static void
1311 back_kill_word (WInput *in)
1313 int old_point = in->point;
1314 int new_point;
1316 backward_word (in);
1317 new_point = in->point;
1318 in->point = old_point;
1320 copy_region (in, old_point, new_point);
1321 delete_region (in, old_point, new_point);
1322 in->need_push = 1;
1325 static void
1326 set_mark (WInput *in)
1328 in->mark = in->point;
1331 static void
1332 kill_save (WInput *in)
1334 copy_region (in, in->mark, in->point);
1337 static void
1338 kill_region (WInput *in)
1340 kill_save (in);
1341 delete_region (in, in->point, in->mark);
1344 static void
1345 yank (WInput *in)
1347 char *p;
1349 if (!kill_buffer)
1350 return;
1351 for (p = kill_buffer; *p; p++)
1352 insert_char (in, *p);
1355 static void
1356 kill_line (WInput *in)
1358 if (kill_buffer)
1359 g_free (kill_buffer);
1360 kill_buffer = g_strdup (&in->buffer [in->point]);
1361 in->buffer [in->point] = 0;
1364 void
1365 assign_text (WInput *in, char *text)
1367 free_completions (in);
1368 g_free (in->buffer);
1369 in->buffer = g_strdup (text); /* was in->buffer->text */
1370 in->current_max_len = strlen (in->buffer) + 1;
1371 in->point = strlen (in->buffer);
1372 in->mark = 0;
1373 in->need_push = 1;
1376 static void
1377 hist_prev (WInput *in)
1379 if (!in->history)
1380 return;
1382 if (in->need_push) {
1383 switch (push_history (in, in->buffer)) {
1384 case 2: in->history = in->history->prev; break;
1385 case 1: if (in->history->prev) in->history = in->history->prev; break;
1386 case 0: break;
1388 } else if (in->history->prev)
1389 in->history = in->history->prev;
1390 else
1391 return;
1392 assign_text (in, in->history->text);
1393 in->need_push = 0;
1396 static void
1397 hist_next (WInput *in)
1399 if (in->need_push) {
1400 switch (push_history (in, in->buffer)) {
1401 case 2:
1402 assign_text (in, "");
1403 return;
1404 case 0:
1405 return;
1409 if (!in->history)
1410 return;
1412 if (!in->history->next) {
1413 assign_text (in, "");
1414 return;
1417 in->history = in->history->next;
1418 assign_text (in, in->history->text);
1419 in->need_push = 0;
1422 static const struct {
1423 int key_code;
1424 void (*fn)(WInput *in);
1425 } input_map [] = {
1426 /* Motion */
1427 { XCTRL('a'), beginning_of_line },
1428 { KEY_HOME, beginning_of_line },
1429 { KEY_A1, beginning_of_line },
1430 { XCTRL('e'), end_of_line },
1431 { KEY_END, end_of_line },
1432 { KEY_C1, end_of_line },
1433 { KEY_LEFT, key_left },
1434 { XCTRL('b'), backward_char },
1435 { ALT('b'), backward_word },
1436 { KEY_RIGHT, key_right },
1437 { XCTRL('f'), forward_char },
1438 { ALT('f'), forward_word },
1440 /* Editing */
1441 { 0177, backward_delete },
1442 { KEY_BACKSPACE, backward_delete },
1443 { XCTRL('h'), backward_delete },
1444 { KEY_DC, delete_char },
1445 { XCTRL('d'), delete_char },
1446 { ALT('d'), kill_word },
1447 { ALT(KEY_BACKSPACE), back_kill_word },
1448 { ALT(XCTRL('h')), back_kill_word },
1449 { ALT(127), back_kill_word },
1451 /* Region manipulation */
1452 { 0, set_mark },
1453 { XCTRL('w'), kill_region },
1454 { ALT('w'), kill_save },
1455 { XCTRL('y'), yank },
1456 { XCTRL('k'), kill_line },
1458 /* History */
1459 { ALT('p'), hist_prev },
1460 { ALT('n'), hist_next },
1461 { ALT('h'), do_show_hist },
1463 /* Completion */
1464 { ALT('\t'), complete },
1466 { 0, 0 }
1469 /* This function is a test for a special input key used in complete.c */
1470 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1471 and 2 if it is a complete key */
1473 is_in_input_map (WInput *in, int c_code)
1475 int i;
1477 for (i = 0; input_map [i].fn; i++)
1478 if (c_code == input_map [i].key_code) {
1479 if (input_map [i].fn == complete)
1480 return 2;
1481 else
1482 return 1;
1484 return 0;
1487 static void
1488 port_region_marked_for_delete (WInput *in)
1490 *in->buffer = 0;
1491 in->point = 0;
1492 in->first = 0;
1496 handle_char (WInput *in, int c_code)
1498 int i;
1499 int v;
1501 v = 0;
1503 if (quote){
1504 free_completions (in);
1505 v = insert_char (in, c_code);
1506 update_input (in, 1);
1507 quote = 0;
1508 return v;
1511 for (i = 0; input_map [i].fn; i++){
1512 if (c_code == input_map [i].key_code){
1513 if (input_map [i].fn != complete)
1514 free_completions (in);
1515 (*input_map [i].fn)(in);
1516 v = 1;
1517 break;
1520 if (!input_map [i].fn){
1521 if (c_code > 255 || !is_printable (c_code))
1522 return 0;
1523 if (in->first){
1524 port_region_marked_for_delete (in);
1526 free_completions (in);
1527 v = insert_char (in, c_code);
1528 in->inserted_one = c_code;
1530 if (!disable_update)
1531 update_input (in, 1);
1532 return v;
1535 /* Inserts text in input line */
1536 void
1537 stuff (WInput *in, char *text, int insert_extra_space)
1539 input_disable_update (in);
1540 while (*text)
1541 handle_char (in, *text++);
1542 if (insert_extra_space)
1543 handle_char (in, ' ');
1544 input_enable_update (in);
1545 update_input (in, 1);
1548 void
1549 input_set_point (WInput *in, int pos)
1551 if (pos > in->current_max_len)
1552 pos = in->current_max_len;
1553 if (pos != in->point)
1554 free_completions (in);
1555 in->point = pos;
1556 update_input (in, 1);
1559 int input_event (Gpm_Event *event, WInput *b);
1561 static int
1562 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1564 switch (Msg){
1565 case WIDGET_INIT:
1566 return x_create_input (h, h->wdata, in);
1568 case WIDGET_KEY:
1569 if (Par == XCTRL('q')){
1570 int v;
1572 quote = 1;
1573 v = handle_char (in, mi_getch ());
1574 quote = 0;
1575 return v;
1577 if (Par == KEY_UP || Par == KEY_DOWN ||
1578 Par == ESC_CHAR || Par == KEY_F(10) ||
1579 Par == XCTRL('g'))
1580 return 0; /* We don't handle up/down */
1582 if (Par == '\n'){
1583 dlg_one_down (h);
1584 return 1;
1586 return handle_char (in, Par);
1588 case WIDGET_FOCUS:
1589 case WIDGET_UNFOCUS:
1590 case WIDGET_DRAW:
1591 update_input (in, 0);
1592 break;
1593 case WIDGET_CURSOR:
1594 widget_move (&in->widget, 0, in->point - in->first_shown);
1595 return 1;
1598 return default_proc (h, Msg, Par);
1601 /* Not declared static, since we check against this value in dlg.c */
1602 /* FIXME: Declare static again and provide an identification mechanism */
1603 int
1604 input_event (Gpm_Event *event, WInput *in)
1606 if (event->type & (GPM_DOWN|GPM_DRAG)){
1607 dlg_select_widget (in->widget.parent, in);
1609 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
1610 do_show_hist (in);
1611 } else {
1612 in->point = strlen (in->buffer);
1613 if (event->x - in->first_shown - 1 < in->point)
1614 in->point = event->x - in->first_shown - 1;
1615 if (in->point < 0)
1616 in->point = 0;
1618 update_input (in, 1);
1620 return MOU_NORMAL;
1623 WInput *
1624 input_new (int y, int x, int color, int len, const char *def_text, char *tkname)
1626 WInput *in = g_new (WInput, 1);
1627 int initial_buffer_len;
1629 init_widget (&in->widget, y, x, 1, len,
1630 (callback_fn) input_callback,
1631 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1633 /* history setup */
1634 in->history = NULL;
1635 in->history_name = 0;
1636 if (tkname) {
1637 if (*tkname) {
1638 in->history_name = g_strdup (tkname);
1639 in->history = history_get (tkname);
1643 if (!def_text)
1644 def_text = "";
1646 if (def_text == INPUT_LAST_TEXT) {
1647 def_text = "";
1648 if (in->history)
1649 if (in->history->text)
1650 def_text = in->history->text;
1652 initial_buffer_len = 1 + max (len, strlen (def_text));
1653 in->widget.options |= W_IS_INPUT;
1654 in->completions = NULL;
1655 in->completion_flags =
1656 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1657 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1658 in->current_max_len = initial_buffer_len;
1659 in->buffer = g_malloc (initial_buffer_len);
1660 in->color = color;
1661 in->field_len = len;
1662 in->first = 1;
1663 in->first_shown = 0;
1664 in->disable_update = 0;
1665 in->mark = 0;
1666 in->need_push = 1;
1667 in->is_password = 0;
1669 strcpy (in->buffer, def_text);
1670 in->point = strlen (in->buffer);
1671 return in;
1675 /* Listbox widget */
1677 /* Should draw the scrollbar, but currently draws only
1678 * indications that there is more information
1680 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1682 static void
1683 listbox_drawscroll (WListbox *l)
1685 extern int slow_terminal;
1686 int line;
1687 int i, top;
1688 int max_line = l->height-1;
1690 /* Are we at the top? */
1691 widget_move (&l->widget, 0, l->width);
1692 if (l->list == l->top)
1693 one_vline ();
1694 else
1695 addch ('^');
1697 /* Are we at the bottom? */
1698 widget_move (&l->widget, max_line, l->width);
1699 top = listbox_cdiff (l->list, l->top);
1700 if ((top + l->height == l->count) || l->height >= l->count)
1701 one_vline ();
1702 else
1703 addch ('v');
1705 /* Now draw the nice relative pointer */
1706 if (l->count)
1707 line = 1+ ((l->pos * (l->height-2)) / l->count);
1708 else
1709 line = 0;
1711 for (i = 1; i < max_line; i++){
1712 widget_move (&l->widget, i, l->width);
1713 if (i != line)
1714 one_vline ();
1715 else
1716 addch ('*');
1720 static void
1721 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1723 WLEntry *e;
1724 int i;
1725 int sel_line;
1726 int normalc = NORMALC;
1727 int selc;
1728 char *text;
1730 if (focused){
1731 selc = FOCUSC;
1732 } else {
1733 selc = HOT_FOCUSC;
1735 sel_line = -1;
1737 for (e = l->top, i = 0; (i < l->height); i++){
1739 /* Display the entry */
1740 if (e == l->current && sel_line == -1){
1741 sel_line = i;
1742 attrset (selc);
1743 } else
1744 attrset (normalc);
1746 widget_move (&l->widget, i, 0);
1748 if ((i > 0 && e == l->list) || !l->list)
1749 text = "";
1750 else {
1751 text = e->text;
1752 e = e->next;
1754 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1756 l->cursor_y = sel_line;
1757 if (!l->scrollbar)
1758 return;
1759 attrset (normalc);
1760 listbox_drawscroll (l);
1763 /* Returns the number of items between s and e,
1764 must be on the same linked list */
1765 static int
1766 listbox_cdiff (WLEntry *s, WLEntry *e)
1768 int count;
1770 for (count = 0; s != e; count++)
1771 s = s->next;
1772 return count;
1775 static WLEntry *
1776 listbox_check_hotkey (WListbox *l, int key)
1778 int i;
1779 WLEntry *e;
1781 i = 0;
1782 e = l->list;
1783 if (!e)
1784 return 0;
1786 while (1){
1788 /* If we didn't find anything, return */
1789 if (i && e == l->list)
1790 return 0;
1792 if (e->hotkey == key)
1793 return e;
1795 i++;
1796 e = e->next;
1800 /* Used only for display updating, for avoiding line at a time scroll */
1801 void
1802 listbox_select_last (WListbox *l, int set_top)
1804 if (l->list){
1805 l->current = l->list->prev;
1806 l->pos = l->count - 1;
1807 if (set_top)
1808 l->top = l->list->prev;
1809 x_listbox_select_nth (l, l->pos);
1813 void
1814 listbox_remove_list (WListbox *l)
1816 WLEntry *p, *q;
1818 if (!l->count)
1819 return;
1821 p = l->list;
1823 while (l->count--) {
1824 q = p->next;
1825 g_free (p->text);
1826 g_free (p);
1827 p = q;
1829 l->pos = l->count = 0;
1830 l->list = l->top = l->current = 0;
1834 * bor 30.10.96: added force flag to remove *last* entry as well
1835 * bor 30.10.96: corrected selection bug if last entry was removed
1838 void
1839 listbox_remove_current (WListbox *l, int force)
1841 WLEntry *p;
1843 /* Ok, note: this won't allow for emtpy lists */
1844 if (!force && (!l->count || l->count == 1))
1845 return;
1847 l->count--;
1848 p = l->current;
1850 if (l->count) {
1851 l->current->next->prev = l->current->prev;
1852 l->current->prev->next = l->current->next;
1853 if (p->next == l->list) {
1854 l->current = p->prev;
1855 l->pos--;
1857 else
1858 l->current = p->next;
1860 if (p == l->list)
1861 l->list = l->top = p->next;
1862 } else {
1863 l->pos = 0;
1864 l->list = l->top = l->current = 0;
1867 g_free (p->text);
1868 g_free (p);
1871 /* Makes *e the selected entry (sets current and pos) */
1872 void
1873 listbox_select_entry (WListbox *l, WLEntry *dest)
1875 WLEntry *e;
1876 int pos;
1877 int top_seen;
1879 top_seen = 0;
1881 /* Special case */
1882 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1884 if (e == l->top)
1885 top_seen = 1;
1887 if (e == dest){
1888 l->current = e;
1889 if (top_seen){
1890 while (listbox_cdiff (l->top, l->current) >= l->height)
1891 l->top = l->top->next;
1892 } else {
1893 l->top = l->current;
1895 l->pos = pos;
1896 x_listbox_select_nth (l, l->pos);
1897 return;
1900 /* If we are unable to find it, set decent values */
1901 l->current = l->top = l->list;
1902 l->pos = 0;
1903 x_listbox_select_nth (l, l->pos);
1906 /* Selects from base the pos element */
1907 static WLEntry *
1908 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1910 WLEntry *last = l->list->prev;
1912 if (base == last)
1913 return last;
1914 while (pos--){
1915 base = base->next;
1916 if (base == last)
1917 break;
1919 return base;
1922 static inline int
1923 listbox_back (WListbox *l)
1925 if (l->pos){
1926 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
1927 return 1;
1929 return 0;
1932 static inline int
1933 listbox_fwd (WListbox *l)
1935 if (l->current != l->list->prev){
1936 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
1937 return 1;
1939 return 0;
1942 /* Returns 1 if we want a redraw */
1943 static int
1944 listbox_key (WListbox *l, int key)
1946 int i;
1947 int j = 0;
1949 if (!l->list)
1950 return 0;
1952 switch (key){
1953 case KEY_HOME:
1954 case KEY_A1:
1955 l->current = l->top = l->list;
1956 l->pos = 0;
1957 return 1;
1959 case KEY_END:
1960 case KEY_C1:
1961 l->current = l->top = l->list->prev;
1962 for (i = min (l->height - 1, l->count - 1); i; i--)
1963 l->top = l->top->prev;
1964 l->pos = l->count - 1;
1965 return 1;
1967 case XCTRL('p'):
1968 case KEY_UP:
1969 listbox_back (l);
1970 return 1;
1972 case XCTRL('n'):
1973 case KEY_DOWN:
1974 listbox_fwd (l);
1975 return 1;
1977 case KEY_NPAGE:
1978 case XCTRL('v'):
1979 for (i = 0; i < l->height-1; i++)
1980 j |= listbox_fwd (l);
1981 return j > 0;
1983 case KEY_PPAGE:
1984 case ALT('v'):
1985 for (i = 0; i < l->height-1; i++)
1986 j |= listbox_back (l);
1987 return j > 0;
1989 return 0;
1992 static int listbox_event (Gpm_Event *event, WListbox *l);
1993 static int
1994 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
1996 WLEntry *e;
1997 /* int selected_color; Never used */
1998 int ret_code;
2000 switch (msg){
2001 case WIDGET_INIT:
2002 return x_create_listbox (h, h->wdata, l);
2004 case WIDGET_HOTKEY:
2005 if ((e = listbox_check_hotkey (l, par)) != NULL){
2006 listbox_select_entry (l, e);
2008 /* Take the appropriate action */
2009 if (l->action == listbox_finish){
2010 l->widget.parent->running = 0;
2011 l->widget.parent->ret_value = B_ENTER;
2012 } else if (l->action == listbox_cback){
2013 if ((*l->cback)(l) == listbox_finish){
2014 l->widget.parent->running = 0;
2015 l->widget.parent->ret_value = B_ENTER;
2018 return 1;
2019 } else
2020 return 0;
2022 case WIDGET_KEY:
2023 if ((ret_code = listbox_key (l, par)))
2024 listbox_draw (l, h, 1);
2025 return ret_code;
2027 case WIDGET_CURSOR:
2028 widget_move (&l->widget, l->cursor_y, 0);
2029 return 1;
2031 case WIDGET_FOCUS:
2032 case WIDGET_UNFOCUS:
2033 case WIDGET_DRAW:
2034 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2035 return 1;
2037 return default_proc (h, msg, par);
2040 static int
2041 listbox_event (Gpm_Event *event, WListbox *l)
2043 int i;
2045 Dlg_head *h = l->widget.parent;
2047 /* Single click */
2048 if (event->type & GPM_DOWN)
2049 dlg_select_widget (l->widget.parent, l);
2050 if (!l->list)
2051 return MOU_NORMAL;
2052 if (event->type & (GPM_DOWN|GPM_DRAG)){
2053 if (event->x < 0 || event->x >= l->width)
2054 return MOU_REPEAT;
2055 if (event->y < 1)
2056 for (i = -event->y; i >= 0; i--)
2057 listbox_back (l);
2058 else if (event->y > l->height)
2059 for (i = event->y - l->height; i > 0; i--)
2060 listbox_fwd (l);
2061 else
2062 listbox_select_entry (l, listbox_select_pos (l, l->top,
2063 event->y - 1));
2065 /* We need to refresh ourselves since the dialog manager doesn't */
2066 /* know about this event */
2067 listbox_callback (h, l, WIDGET_DRAW, 0);
2068 mc_refresh ();
2069 return MOU_REPEAT;
2072 /* Double click */
2073 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2074 if (event->x < 0 || event->x >= l->width)
2075 return MOU_NORMAL;
2076 if (event->y < 1 || event->y > l->height)
2077 return MOU_NORMAL;
2079 dlg_select_widget (l->widget.parent, l);
2080 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2082 switch (l->action){
2083 case listbox_nothing:
2084 break;
2086 case listbox_finish:
2087 h->ret_value = B_ENTER;
2088 dlg_stop (h);
2089 return MOU_ENDLOOP;
2091 case listbox_cback:
2092 if ((*l->cback)(l) == listbox_finish)
2093 return MOU_ENDLOOP;
2096 return MOU_NORMAL;
2099 static void
2100 listbox_destroy (WListbox *l)
2102 WLEntry *n, *p = l->list;
2103 int i;
2105 x_destroy_cmd (l);
2106 for (i = 0; i < l->count; i++){
2107 n = p->next;
2108 g_free (p->text);
2109 g_free (p);
2110 p = n;
2114 WListbox *
2115 listbox_new (int y, int x, int width, int height,
2116 int action, lcback callback, char *tkname)
2118 WListbox *l = g_new (WListbox, 1);
2119 extern int slow_terminal;
2121 init_widget (&l->widget, y, x, height, width,
2122 (callback_fn)listbox_callback,
2123 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2125 l->list = l->top = l->current = 0;
2126 l->pos = 0;
2127 l->width = width;
2128 if (height <= 0)
2129 l->height = 1;
2130 else
2131 l->height = height;
2132 l->count = 0;
2133 l->top = 0;
2134 l->current= 0;
2135 l->cback = callback;
2136 l->action = action;
2137 l->allow_duplicates = 1;
2138 l->scrollbar = slow_terminal ? 0 : 1;
2139 widget_want_hotkey (l->widget, 1);
2141 return l;
2144 /* Listbox item adding function. They still lack a lot of functionality */
2145 /* any takers? */
2146 /* 1.11.96 bor: added pos argument to control placement of new entry */
2147 static void
2148 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2150 if (!l->list){
2151 l->list = e;
2152 l->top = e;
2153 l->current = e;
2154 e->next = l->list;
2155 e->prev = l->list;
2156 } else if (pos == LISTBOX_APPEND_AT_END) {
2157 e->next = l->list;
2158 e->prev = l->list->prev;
2159 l->list->prev->next = e;
2160 l->list->prev = e;
2161 } else if (pos == LISTBOX_APPEND_BEFORE){
2162 e->next = l->current;
2163 e->prev = l->current->prev;
2164 l->current->prev->next = e;
2165 l->current->prev = e;
2166 if (l->list == l->current) { /* move list one position down */
2167 l->list = e;
2168 l->top = e;
2170 } else if (pos == LISTBOX_APPEND_AFTER) {
2171 e->prev = l->current;
2172 e->next = l->current->next;
2173 l->current->next->prev = e;
2174 l->current->next = e;
2176 x_list_insert (l, l->list, e);
2177 l->count++;
2180 char *
2181 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2182 void *data)
2184 WLEntry *entry;
2186 if (!l)
2187 return 0;
2189 if (!l->allow_duplicates)
2190 if (listbox_search_text (l, text))
2191 return 0;
2193 entry = g_new (WLEntry, 1);
2194 entry->text = g_strdup (text);
2195 entry->data = data;
2196 entry->hotkey = hotkey;
2198 listbox_append_item (l, entry, pos);
2200 return entry->text;
2203 /* Selects the nth entry in the listbox */
2204 void
2205 listbox_select_by_number (WListbox *l, int n)
2207 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2210 WLEntry *
2211 listbox_search_text (WListbox *l, char *text)
2213 WLEntry *e;
2215 e = l->list;
2216 if (!e)
2217 return NULL;
2219 do {
2220 if(!strcmp (e->text, text))
2221 return e;
2222 e = e->next;
2223 } while (e!=l->list);
2225 return NULL;
2228 /* Returns the current string text as well as the associated extra data */
2229 void
2230 listbox_get_current (WListbox *l, char **string, char **extra)
2232 if (!l->current){
2233 *string = 0;
2234 *extra = 0;
2236 if (string && l->current)
2237 *string = l->current->text;
2238 if (extra && l->current)
2239 *extra = l->current->data;
2242 static int
2243 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2245 int i;
2247 switch (msg){
2248 case WIDGET_INIT:
2249 return x_create_buttonbar (h, h->wdata, bb);
2251 case WIDGET_FOCUS:
2252 return 0;
2254 case WIDGET_HOTKEY:
2255 for (i = 0; i < 10; i++){
2256 if (par == KEY_F(i+1) && bb->labels [i].function){
2257 (*bb->labels [i].function)(bb->labels [i].data);
2258 return 1;
2261 return 0;
2263 case WIDGET_DRAW:
2264 if (!bb->visible)
2265 return 1;
2266 widget_move (&bb->widget, 0, 0);
2267 attrset (DEFAULT_COLOR);
2268 printw ("%-*s", bb->widget.cols - 1, "");
2269 for (i = 0; i < COLS/8 && i < 10; i++){
2270 widget_move (&bb->widget, 0, i*8);
2271 attrset (DEFAULT_COLOR);
2272 printw ("%d", i+1);
2273 attrset (SELECTED_COLOR);
2274 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2275 bb->labels [i].text ? bb->labels [i].text : "");
2276 attrset (DEFAULT_COLOR);
2278 attrset (SELECTED_COLOR);
2279 return 1;
2281 return default_proc (h, msg, par);
2284 static void
2285 buttonbar_destroy (WButtonBar *bb)
2287 int i;
2289 for (i = 0; i < 10; i++){
2290 if (bb->labels [i].text)
2291 g_free (bb->labels [i].text);
2295 static int
2296 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2298 int button;
2300 if (!(event->type & GPM_UP))
2301 return MOU_NORMAL;
2302 if (event->y == 2)
2303 return MOU_NORMAL;
2304 button = event->x / 8;
2305 if (button < 10 && bb->labels [button].function)
2306 (*bb->labels [button].function)(bb->labels [button].data);
2307 return MOU_NORMAL;
2310 WButtonBar *
2311 buttonbar_new (int visible)
2313 int i;
2314 WButtonBar *bb = g_new (WButtonBar, 1);
2316 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2317 (callback_fn) buttonbar_callback,
2318 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2320 bb->visible = visible;
2321 for (i = 0; i < 10; i++){
2322 bb->labels [i].text = 0;
2323 bb->labels [i].function = 0;
2325 widget_want_hotkey (bb->widget, 1);
2326 widget_want_cursor (bb->widget, 0);
2328 return bb;
2331 void
2332 set_label_text (WButtonBar *bb, int index, char *text)
2334 if (bb->labels [index-1].text)
2335 g_free (bb->labels [index-1].text);
2337 bb->labels [index-1].text = g_strdup (text);
2340 /* paneletc is either the panel widget, or info or view or tree widget */
2341 WButtonBar *
2342 find_buttonbar (Dlg_head *h, Widget *paneletc)
2344 WButtonBar *bb;
2345 Widget_Item *item;
2346 int i;
2348 bb = 0;
2349 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2350 if (item->widget->callback == (callback_fn) buttonbar_callback){
2351 bb = (WButtonBar *) item->widget;
2352 break;
2355 return bb;
2358 void
2359 define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
2360 buttonbarfn cback, void *data)
2362 WButtonBar *bb = find_buttonbar (h, paneletc);
2363 if (!bb)
2364 return;
2366 set_label_text (bb, idx, text);
2367 bb->labels [idx-1].function = cback;
2368 bb->labels [idx-1].data = data;
2369 x_redefine_label (bb, idx);
2372 void
2373 define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
2375 define_label_data (h, paneletc, idx, text, (buttonbarfn) cback, 0);
2378 void
2379 redraw_labels (Dlg_head *h, Widget *paneletc)
2381 Widget_Item *item;
2382 int i;
2384 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2385 if (item->widget->callback == (callback_fn) buttonbar_callback){
2386 widget_redraw (h, item);
2387 return;