Fix umpnpmgr build
[reactos.git] / rosapps / mc / src / widget.c
1 /* Widgets for the Midnight Commander
2
3 Copyright (C) 1994, 1995, 1996 the Free Software Foundation
4
5 Authors: 1994, 1995 Radek Doulik
6 1994, 1995 Miguel de Icaza
7 1995 Jakub Jelinek
8 1996 Andrej Borsenkow
9 1997 Norbert Warmuth
10
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.
15
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.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24
25 */
26 /* "$Id$" */
27
28 #include <config.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <malloc.h>
32 #include "tty.h"
33 #include <ctype.h>
34 #include "mad.h"
35 #include "global.h"
36 #include "util.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 */
46
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
60
61 #ifndef PORT_HAS_DESTROY_CMD
62 # define x_destroy_cmd(w)
63 #endif
64
65 #ifndef PORT_HAS_RADIO_FOCUS_ITEM
66 # define x_radio_focus_item(r)
67 #endif
68
69 #ifndef PORT_HAS_RADIO_TOGGLE
70 # define x_radio_toggle
71 #endif
72
73 static int button_event (Gpm_Event *event, WButton *b);
74
75 int quote = 0;
76
77 static int
78 button_callback (Dlg_head *h, WButton *b, int Msg, int Par)
79 {
80 char *txt, buf[256];
81 int stop = 0;
82 int off = 0;
83
84 switch (Msg){
85 case WIDGET_INIT:
86 return x_create_button (h, h->wdata, b);
87 #ifndef HAVE_XVIEW
88 case WIDGET_HOTKEY:
89 if (b->hotkey == Par || toupper(b->hotkey) == Par){
90 button_callback (h, b, WIDGET_KEY, ' '); /* to make action */
91 return 1;
92 } else
93 return 0;
94
95 case WIDGET_KEY:
96 if (Par != ' ' && Par != '\n')
97 break;
98
99 if (b->callback)
100 stop = (*b->callback)(b->action, b->callback_data);
101 if (!b->callback || stop){
102 h->ret_value = b->action;
103 dlg_stop (h);
104 }
105 return 1;
106
107 #ifdef HAVE_TK
108 case WIDGET_FOCUS:
109 case WIDGET_CURSOR:
110 {
111 char *s = b->action == B_ENTER ? ".button" : "";
112
113 tk_evalf ("focus %s%s", (char *)(b->widget.wdata)+1, s);
114 /* Do not call default_proc: we did the tk focus command */
115 return 1;
116 }
117 #else
118
119 case WIDGET_CURSOR:
120 switch (b->flags) {
121 case DEFPUSH_BUTTON:
122 off = 3;
123 break;
124 case NORMAL_BUTTON:
125 off = 2;
126 break;
127 case NARROW_BUTTON:
128 off = 1;
129 break;
130 case HIDDEN_BUTTON:
131 default:
132 off = 0;
133 break;
134 }
135 widget_move (&b->widget, 0, b->hotpos + off);
136 return 1;
137
138 case WIDGET_UNFOCUS:
139 case WIDGET_FOCUS:
140 case WIDGET_DRAW:
141 if (Msg==WIDGET_UNFOCUS)
142 b->selected = 0;
143 else if (Msg==WIDGET_FOCUS)
144 b->selected = 1;
145
146 switch (b->flags){
147 case DEFPUSH_BUTTON:
148 sprintf (buf, "[< %s >]", b->text);
149 off = 3;
150 break;
151 case NORMAL_BUTTON:
152 sprintf (buf, "[ %s ]", b->text);
153 off = 2;
154 break;
155 case NARROW_BUTTON:
156 sprintf (buf, "[%s]", b->text);
157 off = 1;
158 break;
159 case HIDDEN_BUTTON:
160 default:
161 buf[0] = '\0';
162 off = 0;
163 break;
164 }
165 txt = buf;
166
167 attrset ((b->selected) ? FOCUSC : NORMALC);
168 widget_move (&b->widget, 0, 0);
169
170 addstr (txt);
171
172 if (b->hotpos >= 0){
173 attrset ((b->selected) ? HOT_FOCUSC : HOT_NORMALC);
174 widget_move (&b->widget, 0, b->hotpos+off);
175 addch ((unsigned char)b->text [b->hotpos]);
176 }
177 if (Msg == WIDGET_FOCUS)
178 break;
179 else
180 return 1;
181 break;
182 #endif
183 #endif /* !HAVE_XVIEW */
184 }
185 return default_proc (h, Msg, Par);
186 }
187
188 static int
189 button_event (Gpm_Event *event, WButton *b)
190 {
191 #ifndef HAVE_X
192 if (event->type & (GPM_DOWN|GPM_UP)){
193 Dlg_head *h=b->widget.parent;
194 dlg_select_widget (h, b);
195 if (event->type & GPM_UP){
196 button_callback (h, b, WIDGET_KEY, ' ');
197 (*h->callback) (h, ' ', DLG_POST_KEY);
198 return MOU_NORMAL;
199 }
200 }
201 #endif
202 return MOU_NORMAL;
203 }
204
205 static void
206 button_destroy (WButton *b)
207 {
208 x_destroy_cmd (b);
209 free (b->text);
210 }
211
212 static int
213 button_len (const char *text, unsigned int flags)
214 {
215 #ifndef HAVE_X
216 int ret = strlen (text);
217 switch (flags){
218 case DEFPUSH_BUTTON:
219 ret += 6;
220 break;
221 case NORMAL_BUTTON:
222 ret += 4;
223 break;
224 case NARROW_BUTTON:
225 ret += 2;
226 break;
227 case HIDDEN_BUTTON:
228 default:
229 return 0;
230 }
231 return ret;
232 #else
233 return strlen (text);
234 #endif
235 }
236
237 /*
238 * Assuming that button text is malloc'ed, we may safely change it
239 * (as opposed to statically allocated); from other hand, excluding &
240 * and shifting data past it to the left results to one unused byte.
241 * This does not harm though :)
242 */
243 void
244 button_scan_hotkey(WButton* b)
245 {
246 char* cp = strchr (b->text, '&');
247
248 if (cp != NULL && cp[1] != '\0'){
249 strcpy (cp, cp+1);
250 b->hotkey = tolower (*cp);
251 b->hotpos = cp - b->text;
252 }
253 }
254
255 WButton *
256 button_new (int y, int x, int action, int flags, char *text,
257 int (*callback)(int, void *), void *callback_data, char *tkname)
258 {
259 WButton *b = xmalloc (sizeof (WButton), "new_button");
260
261 init_widget (&b->widget, y, x, 1, button_len (text, flags),
262 (callback_fn) button_callback,
263 (destroy_fn) button_destroy, (mouse_h)button_event, tkname);
264
265 b->action = action;
266 b->flags = flags;
267 b->selected = 0;
268 b->text = strdup (text);
269 b->callback = callback;
270 b->callback_data = callback_data;
271 widget_want_hotkey (b->widget, 1);
272 b->hotkey = 0;
273 b->hotpos = -1;
274
275 button_scan_hotkey(b);
276 return b;
277 }
278
279 void
280 button_set_text (WButton *b, char *text)
281 {
282 free (b->text);
283 b->text = strdup (text);
284 b->widget.cols = button_len (text, b->flags);
285 button_scan_hotkey(b);
286 #ifdef HAVE_X
287 x_button_set (b, b->text);
288 #else
289 dlg_redraw (b->widget.parent);
290 #endif
291 }
292
293 \f
294 /* Radio button widget */
295 static int radio_event (Gpm_Event *event, WRadio *r);
296
297 static int
298 radio_callback (Dlg_head *h, WRadio *r, int Msg, int Par)
299 {
300 int i;
301
302 switch (Msg) {
303 case WIDGET_INIT:
304 return x_create_radio (h, h->wdata, r);
305
306 #ifndef HAVE_XVIEW
307 case WIDGET_HOTKEY:
308 {
309 int i, lp = tolower(Par);
310 char *cp;
311
312 for (i = 0; i < r->count; i++){
313 cp = strchr (r->texts [i], '&');
314 if (cp != NULL && cp[1] != '\0'){
315 int c = tolower (cp [1]);
316
317 if (c != lp)
318 continue;
319 r->pos = i;
320 radio_callback (h, r, WIDGET_KEY, ' '); /* Take action */
321 return 1;
322 }
323 }
324 }
325 return 0;
326
327 case WIDGET_KEY:
328 switch (Par){
329 case ' ':
330 r->sel = r->pos;
331 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
332 radio_callback (h, r, WIDGET_FOCUS, ' ');
333 x_radio_toggle (r);
334 return 1;
335
336 case KEY_UP:
337 case KEY_LEFT:
338 if (r->pos > 0){
339 r->pos--;
340 x_radio_focus_item (r);
341 return 1;
342 }
343 return 0;
344
345 case KEY_DOWN:
346 case KEY_RIGHT:
347 if (r->count - 1 > r->pos) {
348 r->pos++;
349 x_radio_focus_item (r);
350 return 1;
351 }
352 }
353 return 0;
354
355 #ifdef HAVE_X
356 case WIDGET_FOCUS:
357 case WIDGET_CURSOR:
358 x_radio_focus_item (r);
359 return 1;
360 #endif
361 #endif
362
363 #ifndef HAVE_X
364 case WIDGET_CURSOR:
365 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
366 radio_callback (h, r, WIDGET_FOCUS, ' ');
367 widget_move (&r->widget, r->pos, 1);
368 break;
369
370 case WIDGET_UNFOCUS:
371 case WIDGET_FOCUS:
372 case WIDGET_DRAW:
373 for (i = 0; i < r->count; i++){
374 register char* cp;
375 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC :NORMALC);
376 widget_move (&r->widget, i, 0);
377
378 printw("(%c) ", (r->sel == i) ? '*' : ' ');
379 for (cp = r->texts[i]; *cp; cp++)
380 {
381 if (*cp == '&')
382 {
383 attrset ((i==r->pos && Msg==WIDGET_FOCUS)
384 ? HOT_FOCUSC : HOT_NORMALC);
385 addch(*++cp);
386 attrset ((i==r->pos && Msg==WIDGET_FOCUS) ? FOCUSC : NORMALC);
387 }
388 else
389 addch(*cp);
390 }
391 }
392 return 1;
393 break;
394 #endif
395 }
396 return default_proc (h, Msg, Par);
397 }
398
399 #ifdef HAVE_TK
400 static void Radio_destroy (WRadio *r)
401 {
402 x_destroy_cmd (r);
403 }
404 # define radio_destroy (destroy_fn) Radio_destroy
405 #else
406 # define radio_destroy 0
407 #endif
408
409 static int
410 radio_event (Gpm_Event *event, WRadio *r)
411 {
412 #ifndef HAVE_X
413 if (event->type & (GPM_DOWN|GPM_UP)){
414 Dlg_head *h = r->widget.parent;
415
416 r->pos = event->y - 1;
417 dlg_select_widget (h, r);
418 if (event->type & GPM_UP){
419 radio_callback (h, r, WIDGET_KEY, ' ');
420 radio_callback (h, r, WIDGET_FOCUS, 0);
421 (*h->callback) (h, ' ', DLG_POST_KEY);
422 return MOU_NORMAL;
423 }
424 }
425 #endif
426 return MOU_NORMAL;
427 }
428
429 WRadio *
430 radio_new (int y, int x, int count, char **texts, int use_hotkey, char *tkname)
431 {
432 WRadio *r = xmalloc (sizeof (WRadio), "radio_new");
433 int i, max, m;
434
435 /* Compute the longest string */
436 max = 0;
437 for (i = 0; i < count; i++){
438 m = strlen (texts [i]);
439 if (m > max)
440 max = m;
441 }
442
443 init_widget (&r->widget, y, x, count, max, (callback_fn) radio_callback,
444 radio_destroy, (mouse_h) radio_event, tkname);
445 r->state = 1;
446 r->pos = 0;
447 r->sel = 0;
448 r->count = count;
449 r->texts = texts;
450 r->upper_letter_is_hotkey = use_hotkey;
451 widget_want_hotkey (r->widget, 1);
452
453 return r;
454 }
455
456 \f
457 /* Checkbutton widget */
458
459 static int check_event (Gpm_Event *event, WCheck *b);
460
461 static int
462 check_callback (Dlg_head *h, WCheck *c, int Msg, int Par)
463 {
464 switch (Msg) {
465 case WIDGET_INIT:
466 return x_create_check (h, h->wdata, c);
467
468 #ifndef HAVE_XVIEW
469 case WIDGET_HOTKEY:
470 if (c->hotkey==Par ||
471 (c->hotkey>='a' && c->hotkey<='z' && c->hotkey-32==Par)){
472 check_callback (h, c, WIDGET_KEY, ' '); /* make action */
473 return 1;
474 } else
475 return 0;
476
477 case WIDGET_KEY:
478 if (Par != ' ')
479 break;
480 c->state ^= C_BOOL;
481 c->state ^= C_CHANGE;
482 (*h->callback) (h, h->current->dlg_id, DLG_ACTION);
483 check_callback (h, c, WIDGET_FOCUS, ' ');
484 return 1;
485
486 #ifndef HAVE_X
487 case WIDGET_CURSOR:
488 widget_move (&c->widget, 0, 1);
489 break;
490
491 case WIDGET_FOCUS:
492 case WIDGET_UNFOCUS:
493 case WIDGET_DRAW:
494 attrset ((Msg == WIDGET_FOCUS) ? FOCUSC : NORMALC);
495 widget_move (&c->widget, 0, 0);
496 printw ("[%c] %s", (c->state & C_BOOL) ? 'x' : ' ', c->text);
497
498 if (c->hotpos >= 0){
499 attrset ((Msg == WIDGET_FOCUS) ? HOT_FOCUSC : HOT_NORMALC);
500 widget_move (&c->widget, 0, + c->hotpos+4);
501 addch ((unsigned char)c->text [c->hotpos]);
502 }
503 return 1;
504 #endif /* !HAVE_X */
505 #endif /* !HAVE_XVIEW */
506 }
507 return default_proc (h, Msg, Par);
508 }
509
510 static int
511 check_event (Gpm_Event *event, WCheck *c)
512 {
513 #ifndef HAVE_X
514 if (event->type & (GPM_DOWN|GPM_UP)){
515 Dlg_head *h = c->widget.parent;
516
517 dlg_select_widget (h, c);
518 if (event->type & GPM_UP){
519 check_callback (h, c, WIDGET_KEY, ' ');
520 check_callback (h, c, WIDGET_FOCUS, 0);
521 (*h->callback) (h, ' ', DLG_POST_KEY);
522 return MOU_NORMAL;
523 }
524 }
525 #endif
526 return MOU_NORMAL;
527 }
528
529 static void
530 check_destroy (WCheck *c)
531 {
532 x_destroy_cmd (c);
533 free (c->text);
534 }
535
536 WCheck *
537 check_new (int y, int x, int state, char *text, char *tkname)
538 {
539 WCheck *c = xmalloc (sizeof (WCheck), "check_new");
540 char *s, *t;
541
542 init_widget (&c->widget, y, x, 1, strlen (text),
543 (callback_fn)check_callback,
544 (destroy_fn)check_destroy, (mouse_h) check_event, tkname);
545 c->state = state ? C_BOOL : 0;
546 c->text = strdup (text);
547 c->hotkey = 0;
548 c->hotpos = -1;
549 widget_want_hotkey (c->widget, 1);
550
551 /* Scan for the hotkey */
552 for (s = text, t = c->text; *s; s++, t++){
553 if (*s != '&'){
554 *t = *s;
555 continue;
556 }
557 s++;
558 if (*s){
559 c->hotkey = tolower (*s);
560 c->hotpos = t - c->text;
561 }
562 *t = *s;
563 }
564 *t = 0;
565 return c;
566 }
567
568 \f
569 /* Label widget */
570
571 static int
572 label_callback (Dlg_head *h, WLabel *l, int Msg, int Par)
573 {
574 if (Msg == WIDGET_INIT)
575 return x_create_label (h, h->wdata, l);
576
577 /* We don't want to get the focus */
578 if (Msg == WIDGET_FOCUS)
579 return 0;
580 #ifndef HAVE_X
581 if (Msg == WIDGET_DRAW && l->text){
582 char *p = l->text, *q, c = 0;
583 int y = 0;
584 if (l->transparent)
585 attrset (DEFAULT_COLOR);
586 else
587 attrset (NORMALC);
588 for (;;){
589 int xlen;
590
591 q = strchr (p, '\n');
592 if (q){
593 c = *q;
594 *q = 0;
595 }
596 widget_move (&l->widget, y, 0);
597 printw ("%s", p);
598 xlen = l->widget.cols - strlen (p);
599 if (xlen > 0)
600 printw ("%*s", xlen, " ");
601 if (!q)
602 break;
603 *q = c;
604 p = q + 1;
605 y++;
606 }
607 return 1;
608 }
609 #endif
610 return default_proc (h, Msg, Par);
611 }
612
613 void
614 label_set_text (WLabel *label, char *text)
615 {
616 int newcols = label->widget.cols;
617
618 if (label->text && text && !strcmp (label->text, text))
619 return; /* Flickering is not nice */
620
621 if (label->text){
622 free (label->text);
623 }
624 if (text){
625 label->text = strdup (text);
626 if (label->auto_adjust_cols) {
627 newcols = strlen (text);
628 if (newcols > label->widget.cols)
629 label->widget.cols = newcols;
630 }
631 } else
632 label->text = 0;
633
634 if (label->widget.parent)
635 #ifdef HAVE_X
636 x_label_set_text (label, text);
637 #else
638 label_callback (label->widget.parent, label, WIDGET_DRAW, 0);
639 #endif
640 if (newcols < label->widget.cols)
641 label->widget.cols = newcols;
642 }
643
644 static void
645 label_destroy (WLabel *l)
646 {
647 x_destroy_cmd (l);
648 if (l->text)
649 free (l->text);
650 }
651
652 WLabel *
653 label_new (int y, int x, char *text, char *tkname)
654 {
655 WLabel *l = xmalloc (sizeof (WLabel), "label_new");
656
657 init_widget (&l->widget, y, x, 1, 1,
658 (callback_fn) label_callback,
659 (destroy_fn) label_destroy, NULL, tkname);
660 l->text = text ? strdup (text) : 0;
661 l->auto_adjust_cols = 1;
662 l->transparent = 0;
663 widget_want_cursor (l->widget, 0);
664 return l;
665 }
666
667 \f
668 /* Gauge widget (progress indicator) */
669 /* Currently width is hardcoded here for text mode */
670 #define gauge_len 47
671
672 static int
673 gauge_callback (Dlg_head *h, WGauge *g, int Msg, int Par)
674 {
675
676 if (Msg == WIDGET_INIT)
677 return x_create_gauge (h, h->wdata, g);
678
679 /* We don't want to get the focus */
680 if (Msg == WIDGET_FOCUS)
681 return 0;
682
683 #ifndef HAVE_X
684 if (Msg == WIDGET_DRAW){
685 widget_move (&g->widget, 0, 0);
686 attrset (NORMALC);
687 if (!g->shown)
688 printw ("%*s", gauge_len, "");
689 else {
690 long percentage, columns;
691 long total = g->max, done = g->current;
692
693 if (total <= 0 || done < 0) {
694 done = 0;
695 total = 100;
696 }
697 if (done > total)
698 done = total;
699 while (total > 65535) {
700 total /= 256;
701 done /= 256;
702 }
703 percentage = (200 * done / total + 1) / 2;
704 columns = (2 * (gauge_len - 7) * done / total + 1) / 2;
705 addch ('[');
706 attrset (GAUGE_COLOR);
707 printw ("%*s", columns, "");
708 attrset (NORMALC);
709 printw ("%*s] %3d%%", gauge_len - 7 - columns, "", percentage);
710 }
711 return 1;
712 }
713 #endif
714 return default_proc (h, Msg, Par);
715 }
716
717 void
718 gauge_set_value (WGauge *g, int max, int current)
719 {
720 if (g->current == current && g->max == max)
721 return; /* Do not flicker */
722 if (max == 0)
723 max = 1; /* I do not like division by zero :) */
724 #ifdef HAVE_X
725 /* NOTE: x_gauge_set_value has to be called before we change actual
726 * max and current values in g, since it assumes g->max and
727 * g->current as the previous values and max and current
728 * as the new ones :) */
729 x_gauge_set_value (g, max, current);
730 #endif
731 g->current = current;
732 g->max = max;
733 #ifndef HAVE_X
734 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
735 #endif
736 }
737
738 void
739 gauge_show (WGauge *g, int shown)
740 {
741 if (g->shown == shown)
742 return;
743 g->shown = shown;
744 #ifdef HAVE_X
745 x_gauge_show (g);
746 #else
747 gauge_callback (g->widget.parent, g, WIDGET_DRAW, 0);
748 #endif
749 }
750
751 static void
752 gauge_destroy (WGauge *g)
753 {
754 /* nothing */
755 }
756
757 WGauge *
758 gauge_new (int y, int x, int shown, int max, int current, char *tkname)
759 {
760 WGauge *g = xmalloc (sizeof (WGauge), "gauge_new");
761
762 init_widget (&g->widget, y, x, 1, gauge_len,
763 (callback_fn) gauge_callback,
764 (destroy_fn) gauge_destroy, NULL, tkname);
765 g->shown = shown;
766 if (max == 0)
767 max = 1; /* I do not like division by zero :) */
768 g->max = max;
769 g->current = current;
770 g->pixels = 0;
771 widget_want_cursor (g->widget, 0);
772 return g;
773 }
774
775 \f
776 /* Input widget */
777
778 /* {{{ history button */
779
780 #define LARGE_HISTORY_BUTTON 1
781
782 #ifdef LARGE_HISTORY_BUTTON
783 # define HISTORY_BUTTON_WIDTH 3
784 #else
785 # define HISTORY_BUTTON_WIDTH 1
786 #endif
787
788 #define should_show_history_button(in) \
789 (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent)
790
791 static void draw_history_button (WInput * in)
792 {
793 char c;
794 c = in->history->next ? (in->history->prev ? '|' : 'v') : '^';
795 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH);
796 #ifdef LARGE_HISTORY_BUTTON
797 {
798 Dlg_head *h;
799 h = in->widget.parent;
800 #if 0
801 attrset (NORMALC); /* button has the same colour as other buttons */
802 addstr ("[ ]");
803 attrset (HOT_NORMALC);
804 #else
805 attrset (NORMAL_COLOR);
806 addstr ("[ ]");
807 /* Too distracting: attrset (MARKED_COLOR); */
808 #endif
809 widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
810 addch (c);
811 }
812 #else
813 attrset (MARKED_COLOR);
814 addch (c);
815 #endif
816 }
817
818 /* }}} history button */
819
820
821 /* Input widgets now have a global kill ring */
822 /* Pointer to killed data */
823 static char *kill_buffer = 0;
824
825 void
826 update_input (WInput *in, int clear_first)
827 {
828 #ifndef HAVE_XVIEW
829 int has_history = 0;
830 int i, j;
831 char c;
832 int buf_len = strlen (in->buffer);
833
834 if (should_show_history_button (in))
835 has_history = HISTORY_BUTTON_WIDTH;
836
837 if (in->disable_update)
838 return;
839
840 /* Make the point visible */
841 if ((in->point < in->first_shown) ||
842 (in->point >= in->first_shown+in->field_len - has_history)){
843 in->first_shown = in->point - (in->field_len / 3);
844 if (in->first_shown < 0)
845 in->first_shown = 0;
846 }
847
848 /* Adjust the mark */
849 if (in->mark > buf_len)
850 in->mark = buf_len;
851
852 #ifdef HAVE_X
853 if (clear_first && in->first)
854 in->first = -1;
855 x_update_input (in);
856 #else
857
858 if (has_history)
859 draw_history_button (in);
860
861 attrset (in->color);
862
863 widget_move (&in->widget, 0, 0);
864 for (i = 0; i < in->field_len - has_history; i++)
865 addch (' ');
866 widget_move (&in->widget, 0, 0);
867
868 for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){
869 c = in->buffer [j++];
870 c = is_printable (c) ? c : '.';
871 if (in->is_password)
872 c = '*';
873 addch (c);
874 }
875 widget_move (&in->widget, 0, in->point - in->first_shown);
876
877 if (clear_first)
878 in->first = 0;
879 #endif
880
881 #endif
882 }
883
884 void
885 winput_set_origin (WInput *in, int x, int field_len)
886 {
887 in->widget.x = x;
888 in->field_len = in->widget.cols = field_len;
889 update_input (in, 0);
890 }
891
892 /* {{{ history saving and loading */
893
894 /*
895 This loads and saves the history of an input line to and from the
896 widget. It is called with the widgets tk name on creation of the
897 widget, and returns the Hist list. It stores histories in the file
898 ~/.mc/history in using the profile code.
899
900 If def_text is passed as INPUT_LAST_TEXT (to the input_new()
901 function) then input_new assigns the default text to be the last text
902 entered, or "" if not found.
903 */
904
905 int num_history_items_recorded = 60;
906
907 Hist *history_get (char *input_name)
908 {
909 int i;
910 Hist *old = 0, *new = 0;
911 char *profile;
912
913 if (!num_history_items_recorded) /* this is how to disable */
914 return 0;
915 if (!input_name)
916 return 0;
917 if (!*input_name)
918 return 0;
919 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
920 for (i = 0;; i++) {
921 char key_name[32];
922 char this_entry[1024];
923 sprintf (key_name, "%d", i);
924 GetPrivateProfileString (input_name, key_name, "", this_entry, sizeof (this_entry), profile);
925 if (!*this_entry)
926 break;
927 new = xmalloc (sizeof (Hist), "history_get");
928 memset (new, 0, sizeof (Hist));
929 new->text = strdup (this_entry);
930 new->prev = old; /* set up list pointers */
931 if (old)
932 old->next = new;
933 old = new;
934 }
935 free (profile);
936 return new; /* return pointer to last entry in list */
937 }
938
939 #ifdef PORT_WIDGET_WANTS_HISTORY
940 void history_put (char *input_name, Hist *h)
941 {
942 int i;
943 char *profile;
944
945 if (!input_name)
946 return;
947
948 if (!*input_name)
949 return;
950
951 if (!h)
952 return;
953
954 if (!num_history_items_recorded) /* this is how to disable */
955 return;
956
957 profile = concat_dir_and_file (home_dir, HISTORY_FILE_NAME);
958 while (h->next) /* go to end of list */
959 h = h->next;
960
961 /* go back 60 places */
962 for (i = 0; i < num_history_items_recorded - 1 && h->prev; i++)
963 h = h->prev;
964 i = 0;
965
966 if (input_name)
967 profile_clean_section (input_name, profile);
968
969 /* dump histories into profile */
970 while (h){
971 if (h->text){
972
973 /* probably aren't any null entries, but lets be sure */
974 if (*(h->text)){
975 char key_name[32];
976 sprintf (key_name, "%d", i++);
977 WritePrivateProfileString (input_name, key_name, h->text, profile);
978 }
979 }
980 h = h->next;
981 }
982 free (profile);
983 }
984 #else
985 void history_put (char *input_name, Hist *h)
986 {
987 }
988 #endif
989
990 /* }}} history saving and loading */
991
992
993 /* {{{ history display */
994
995 static const char history_title[] = " History ";
996
997 int history_callback (Dlg_head * h, int Par, int Msg)
998 {
999 #ifndef HAVE_X
1000 switch (Msg) {
1001 case DLG_DRAW:
1002 attrset (COLOR_NORMAL);
1003 dlg_erase (h);
1004 draw_box (h, 0, 0, h->lines, h->cols);
1005 attrset (COLOR_HOT_NORMAL);
1006 dlg_move (h, 0, (h->cols - strlen (history_title)) / 2);
1007 printw ((char *) history_title);
1008 break;
1009 }
1010 #endif
1011 return 0;
1012 }
1013
1014 static inline int listbox_fwd (WListbox *l);
1015
1016 char *show_hist (Hist *history, int widget_x, int widget_y)
1017 {
1018 Hist *hi, *z;
1019 int maxlen = strlen(history_title), i, count = 0;
1020 int x, y, w, h;
1021 char *q, *r = 0;
1022 Dlg_head *query_dlg;
1023 WListbox *query_list;
1024
1025 z = history;
1026 if (!z)
1027 return 0;
1028
1029 while (z->prev) /* goto first */
1030 z = z->prev;
1031 hi = z;
1032 while (hi) {
1033 if ((i = strlen (hi->text)) > maxlen)
1034 maxlen = i;
1035 count++;
1036 hi = hi->next;
1037 }
1038
1039 y = widget_y;
1040 h = count + 2;
1041 if (h <= y || y > LINES - 6)
1042 {
1043 h = min(h, y - 1);
1044 y -= h;
1045 }
1046 else
1047 {
1048 y++;
1049 h = min(h, LINES - y);
1050 }
1051
1052 x = widget_x - 2;
1053 if ((w = maxlen + 4) + x > COLS)
1054 {
1055 w = min(w,COLS);
1056 x = COLS - w;
1057 }
1058
1059 query_dlg = create_dlg (y, x, h, w, dialog_colors, history_callback,
1060 "[History-query]", "history", DLG_NONE);
1061 query_list = listbox_new (1, 1, w - 2, h - 2, listbox_finish, 0, NULL);
1062 add_widget (query_dlg, query_list);
1063 hi = z;
1064 if (y < widget_y) {
1065 while (hi) { /* traverse */
1066 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1067 hi = hi->next;
1068 }
1069 while (listbox_fwd (query_list));
1070 } else {
1071 while (hi->next)
1072 hi = hi->next;
1073 while (hi) { /* traverse backwards */
1074 listbox_add_item (query_list, 0, 0, hi->text, NULL);
1075 hi = hi->prev;
1076 }
1077 }
1078 run_dlg (query_dlg);
1079 q = NULL;
1080 if (query_dlg->ret_value != B_CANCEL) {
1081 listbox_get_current (query_list, &q, NULL);
1082 if (q)
1083 r = strdup (q);
1084 }
1085 destroy_dlg (query_dlg);
1086 return r;
1087 }
1088
1089 static void do_show_hist (WInput * in)
1090 {
1091 char *r;
1092 r = show_hist (in->history, in->widget.x, in->widget.y);
1093 if (r) {
1094 assign_text (in, r);
1095 free (r);
1096 }
1097 }
1098
1099 /* }}} history display */
1100
1101 static void
1102 input_destroy (WInput *in)
1103 {
1104 if (!in){
1105 fprintf (stderr, "Internal error: null Input *\n");
1106 exit (1);
1107 }
1108
1109 new_input (in);
1110 if (in->history){
1111 Hist *current, *old;
1112
1113 if (!in->is_password && PORT_WIDGET_WANTS_HISTORY) /* don't save passwords ;-) */
1114 history_put (in->history_name, in->history);
1115
1116 current = in->history;
1117 while (current->next)
1118 current = current->next;
1119 while (current){
1120 old = current;
1121 current = current->prev;
1122 free (old->text);
1123 free (old);
1124 }
1125 }
1126 x_destroy_cmd (in);
1127 free (in->buffer);
1128 free_completions (in);
1129 if (in->history_name)
1130 free (in->history_name);
1131 }
1132
1133 static char disable_update = 0;
1134
1135 void
1136 input_disable_update (WInput *in)
1137 {
1138 in->disable_update++;
1139 }
1140
1141 void
1142 input_enable_update (WInput *in)
1143 {
1144 in->disable_update--;
1145 update_input (in, 0);
1146 }
1147
1148 int
1149 push_history (WInput *in, char *text)
1150 {
1151 Hist *new;
1152 char *p;
1153
1154 for (p = text; *p == ' ' || *p == '\t'; p++);
1155 if (!*p)
1156 return 0;
1157 if (in->history){
1158 while (in->history->next)
1159 in->history = in->history->next;
1160 if (!strcmp (in->history->text, text))
1161 return 1;
1162 new = xmalloc (sizeof (Hist), "push_history");
1163 in->history->next = new;
1164 } else
1165 new = xmalloc (sizeof (Hist), "push_history");
1166 in->need_push = 0;
1167 new->next = 0;
1168 new->prev = in->history;
1169 new->text = strdup (text);
1170 in->history = new;
1171 return 2;
1172 }
1173
1174 /* Cleans the input line and adds the current text to the history */
1175 void
1176 new_input (WInput *in)
1177 {
1178 if (in->buffer)
1179 push_history (in, in->buffer);
1180 in->need_push = 1;
1181 in->buffer [0] = 0;
1182 in->point = 0;
1183 in->mark = 0;
1184 free_completions (in);
1185 update_input (in, 0);
1186 }
1187
1188 static int
1189 insert_char (WInput *in, int c_code)
1190 {
1191 int i;
1192
1193 if (c_code == -1)
1194 return 0;
1195
1196 in->need_push = 1;
1197 if (strlen (in->buffer)+1 == in->current_max_len){
1198 /* Expand the buffer */
1199 char *narea = xmalloc(in->current_max_len + in->field_len, "string expansion");
1200 if (narea){
1201 char *p = in->buffer;
1202
1203 strcpy (narea, in->buffer);
1204 in->buffer = narea;
1205 in->current_max_len += in->field_len;
1206 free (p);
1207 }
1208 }
1209 if (strlen (in->buffer)+1 < in->current_max_len){
1210 int l = strlen (&in->buffer [in->point]);
1211 for (i = l+1; i > 0; i--)
1212 in->buffer [in->point+i] = in->buffer [in->point+i-1];
1213 in->buffer [in->point] = c_code;
1214 in->point++;
1215 }
1216 return 1;
1217 }
1218
1219 static void
1220 beginning_of_line (WInput *in)
1221 {
1222 in->point = 0;
1223 }
1224
1225 static void
1226 end_of_line (WInput *in)
1227 {
1228 in->point = strlen (in->buffer);
1229 }
1230
1231 static void
1232 backward_char (WInput *in)
1233 {
1234 if (in->point)
1235 in->point--;
1236 }
1237
1238 static void
1239 forward_char (WInput *in)
1240 {
1241 if (in->buffer [in->point])
1242 in->point++;
1243 }
1244
1245 static void
1246 forward_word (WInput *in)
1247 {
1248 char *p = in->buffer+in->point;
1249
1250 while ((*p && isspace (*p)) || ispunct (*p))
1251 p++;
1252 while (*p && isalnum (*p))
1253 p++;
1254 in->point = p - in->buffer;
1255 }
1256
1257 static void
1258 backward_word (WInput *in)
1259 {
1260 char *p = in->buffer+in->point;
1261
1262 while (p-1 > in->buffer-1 && (isspace (*(p-1)) || ispunct (*(p-1))))
1263 p--;
1264 while (p-1 > in->buffer-1 && isalnum (*(p-1)))
1265 p--;
1266 in->point = p - in->buffer;
1267 }
1268
1269 #ifdef __linux__
1270 static void
1271 key_left (WInput *in)
1272 {
1273 if (ctrl_pressed ())
1274 backward_word (in);
1275 else
1276 backward_char (in);
1277 }
1278
1279 static void
1280 key_right (WInput *in)
1281 {
1282 if (ctrl_pressed ())
1283 forward_word (in);
1284 else
1285 forward_char (in);
1286 }
1287 #else
1288 #define key_left backward_char
1289 #define key_right forward_char
1290 #endif
1291
1292 static void
1293 backward_delete (WInput *in)
1294 {
1295 int i;
1296
1297 if (!in->point)
1298 return;
1299 for (i = in->point; in->buffer [i-1]; i++)
1300 in->buffer [i-1] = in->buffer [i];
1301 in->need_push = 1;
1302 in->point--;
1303 }
1304
1305 static void
1306 delete_char (WInput *in)
1307 {
1308 int i;
1309
1310 for (i = in->point; in->buffer [i]; i++)
1311 in->buffer [i] = in->buffer [i+1];
1312 in->need_push = 1;
1313 }
1314
1315 static void
1316 copy_region (WInput *in, int x_first, int x_last)
1317 {
1318 int first = min (x_first, x_last);
1319 int last = max (x_first, x_last);
1320
1321 if (last == first)
1322 return;
1323
1324 if (kill_buffer)
1325 free (kill_buffer);
1326
1327 kill_buffer = xmalloc (last-first + 1, "copy_region");
1328 strncpy (kill_buffer, in->buffer+first, last-first);
1329 kill_buffer [last-first] = 0;
1330 }
1331
1332 static void
1333 delete_region (WInput *in, int x_first, int x_last)
1334 {
1335 int first = min (x_first, x_last);
1336 int last = max (x_first, x_last);
1337
1338 in->point = first;
1339 in->mark = first;
1340 strcpy (&in->buffer [first], &in->buffer [last]);
1341 in->need_push = 1;
1342 }
1343
1344 static void
1345 kill_word (WInput *in)
1346 {
1347 int old_point = in->point;
1348 int new_point;
1349
1350 forward_word (in);
1351 new_point = in->point;
1352 in->point = old_point;
1353
1354 copy_region (in, old_point, new_point);
1355 delete_region (in, old_point, new_point);
1356 in->need_push = 1;
1357 }
1358
1359 static void
1360 back_kill_word (WInput *in)
1361 {
1362 int old_point = in->point;
1363 int new_point;
1364
1365 backward_word (in);
1366 new_point = in->point;
1367 in->point = old_point;
1368
1369 copy_region (in, old_point, new_point);
1370 delete_region (in, old_point, new_point);
1371 in->need_push = 1;
1372 }
1373
1374 static void
1375 set_mark (WInput *in)
1376 {
1377 in->mark = in->point;
1378 }
1379
1380 static void
1381 kill_save (WInput *in)
1382 {
1383 copy_region (in, in->mark, in->point);
1384 }
1385
1386 static void
1387 kill_region (WInput *in)
1388 {
1389 kill_save (in);
1390 delete_region (in, in->point, in->mark);
1391 }
1392
1393 static void
1394 yank (WInput *in)
1395 {
1396 char *p;
1397
1398 if (!kill_buffer)
1399 return;
1400 for (p = kill_buffer; *p; p++)
1401 insert_char (in, *p);
1402 }
1403
1404 static void
1405 kill_line (WInput *in)
1406 {
1407 if (kill_buffer)
1408 free (kill_buffer);
1409 kill_buffer = strdup (&in->buffer [in->point]);
1410 in->buffer [in->point] = 0;
1411 }
1412
1413 void
1414 assign_text (WInput *in, char *text)
1415 {
1416 free_completions (in);
1417 free (in->buffer);
1418 in->buffer = strdup (text); /* was in->buffer->text */
1419 in->current_max_len = strlen (in->buffer) + 1;
1420 in->point = strlen (in->buffer);
1421 in->mark = 0;
1422 in->need_push = 1;
1423 }
1424
1425 static void
1426 hist_prev (WInput *in)
1427 {
1428 if (!in->history)
1429 return;
1430
1431 if (in->need_push) {
1432 switch (push_history (in, in->buffer)) {
1433 case 2: in->history = in->history->prev; break;
1434 case 1: if (in->history->prev) in->history = in->history->prev; break;
1435 case 0: break;
1436 }
1437 } else if (in->history->prev)
1438 in->history = in->history->prev;
1439 else
1440 return;
1441 assign_text (in, in->history->text);
1442 in->need_push = 0;
1443 }
1444
1445 static void
1446 hist_next (WInput *in)
1447 {
1448 if (in->need_push) {
1449 switch (push_history (in, in->buffer)) {
1450 case 2:
1451 assign_text (in, "");
1452 return;
1453 case 0:
1454 return;
1455 }
1456 }
1457
1458 if (!in->history)
1459 return;
1460
1461 if (!in->history->next) {
1462 assign_text (in, "");
1463 return;
1464 }
1465
1466 in->history = in->history->next;
1467 assign_text (in, in->history->text);
1468 in->need_push = 0;
1469 }
1470
1471 static struct {
1472 int key_code;
1473 void (*fn)(WInput *in);
1474 } input_map [] = {
1475 /* Motion */
1476 { XCTRL('a'), beginning_of_line },
1477 { KEY_HOME, beginning_of_line },
1478 { KEY_A1, beginning_of_line },
1479 { XCTRL('e'), end_of_line },
1480 { KEY_END, end_of_line },
1481 { KEY_C1, end_of_line },
1482 { KEY_LEFT, key_left },
1483 { XCTRL('b'), backward_char },
1484 { ALT('b'), backward_word },
1485 { KEY_RIGHT, key_right },
1486 { XCTRL('f'), forward_char },
1487 { ALT('f'), forward_word },
1488
1489 /* Editing */
1490 { 0177, backward_delete },
1491 { KEY_BACKSPACE, backward_delete },
1492 { XCTRL('h'), backward_delete },
1493 { KEY_DC, delete_char },
1494 { XCTRL('d'), delete_char },
1495 { ALT('d'), kill_word },
1496 { ALT(KEY_BACKSPACE), back_kill_word },
1497 { ALT(XCTRL('h')), back_kill_word },
1498 { ALT(127), back_kill_word },
1499
1500 /* Region manipulation */
1501 { 0, set_mark },
1502 { XCTRL('w'), kill_region },
1503 { ALT('w'), kill_save },
1504 { XCTRL('y'), yank },
1505 { XCTRL('k'), kill_line },
1506
1507 /* History */
1508 { ALT('p'), hist_prev },
1509 { ALT('n'), hist_next },
1510 { ALT('h'), do_show_hist },
1511
1512 /* Completion */
1513 { ALT('\t'), complete },
1514
1515 { 0, 0 }
1516 };
1517
1518 /* This function is a test for a special input key used in complete.c */
1519 /* Returns 0 if it is not a special key, 1 if it is a non-complete key
1520 and 2 if it is a complete key */
1521 int
1522 is_in_input_map (WInput *in, int c_code)
1523 {
1524 int i;
1525
1526 for (i = 0; input_map [i].fn; i++)
1527 if (c_code == input_map [i].key_code)
1528 if (input_map [i].fn == complete)
1529 return 2;
1530 else
1531 return 1;
1532 return 0;
1533 }
1534
1535 #ifdef PORT_WINPUT_DELETES_MARKED
1536 static void
1537 port_region_marked_for_delete (WInput *in)
1538 {
1539 kill_region (in);
1540 }
1541 #else
1542 static void
1543 port_region_marked_for_delete (WInput *in)
1544 {
1545 *in->buffer = 0;
1546 in->point = 0;
1547 in->first = 0;
1548 }
1549 #endif
1550
1551 int
1552 handle_char (WInput *in, int c_code)
1553 {
1554 int i;
1555 int v;
1556
1557 v = 0;
1558
1559 #ifdef HAVE_TK
1560 in->inserted_one = 0;
1561 #endif
1562 if (quote){
1563 free_completions (in);
1564 v = insert_char (in, c_code);
1565 update_input (in, 1);
1566 quote = 0;
1567 return v;
1568 }
1569
1570 for (i = 0; input_map [i].fn; i++){
1571 if (c_code == input_map [i].key_code){
1572 if (input_map [i].fn != complete)
1573 free_completions (in);
1574 (*input_map [i].fn)(in);
1575 v = 1;
1576 break;
1577 }
1578 }
1579 if (!input_map [i].fn){
1580 if (c_code > 255 || !is_printable (c_code))
1581 return 0;
1582 if (in->first){
1583 port_region_marked_for_delete (in);
1584 }
1585 free_completions (in);
1586 v = insert_char (in, c_code);
1587 in->inserted_one = c_code;
1588 }
1589 if (!disable_update)
1590 update_input (in, 1);
1591 return v;
1592 }
1593
1594 /* Inserts text in input line */
1595 void
1596 stuff (WInput *in, char *text, int insert_extra_space)
1597 {
1598 input_disable_update (in);
1599 while (*text)
1600 handle_char (in, *text++);
1601 if (insert_extra_space)
1602 handle_char (in, ' ');
1603 input_enable_update (in);
1604 update_input (in, 1);
1605 }
1606
1607 void
1608 input_set_point (WInput *in, int pos)
1609 {
1610 if (pos > in->current_max_len)
1611 pos = in->current_max_len;
1612 if (pos != in->point)
1613 free_completions (in);
1614 in->point = pos;
1615 update_input (in, 1);
1616 }
1617
1618 int input_event (Gpm_Event *event, WInput *b);
1619
1620 static int
1621 input_callback (Dlg_head *h, WInput *in, int Msg, int Par)
1622 {
1623 switch (Msg){
1624 case WIDGET_INIT:
1625 return x_create_input (h, h->wdata, in);
1626
1627 #ifndef HAVE_XVIEW
1628 case WIDGET_KEY:
1629 if (Par == XCTRL('q')){
1630 int v;
1631
1632 quote = 1;
1633 v = handle_char (in, mi_getch ());
1634 quote = 0;
1635 return v;
1636 }
1637 if (Par == KEY_UP || Par == KEY_DOWN ||
1638 Par == ESC_CHAR || Par == KEY_F(10) ||
1639 Par == XCTRL('g'))
1640 return 0; /* We don't handle up/down */
1641
1642 if (Par == '\n'){
1643 dlg_one_down (h);
1644 return 1;
1645 }
1646 return handle_char (in, Par);
1647
1648 case WIDGET_FOCUS:
1649 case WIDGET_UNFOCUS:
1650 case WIDGET_DRAW:
1651 update_input (in, 0);
1652 break;
1653 #endif /* !HAVE_XVIEW */
1654 #ifndef HAVE_X
1655 case WIDGET_CURSOR:
1656 widget_move (&in->widget, 0, in->point - in->first_shown);
1657 return 1;
1658 #endif
1659
1660 }
1661 return default_proc (h, Msg, Par);
1662 }
1663
1664 /* Not declared static, since we check against this value in dlg.c */
1665 /* FIXME: Declare static again and provide an identification mechanism */
1666 int
1667 input_event (Gpm_Event *event, WInput *in)
1668 {
1669 #ifndef HAVE_X
1670 if (event->type & (GPM_DOWN|GPM_DRAG)){
1671 dlg_select_widget (in->widget.parent, in);
1672
1673 if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) {
1674 do_show_hist (in);
1675 update_input (in, 1);
1676 } else {
1677 in->point = strlen (in->buffer);
1678 if (event->x - in->first_shown - 1 < in->point)
1679 in->point = event->x - in->first_shown - 1;
1680 if (in->point < 0)
1681 in->point = 0;
1682
1683 update_input (in, 1);
1684 }
1685 }
1686 #endif
1687 return MOU_NORMAL;
1688 }
1689
1690 WInput *
1691 input_new (int y, int x, int color, int len, char *def_text, char *tkname)
1692 {
1693 WInput *in = xmalloc (sizeof (WInput), "input_new");
1694 int initial_buffer_len;
1695
1696 init_widget (&in->widget, y, x, 1, len,
1697 (callback_fn) input_callback,
1698 (destroy_fn) input_destroy, (mouse_h) input_event, tkname);
1699
1700 /* history setup */
1701 in->history = NULL;
1702 in->history_name = 0;
1703 if (tkname && PORT_WIDGET_WANTS_HISTORY){
1704 if (*tkname) {
1705 in->history_name = strdup (tkname);
1706 in->history = history_get (tkname);
1707 }
1708 }
1709 if (def_text == INPUT_LAST_TEXT) {
1710 def_text = "";
1711 if (in->history)
1712 if (in->history->text)
1713 def_text = in->history->text;
1714 }
1715 initial_buffer_len = 1 + max (len, strlen (def_text));
1716 in->widget.options |= W_IS_INPUT;
1717 in->completions = NULL;
1718 in->completion_flags =
1719 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
1720 INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
1721 in->current_max_len = initial_buffer_len;
1722 in->buffer = xmalloc (initial_buffer_len, "create_input: in->buffer");
1723 in->color = color;
1724 in->field_len = len;
1725 in->first = 1;
1726 in->first_shown = 0;
1727 in->disable_update = 0;
1728 in->mark = 0;
1729 in->need_push = 1;
1730 in->is_password = 0;
1731
1732 strcpy (in->buffer, def_text);
1733 in->point = strlen (in->buffer);
1734 in->first = 1;
1735 return in;
1736 }
1737
1738 \f
1739 /* Listbox widget */
1740
1741 /* Should draw the scrollbar, but currently draws only
1742 * indications that there is more information
1743 */
1744 static int listbox_cdiff (WLEntry *s, WLEntry *e);
1745
1746 static void
1747 listbox_drawscroll (WListbox *l)
1748 {
1749 extern int slow_terminal;
1750 int line;
1751 int i, top;
1752 int max_line = l->height-1;
1753
1754 /* Are we at the top? */
1755 widget_move (&l->widget, 0, l->width);
1756 if (l->list == l->top)
1757 one_vline ();
1758 else
1759 addch ('^');
1760
1761 /* Are we at the bottom? */
1762 widget_move (&l->widget, max_line, l->width);
1763 top = listbox_cdiff (l->list, l->top);
1764 if ((top + l->height == l->count) || l->height >= l->count)
1765 one_vline ();
1766 else
1767 addch ('v');
1768
1769 /* Now draw the nice relative pointer */
1770 if (l->count)
1771 line = 1+ ((l->pos * (l->height-2)) / l->count);
1772 else
1773 line = 0;
1774
1775 for (i = 1; i < max_line; i++){
1776 widget_move (&l->widget, i, l->width);
1777 if (i != line)
1778 one_vline ();
1779 else
1780 addch ('*');
1781 }
1782 }
1783
1784 static void
1785 listbox_draw (WListbox *l, Dlg_head *h, int focused)
1786 {
1787 WLEntry *e;
1788 int i;
1789 int sel_line;
1790 int normalc, selc;
1791 char *text;
1792
1793 if (focused){
1794 normalc = NORMALC;
1795 selc = FOCUSC;
1796 } else {
1797 normalc = NORMALC;
1798 selc = HOT_FOCUSC;
1799 }
1800 sel_line = -1;
1801
1802 for (e = l->top, i = 0; (i < l->height); i++){
1803
1804 /* Display the entry */
1805 if (e == l->current && sel_line == -1){
1806 sel_line = i;
1807 attrset (selc);
1808 } else
1809 attrset (normalc);
1810
1811 widget_move (&l->widget, i, 0);
1812
1813 if ((i > 0 && e == l->list) || !l->list)
1814 text = "";
1815 else {
1816 text = e->text;
1817 e = e->next;
1818 }
1819 printw (" %-*s ", l->width-2, name_trunc (text, l->width-2));
1820 }
1821 l->cursor_y = sel_line;
1822 if (!l->scrollbar)
1823 return;
1824 attrset (normalc);
1825 listbox_drawscroll (l);
1826 }
1827
1828 /* Returns the number of items between s and e,
1829 must be on the same linked list */
1830 static int
1831 listbox_cdiff (WLEntry *s, WLEntry *e)
1832 {
1833 int count;
1834
1835 for (count = 0; s != e; count++)
1836 s = s->next;
1837 return count;
1838 }
1839
1840 static WLEntry *
1841 listbox_check_hotkey (WListbox *l, int key)
1842 {
1843 int i;
1844 WLEntry *e;
1845
1846 i = 0;
1847 e = l->list;
1848 if (!e)
1849 return 0;
1850
1851 while (1){
1852
1853 /* If we didn't find anything, return */
1854 if (i && e == l->list)
1855 return 0;
1856
1857 if (e->hotkey == key)
1858 return e;
1859
1860 i++;
1861 e = e->next;
1862 }
1863 }
1864
1865 /* Used only for display updating, for avoiding line at a time scroll */
1866 void
1867 listbox_select_last (WListbox *l, int set_top)
1868 {
1869 if (l->list){
1870 l->current = l->list->prev;
1871 l->pos = l->count - 1;
1872 if (set_top)
1873 l->top = l->list->prev;
1874 x_listbox_select_nth (l, l->pos);
1875 }
1876 }
1877
1878 void
1879 listbox_remove_list (WListbox *l)
1880 {
1881 WLEntry *p, *q;
1882
1883 if (!l->count)
1884 return;
1885
1886 #ifdef HAVE_X
1887 if (l->widget.wdata != (widget_data) NULL) {
1888 int i;
1889 for (i = 0; i < l->count; i++)
1890 x_listbox_delete_nth (l, i);
1891 }
1892 #endif
1893 p = l->list;
1894
1895 while (l->count--) {
1896 q = p->next;
1897 free (p->text);
1898 free (p);
1899 p = q;
1900 }
1901 l->pos = l->count = 0;
1902 l->list = l->top = l->current = 0;
1903 }
1904
1905 /*
1906 * bor 30.10.96: added force flag to remove *last* entry as well
1907 * bor 30.10.96: corrected selection bug if last entry was removed
1908 */
1909
1910 void
1911 listbox_remove_current (WListbox *l, int force)
1912 {
1913 WLEntry *p;
1914
1915 /* Ok, note: this won't allow for emtpy lists */
1916 if (!force && (!l->count || l->count == 1))
1917 return;
1918
1919 #ifdef HAVE_X
1920 if (l->widget.wdata != (widget_data) NULL) {
1921 x_listbox_delete_nth (l, l->pos);
1922 if (l->count > 1)
1923 if (l->current->next != l->list)
1924 x_listbox_select_nth (l, l->pos);
1925 else if (l->current != l->list)
1926 x_listbox_select_nth (l, l->pos - 1);
1927 else
1928 x_listbox_select_nth (l, 0);
1929 }
1930 #endif
1931 l->count--;
1932 p = l->current;
1933
1934 if (l->count) {
1935 l->current->next->prev = l->current->prev;
1936 l->current->prev->next = l->current->next;
1937 if (p->next == l->list) {
1938 l->current = p->prev;
1939 l->pos--;
1940 }
1941 else
1942 l->current = p->next;
1943
1944 if (p == l->list)
1945 l->list = l->top = p->next;
1946 } else {
1947 l->pos = 0;
1948 l->list = l->top = l->current = 0;
1949 }
1950
1951 free (p->text);
1952 free (p);
1953 }
1954
1955 /* Makes *e the selected entry (sets current and pos) */
1956 void
1957 listbox_select_entry (WListbox *l, WLEntry *dest)
1958 {
1959 WLEntry *e;
1960 int pos;
1961 int top_seen;
1962
1963 top_seen = 0;
1964
1965 /* Special case */
1966 for (pos = 0, e = l->list; pos < l->count; e = e->next, pos++){
1967
1968 if (e == l->top)
1969 top_seen = 1;
1970
1971 if (e == dest){
1972 l->current = e;
1973 if (top_seen){
1974 while (listbox_cdiff (l->top, l->current) >= l->height)
1975 l->top = l->top->next;
1976 } else {
1977 l->top = l->current;
1978 }
1979 l->pos = pos;
1980 x_listbox_select_nth (l, l->pos);
1981 return;
1982 }
1983 }
1984 /* If we are unable to find it, set decent values */
1985 l->current = l->top = l->list;
1986 l->pos = 0;
1987 x_listbox_select_nth (l, l->pos);
1988 }
1989
1990 /* Selects from base the pos element */
1991 static WLEntry *
1992 listbox_select_pos (WListbox *l, WLEntry *base, int pos)
1993 {
1994 WLEntry *last = l->list->prev;
1995
1996 if (base == last)
1997 return last;
1998 while (pos--){
1999 base = base->next;
2000 if (base == last)
2001 break;
2002 }
2003 return base;
2004 }
2005
2006 static inline int
2007 listbox_back (WListbox *l)
2008 {
2009 if (l->pos){
2010 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos-1));
2011 return 1;
2012 }
2013 return 0;
2014 }
2015
2016 static inline int
2017 listbox_fwd (WListbox *l)
2018 {
2019 if (l->current != l->list->prev){
2020 listbox_select_entry (l, listbox_select_pos (l, l->list, l->pos+1));
2021 return 1;
2022 }
2023 return 0;
2024 }
2025
2026 /* Returns 1 if we want a redraw */
2027 static int
2028 listbox_key (WListbox *l, int key)
2029 {
2030 int i;
2031 int j = 0;
2032
2033 if (!l->list)
2034 return 0;
2035
2036 switch (key){
2037 case KEY_HOME:
2038 case KEY_A1:
2039 l->current = l->top = l->list;
2040 l->pos = 0;
2041 return 1;
2042
2043 case KEY_END:
2044 case KEY_C1:
2045 l->current = l->top = l->list->prev;
2046 for (i = min (l->height - 1, l->count - 1); i; i--)
2047 l->top = l->top->prev;
2048 l->pos = l->count - 1;
2049 return 1;
2050
2051 case XCTRL('p'):
2052 case KEY_UP:
2053 listbox_back (l);
2054 return 1;
2055
2056 case XCTRL('n'):
2057 case KEY_DOWN:
2058 listbox_fwd (l);
2059 return 1;
2060
2061 case KEY_NPAGE:
2062 case XCTRL('v'):
2063 for (i = 0; i < l->height-1; i++)
2064 j |= listbox_fwd (l);
2065 return j > 0;
2066
2067 case KEY_PPAGE:
2068 case ALT('v'):
2069 for (i = 0; i < l->height-1; i++)
2070 j |= listbox_back (l);
2071 return j > 0;
2072 }
2073 return 0;
2074 }
2075
2076 static int listbox_event (Gpm_Event *event, WListbox *l);
2077 static int
2078 listbox_callback (Dlg_head *h, WListbox *l, int msg, int par)
2079 {
2080 WLEntry *e;
2081 /* int selected_color; Never used */
2082 int ret_code;
2083
2084 switch (msg){
2085 case WIDGET_INIT:
2086 return x_create_listbox (h, h->wdata, l);
2087
2088 #ifndef HAVE_XVIEW
2089 case WIDGET_HOTKEY:
2090 if ((e = listbox_check_hotkey (l, par)) != NULL){
2091 listbox_select_entry (l, e);
2092
2093 /* Take the appropriate action */
2094 if (l->action == listbox_finish){
2095 l->widget.parent->running = 0;
2096 l->widget.parent->ret_value = B_ENTER;
2097 } else if (l->action == listbox_cback){
2098 if ((*l->cback)(l) == listbox_finish){
2099 l->widget.parent->running = 0;
2100 l->widget.parent->ret_value = B_ENTER;
2101 }
2102 }
2103 return 1;
2104 } else
2105 return 0;
2106
2107 case WIDGET_KEY:
2108 if ((ret_code = listbox_key (l, par)))
2109 listbox_draw (l, h, 1);
2110 return ret_code;
2111
2112 #ifndef HAVE_X
2113 case WIDGET_CURSOR:
2114 widget_move (&l->widget, l->cursor_y, 0);
2115 return 1;
2116
2117 case WIDGET_FOCUS:
2118 case WIDGET_UNFOCUS:
2119 case WIDGET_DRAW:
2120 listbox_draw (l, h, msg != WIDGET_UNFOCUS);
2121 return 1;
2122 #endif
2123 #endif /* !HAVE_XVIEW */
2124 }
2125 return default_proc (h, msg, par);
2126 }
2127
2128 static int
2129 listbox_event (Gpm_Event *event, WListbox *l)
2130 {
2131 #ifndef HAVE_X
2132 int i;
2133
2134 Dlg_head *h = l->widget.parent;
2135
2136 /* Single click */
2137 if (event->type & GPM_DOWN)
2138 dlg_select_widget (l->widget.parent, l);
2139 if (!l->list)
2140 return MOU_NORMAL;
2141 if (event->type & (GPM_DOWN|GPM_DRAG)){
2142 if (event->x < 0 || event->x >= l->width)
2143 return MOU_REPEAT;
2144 if (event->y < 1)
2145 for (i = -event->y; i >= 0; i--)
2146 listbox_back (l);
2147 else if (event->y > l->height)
2148 for (i = event->y - l->height; i > 0; i--)
2149 listbox_fwd (l);
2150 else
2151 listbox_select_entry (l, listbox_select_pos (l, l->top,
2152 event->y - 1));
2153
2154 /* We need to refresh ourselves since the dialog manager doesn't */
2155 /* know about this event */
2156 listbox_callback (h, l, WIDGET_DRAW, 0);
2157 mc_refresh ();
2158 return MOU_REPEAT;
2159 }
2160
2161 /* Double click */
2162 if ((event->type & (GPM_DOUBLE|GPM_UP)) == (GPM_UP|GPM_DOUBLE)){
2163 if (event->x < 0 || event->x >= l->width)
2164 return MOU_NORMAL;
2165 if (event->y < 1 || event->y > l->height)
2166 return MOU_NORMAL;
2167
2168 dlg_select_widget (l->widget.parent, l);
2169 listbox_select_entry (l, listbox_select_pos (l, l->top, event->y - 1));
2170
2171 switch (l->action){
2172 case listbox_nothing:
2173 break;
2174
2175 case listbox_finish:
2176 h->ret_value = B_ENTER;
2177 dlg_stop (h);
2178 return MOU_ENDLOOP;
2179
2180 case listbox_cback:
2181 if ((*l->cback)(l) == listbox_finish)
2182 return MOU_ENDLOOP;
2183 }
2184 }
2185 #endif
2186 return MOU_NORMAL;
2187 }
2188
2189 static void
2190 listbox_destroy (WListbox *l)
2191 {
2192 WLEntry *n, *p = l->list;
2193 int i;
2194
2195 x_destroy_cmd (l);
2196 for (i = 0; i < l->count; i++){
2197 n = p->next;
2198 free (p->text);
2199 free (p);
2200 p = n;
2201 }
2202 }
2203
2204 WListbox *
2205 listbox_new (int y, int x, int width, int height,
2206 int action, lcback callback, char *tkname)
2207 {
2208 WListbox *l = xmalloc (sizeof (WListbox), "listbox_new");
2209 extern int slow_terminal;
2210
2211 init_widget (&l->widget, y, x, height, width,
2212 (callback_fn)listbox_callback,
2213 (destroy_fn) listbox_destroy, (mouse_h)listbox_event, tkname);
2214
2215 l->list = l->top = l->current = 0;
2216 l->pos = 0;
2217 l->width = width;
2218 l->height = height;
2219 l->count = 0;
2220 l->top = 0;
2221 l->current= 0;
2222 l->cback = callback;
2223 l->action = action;
2224 l->allow_duplicates = 1;
2225 l->scrollbar = slow_terminal ? 0 : 1;
2226 widget_want_hotkey (l->widget, 1);
2227
2228 return l;
2229 }
2230
2231 /* Listbox item adding function. They still lack a lot of functionality */
2232 /* any takers? */
2233 /* 1.11.96 bor: added pos argument to control placement of new entry */
2234 static void
2235 listbox_append_item (WListbox *l, WLEntry *e, enum append_pos pos)
2236 {
2237 if (!l->list){
2238 l->list = e;
2239 l->top = e;
2240 l->current = e;
2241 e->next = l->list;
2242 e->prev = l->list;
2243 } else if (pos == LISTBOX_APPEND_AT_END) {
2244 e->next = l->list;
2245 e->prev = l->list->prev;
2246 l->list->prev->next = e;
2247 l->list->prev = e;
2248 } else if (pos == LISTBOX_APPEND_BEFORE){
2249 e->next = l->current;
2250 e->prev = l->current->prev;
2251 l->current->prev->next = e;
2252 l->current->prev = e;
2253 if (l->list == l->current) { /* move list one position down */
2254 l->list = e;
2255 l->top = e;
2256 }
2257 } else if (pos == LISTBOX_APPEND_AFTER) {
2258 e->prev = l->current;
2259 e->next = l->current->next;
2260 l->current->next->prev = e;
2261 l->current->next = e;
2262 }
2263 x_list_insert (l, l->list, e);
2264 l->count++;
2265 }
2266
2267 char *
2268 listbox_add_item (WListbox *l, enum append_pos pos, int hotkey, char *text,
2269 void *data)
2270 {
2271 WLEntry *entry;
2272
2273 if (!l)
2274 return 0;
2275
2276 if (!l->allow_duplicates)
2277 if (listbox_search_text (l, text))
2278 return 0;
2279
2280 entry = xmalloc (sizeof (WLEntry), "listbox_add_item");
2281 entry->text = strdup (text);
2282 entry->data = data;
2283 entry->hotkey = hotkey;
2284
2285 listbox_append_item (l, entry, pos);
2286
2287 return entry->text;
2288 }
2289
2290 /* Selects the nth entry in the listbox */
2291 void
2292 listbox_select_by_number (WListbox *l, int n)
2293 {
2294 listbox_select_entry (l, listbox_select_pos (l, l->list, n));
2295 }
2296
2297 WLEntry *
2298 listbox_search_text (WListbox *l, char *text)
2299 {
2300 WLEntry *e;
2301
2302 e = l->list;
2303 if (!e)
2304 return NULL;
2305
2306 do {
2307 if(!strcmp (e->text, text))
2308 return e;
2309 e = e->next;
2310 } while (e!=l->list);
2311
2312 return NULL;
2313 }
2314
2315 /* Returns the current string text as well as the associated extra data */
2316 void
2317 listbox_get_current (WListbox *l, char **string, char **extra)
2318 {
2319 if (!l->current){
2320 *string = 0;
2321 *extra = 0;
2322 }
2323 if (string && l->current)
2324 *string = l->current->text;
2325 if (extra && l->current)
2326 *extra = l->current->data;
2327 }
2328
2329 int
2330 buttonbar_callback (Dlg_head *h, WButtonBar *bb, int msg, int par)
2331 {
2332 int i;
2333
2334 switch (msg){
2335 case WIDGET_INIT:
2336 return x_create_buttonbar (h, h->wdata, bb);
2337
2338 case WIDGET_FOCUS:
2339 return 0;
2340
2341 #ifndef HAVE_XVIEW
2342 case WIDGET_HOTKEY:
2343 for (i = 0; i < 10; i++){
2344 if (par == KEY_F(i+1) && bb->labels [i].function){
2345 (*bb->labels [i].function)(bb->labels [i].data);
2346 return 1;
2347 }
2348 }
2349 return 0;
2350
2351 #ifndef HAVE_X
2352 case WIDGET_DRAW:
2353 if (!bb->visible)
2354 return 1;
2355 widget_move (&bb->widget, 0, 0);
2356 attrset (DEFAULT_COLOR);
2357 printw ("%-*s", bb->widget.cols - 1, "");
2358 for (i = 0; i < COLS/8 && i < 10; i++){
2359 widget_move (&bb->widget, 0, i*8);
2360 attrset (DEFAULT_COLOR);
2361 printw ("%d", i+1);
2362 attrset (SELECTED_COLOR);
2363 printw ("%-*s", ((i+1) * 8 == COLS ? 5 : 6),
2364 bb->labels [i].text ? bb->labels [i].text : "");
2365 attrset (DEFAULT_COLOR);
2366 }
2367 attrset (SELECTED_COLOR);
2368 return 1;
2369 #endif
2370 #endif /* !HAVE_XVIEW */
2371 }
2372 return default_proc (h, msg, par);
2373 }
2374
2375 static void
2376 buttonbar_destroy (WButtonBar *bb)
2377 {
2378 int i;
2379
2380 for (i = 0; i < 10; i++){
2381 if (bb->labels [i].text)
2382 free (bb->labels [i].text);
2383 }
2384 }
2385
2386 static int
2387 buttonbar_event (Gpm_Event *event, WButtonBar *bb)
2388 {
2389 #ifndef HAVE_X
2390 int button;
2391
2392 if (!(event->type & GPM_UP))
2393 return MOU_NORMAL;
2394 if (event->y == 2)
2395 return MOU_NORMAL;
2396 button = event->x / 8;
2397 if (button < 10 && bb->labels [button].function)
2398 (*bb->labels [button].function)(bb->labels [button].data);
2399 #endif
2400 return MOU_NORMAL;
2401 }
2402
2403 WButtonBar *
2404 buttonbar_new (int visible)
2405 {
2406 int i;
2407 WButtonBar *bb = xmalloc (sizeof (WButtonBar), "buttonbar_new");
2408
2409 init_widget (&bb->widget, LINES-1, 0, 1, COLS,
2410 (callback_fn) buttonbar_callback,
2411 (destroy_fn) buttonbar_destroy, (mouse_h) buttonbar_event, NULL);
2412
2413 bb->visible = visible;
2414 for (i = 0; i < 10; i++){
2415 bb->labels [i].text = 0;
2416 bb->labels [i].function = 0;
2417 }
2418 widget_want_hotkey (bb->widget, 1);
2419 widget_want_cursor (bb->widget, 0);
2420
2421 return bb;
2422 }
2423
2424 void
2425 set_label_text (WButtonBar *bb, int index, char *text)
2426 {
2427 if (bb->labels [index-1].text)
2428 free (bb->labels [index-1].text);
2429
2430 bb->labels [index-1].text = strdup (text);
2431 }
2432
2433 /* paneletc is either the panel widget, or info or view or tree widget */
2434 WButtonBar *
2435 find_buttonbar (Dlg_head *h, Widget *paneletc)
2436 {
2437 WButtonBar *bb;
2438 Widget_Item *item;
2439 int i;
2440
2441 bb = 0;
2442 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2443 if (item->widget->callback == (callback_fn) buttonbar_callback){
2444 bb = (WButtonBar *) item->widget;
2445 #ifdef HAVE_XVIEW
2446 /* Jakub: do we really need this routine here?
2447 * Does XView hold more that a buttonbar per Dlg_head?
2448 */
2449 if (x_find_buttonbar_check (bb, paneletc)) {
2450 bb = 0;
2451 continue;
2452 }
2453 #endif
2454 break;
2455 }
2456 }
2457 return bb;
2458 }
2459
2460 void
2461 define_label_data (Dlg_head *h, Widget *paneletc, int idx, char *text,
2462 buttonbarfn cback, void *data)
2463 {
2464 WButtonBar *bb = find_buttonbar (h, paneletc);
2465 if (!bb)
2466 return;
2467
2468 set_label_text (bb, idx, text);
2469 bb->labels [idx-1].function = (void (*)(void *)) cback;
2470 bb->labels [idx-1].data = data;
2471 x_redefine_label (bb, idx);
2472 }
2473
2474 void
2475 define_label (Dlg_head *h, Widget *paneletc, int idx, char *text, void (*cback)(void))
2476 {
2477 define_label_data (h, paneletc, idx, text, (void (*)(void *)) cback, 0);
2478 }
2479
2480 #ifdef HAVE_X
2481 void redraw_labels (Dlg_head *h, Widget *paneletc)
2482 {
2483 }
2484
2485 #else
2486 void
2487 redraw_labels (Dlg_head *h, Widget *paneletc)
2488 {
2489 Widget_Item *item;
2490 int i;
2491
2492 for (i = 0, item = h->current; i < h->count; i++, item = item->next){
2493 if (item->widget->callback == (callback_fn) buttonbar_callback){
2494 widget_redraw (h, item);
2495 return;
2496 }
2497 }
2498 }
2499 #endif