Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/dialog/fselect.c
39536 views
1
/*
2
* $Id: fselect.c,v 1.115 2021/01/16 17:19:15 tom Exp $
3
*
4
* fselect.c -- implements the file-selector box
5
*
6
* Copyright 2000-2020,2021 Thomas E. Dickey
7
*
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU Lesser General Public License, version 2.1
10
* as published by the Free Software Foundation.
11
*
12
* This program is distributed in the hope that it will be useful, but
13
* WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Lesser General Public License for more details.
16
*
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this program; if not, write to
19
* Free Software Foundation, Inc.
20
* 51 Franklin St., Fifth Floor
21
* Boston, MA 02110, USA.
22
*/
23
24
#include <dlg_internals.h>
25
#include <dlg_keys.h>
26
27
#include <sys/types.h>
28
#include <sys/stat.h>
29
30
#if HAVE_DIRENT_H
31
# include <dirent.h>
32
# define NAMLEN(dirent) strlen((dirent)->d_name)
33
#else
34
# define dirent direct
35
# define NAMLEN(dirent) (dirent)->d_namlen
36
# if HAVE_SYS_NDIR_H
37
# include <sys/ndir.h>
38
# endif
39
# if HAVE_SYS_DIR_H
40
# include <sys/dir.h>
41
# endif
42
# if HAVE_NDIR_H
43
# include <ndir.h>
44
# endif
45
#endif
46
47
# if defined(_FILE_OFFSET_BITS) && defined(HAVE_STRUCT_DIRENT64)
48
# if !defined(_LP64) && (_FILE_OFFSET_BITS == 64)
49
# define DIRENT struct dirent64
50
# else
51
# define DIRENT struct dirent
52
# endif
53
# else
54
# define DIRENT struct dirent
55
# endif
56
57
#define EXT_WIDE 1
58
#define HDR_HIGH 1
59
#define BTN_HIGH (1 + 2 * MARGIN) /* Ok/Cancel, also input-box */
60
#define MIN_HIGH (HDR_HIGH - MARGIN + (BTN_HIGH * 2) + 4 * MARGIN)
61
#define MIN_WIDE (2 * MAX(dlg_count_columns(d_label), dlg_count_columns(f_label)) + 6 * MARGIN + 2 * EXT_WIDE)
62
63
#define MOUSE_D (KEY_MAX + 0)
64
#define MOUSE_F (KEY_MAX + 10000)
65
#define MOUSE_T (KEY_MAX + 20000)
66
67
typedef enum {
68
sDIRS = -3
69
,sFILES = -2
70
,sTEXT = -1
71
} STATES;
72
73
typedef struct {
74
WINDOW *par; /* parent window */
75
WINDOW *win; /* this window */
76
int length; /* length of the data[] array */
77
int offset; /* index of first item on screen */
78
int choice; /* index of the selection */
79
int mousex; /* base of mouse-code return-values */
80
unsigned allocd;
81
char **data;
82
} LIST;
83
84
typedef struct {
85
int length;
86
char **data;
87
} MATCH;
88
89
static void
90
init_list(LIST * list, WINDOW *par, WINDOW *win, int mousex)
91
{
92
list->par = par;
93
list->win = win;
94
list->length = 0;
95
list->offset = 0;
96
list->choice = 0;
97
list->mousex = mousex;
98
list->allocd = 0;
99
list->data = 0;
100
dlg_mouse_mkbigregion(getbegy(win), getbegx(win),
101
getmaxy(win), getmaxx(win),
102
mousex, 1, 1, 1 /* by lines */ );
103
}
104
105
static char *
106
leaf_of(char *path)
107
{
108
char *leaf = strrchr(path, '/');
109
if (leaf != 0)
110
leaf++;
111
else
112
leaf = path;
113
return leaf;
114
}
115
116
static char *
117
data_of(LIST * list)
118
{
119
if (list != 0
120
&& list->data != 0)
121
return list->data[list->choice];
122
return 0;
123
}
124
125
static void
126
free_list(LIST * list, int reinit)
127
{
128
if (list->data != 0) {
129
int n;
130
131
for (n = 0; list->data[n] != 0; n++)
132
free(list->data[n]);
133
free(list->data);
134
list->data = 0;
135
}
136
if (reinit)
137
init_list(list, list->par, list->win, list->mousex);
138
}
139
140
static void
141
add_to_list(LIST * list, char *text)
142
{
143
unsigned need;
144
145
need = (unsigned) (list->length + 1);
146
if (need + 1 > list->allocd) {
147
list->allocd = 2 * (need + 1);
148
if (list->data == 0) {
149
list->data = dlg_malloc(char *, list->allocd);
150
} else {
151
list->data = dlg_realloc(char *, list->allocd, list->data);
152
}
153
assert_ptr(list->data, "add_to_list");
154
}
155
list->data[list->length++] = dlg_strclone(text);
156
list->data[list->length] = 0;
157
}
158
159
static void
160
keep_visible(LIST * list)
161
{
162
int high = getmaxy(list->win);
163
164
if (list->choice < list->offset) {
165
list->offset = list->choice;
166
}
167
if (list->choice - list->offset >= high)
168
list->offset = list->choice - high + 1;
169
}
170
171
#define Value(c) (int)((c) & 0xff)
172
173
static int
174
find_choice(char *target, LIST * list)
175
{
176
int choice = list->choice;
177
178
if (*target == 0) {
179
list->choice = 0;
180
} else {
181
int n;
182
int len_1, cmp_1;
183
184
/* find the match with the longest length. If more than one has the
185
* same length, choose the one with the closest match of the final
186
* character.
187
*/
188
len_1 = 0;
189
cmp_1 = 256;
190
for (n = 0; n < list->length; n++) {
191
char *a = target;
192
char *b = list->data[n];
193
int len_2, cmp_2;
194
195
len_2 = 0;
196
while ((*a != 0) && (*b != 0) && (*a == *b)) {
197
a++;
198
b++;
199
len_2++;
200
}
201
cmp_2 = Value(*a) - Value(*b);
202
if (cmp_2 < 0)
203
cmp_2 = -cmp_2;
204
if ((len_2 > len_1)
205
|| (len_1 == len_2 && cmp_2 < cmp_1)) {
206
len_1 = len_2;
207
cmp_1 = cmp_2;
208
list->choice = n;
209
}
210
}
211
}
212
if (choice != list->choice) {
213
keep_visible(list);
214
}
215
return (choice != list->choice);
216
}
217
218
static void
219
display_list(LIST * list)
220
{
221
if (list->win != 0) {
222
int n;
223
int x;
224
int y;
225
int top;
226
int bottom;
227
228
dlg_attr_clear(list->win, getmaxy(list->win), getmaxx(list->win), item_attr);
229
for (n = list->offset; n < list->length && list->data[n]; n++) {
230
y = n - list->offset;
231
if (y >= getmaxy(list->win))
232
break;
233
(void) wmove(list->win, y, 0);
234
if (n == list->choice)
235
dlg_attrset(list->win, item_selected_attr);
236
(void) waddstr(list->win, list->data[n]);
237
dlg_attrset(list->win, item_attr);
238
}
239
dlg_attrset(list->win, item_attr);
240
241
getparyx(list->win, y, x);
242
243
top = y - 1;
244
bottom = y + getmaxy(list->win);
245
dlg_draw_scrollbar(list->par,
246
(long) list->offset,
247
(long) list->offset,
248
(long) (list->offset + getmaxy(list->win)),
249
(long) (list->length),
250
x + 1,
251
x + getmaxx(list->win),
252
top,
253
bottom,
254
menubox_border2_attr,
255
menubox_border_attr);
256
257
(void) wmove(list->win, list->choice - list->offset, 0);
258
(void) wnoutrefresh(list->win);
259
}
260
}
261
262
/* FIXME: see arrows.c
263
* This workaround is used to allow two lists to have scroll-tabs at the same
264
* time, by reassigning their return-values to be different. Just for
265
* readability, we use the names of keys with similar connotations, though all
266
* that is really required is that they're distinct, so we can put them in a
267
* switch statement.
268
*/
269
#if USE_MOUSE
270
static void
271
fix_arrows(LIST * list)
272
{
273
if (list->win != 0) {
274
int x;
275
int y;
276
int top;
277
int right;
278
int bottom;
279
280
getparyx(list->win, y, x);
281
top = y - 1;
282
right = getmaxx(list->win);
283
bottom = y + getmaxy(list->win);
284
285
mouse_mkbutton(top, x, right,
286
((list->mousex == MOUSE_D)
287
? KEY_PREVIOUS
288
: KEY_PPAGE));
289
mouse_mkbutton(bottom, x, right,
290
((list->mousex == MOUSE_D)
291
? KEY_NEXT
292
: KEY_NPAGE));
293
}
294
}
295
296
#else
297
#define fix_arrows(list) /* nothing */
298
#endif
299
300
static bool
301
show_list(char *target, LIST * list, bool keep)
302
{
303
bool changed = keep || find_choice(target, list);
304
display_list(list);
305
return changed;
306
}
307
308
/*
309
* Highlight the closest match to 'target' in the given list, setting offset
310
* to match.
311
*/
312
static bool
313
show_both_lists(char *input, LIST * d_list, LIST * f_list, bool keep)
314
{
315
char *leaf = leaf_of(input);
316
317
return show_list(leaf, d_list, keep) || show_list(leaf, f_list, keep);
318
}
319
320
/*
321
* Move up/down in the given list
322
*/
323
static bool
324
change_list(int choice, LIST * list)
325
{
326
if (data_of(list) != 0) {
327
int last = list->length - 1;
328
329
choice += list->choice;
330
if (choice < 0)
331
choice = 0;
332
if (choice > last)
333
choice = last;
334
list->choice = choice;
335
keep_visible(list);
336
display_list(list);
337
return TRUE;
338
}
339
return FALSE;
340
}
341
342
static void
343
scroll_list(int direction, LIST * list)
344
{
345
if (data_of(list) != 0) {
346
int length = getmaxy(list->win);
347
if (change_list(direction * length, list))
348
return;
349
}
350
beep();
351
}
352
353
static int
354
compar(const void *a, const void *b)
355
{
356
return strcmp(*(const char *const *) a, *(const char *const *) b);
357
}
358
359
static void
360
match(char *name, LIST * d_list, LIST * f_list, MATCH * match_list)
361
{
362
char *test = leaf_of(name);
363
size_t test_len = strlen(test);
364
char **matches = dlg_malloc(char *, (size_t) (d_list->length + f_list->length));
365
size_t data_len = 0;
366
367
if (matches != 0) {
368
int i;
369
char **new_ptr;
370
371
for (i = 2; i < d_list->length; i++) {
372
if (strncmp(test, d_list->data[i], test_len) == 0) {
373
matches[data_len++] = d_list->data[i];
374
}
375
}
376
for (i = 0; i < f_list->length; i++) {
377
if (strncmp(test, f_list->data[i], test_len) == 0) {
378
matches[data_len++] = f_list->data[i];
379
}
380
}
381
if ((new_ptr = dlg_realloc(char *, data_len + 1, matches)) != 0) {
382
matches = new_ptr;
383
} else {
384
free(matches);
385
matches = 0;
386
data_len = 0;
387
}
388
}
389
match_list->data = matches;
390
match_list->length = (int) data_len;
391
}
392
393
static void
394
free_match(MATCH * match_list)
395
{
396
free(match_list->data);
397
match_list->length = 0;
398
}
399
400
static int
401
complete(char *name, LIST * d_list, LIST * f_list, char **buff_ptr)
402
{
403
MATCH match_list;
404
char *test;
405
size_t test_len;
406
size_t i;
407
char *buff;
408
409
match(name, d_list, f_list, &match_list);
410
if (match_list.length == 0) {
411
free(match_list.data);
412
*buff_ptr = NULL;
413
return 0;
414
}
415
416
test = match_list.data[0];
417
test_len = strlen(test);
418
buff = dlg_malloc(char, test_len + 2);
419
if (match_list.length == 1) {
420
strcpy(buff, test);
421
i = test_len;
422
if (test == data_of(d_list)) {
423
buff[test_len] = '/';
424
i++;
425
}
426
} else {
427
int j;
428
429
for (i = 0; i < test_len; i++) {
430
char test_char = test[i];
431
if (test_char == '\0')
432
break;
433
for (j = 0; j < match_list.length; j++) {
434
if (match_list.data[j][i] != test_char) {
435
break;
436
}
437
}
438
if (j == match_list.length) {
439
(buff)[i] = test_char;
440
} else
441
break;
442
}
443
buff = dlg_realloc(char, i + 1, buff);
444
}
445
free_match(&match_list);
446
buff[i] = '\0';
447
*buff_ptr = buff;
448
return (i != 0);
449
}
450
451
static bool
452
fill_lists(char *current, char *input, LIST * d_list, LIST * f_list, bool keep)
453
{
454
bool result = TRUE;
455
bool rescan = FALSE;
456
struct stat sb;
457
int n;
458
char path[MAX_LEN + 1];
459
460
/* check if we've updated the lists */
461
for (n = 0; current[n] && input[n]; n++) {
462
if (current[n] != input[n])
463
break;
464
}
465
466
if (current[n] == input[n]) {
467
result = FALSE;
468
rescan = (n == 0 && d_list->length == 0);
469
} else if (strchr(current + n, '/') == 0
470
&& strchr(input + n, '/') == 0) {
471
result = show_both_lists(input, d_list, f_list, keep);
472
} else {
473
rescan = TRUE;
474
}
475
476
if (rescan) {
477
DIR *dp;
478
size_t have = strlen(input);
479
char *leaf;
480
481
if (have > MAX_LEN)
482
have = MAX_LEN;
483
memcpy(current, input, have);
484
current[have] = '\0';
485
486
/* refill the lists */
487
free_list(d_list, TRUE);
488
free_list(f_list, TRUE);
489
memcpy(path, current, have);
490
path[have] = '\0';
491
if ((leaf = strrchr(path, '/')) != 0) {
492
*++leaf = 0;
493
} else {
494
strcpy(path, "./");
495
leaf = path + strlen(path);
496
}
497
DLG_TRACE(("opendir '%s'\n", path));
498
if ((dp = opendir(path)) != 0) {
499
DIRENT *de;
500
501
while ((de = readdir(dp)) != 0) {
502
size_t len = NAMLEN(de);
503
if (len == 0 || (len + have + 2) >= MAX_LEN)
504
continue;
505
memcpy(leaf, de->d_name, len);
506
leaf[len] = '\0';
507
if (stat(path, &sb) == 0) {
508
if ((sb.st_mode & S_IFMT) == S_IFDIR)
509
add_to_list(d_list, leaf);
510
else if (f_list->win)
511
add_to_list(f_list, leaf);
512
}
513
}
514
(void) closedir(dp);
515
/* sort the lists */
516
if (d_list->data != 0 && d_list->length > 1) {
517
qsort(d_list->data,
518
(size_t) d_list->length,
519
sizeof(d_list->data[0]),
520
compar);
521
}
522
if (f_list->data != 0 && f_list->length > 1) {
523
qsort(f_list->data,
524
(size_t) f_list->length,
525
sizeof(f_list->data[0]),
526
compar);
527
}
528
}
529
530
(void) show_both_lists(input, d_list, f_list, FALSE);
531
d_list->offset = d_list->choice;
532
f_list->offset = f_list->choice;
533
result = TRUE;
534
}
535
return result;
536
}
537
538
static bool
539
usable_state(int state, LIST * dirs, LIST * files)
540
{
541
bool result;
542
543
switch (state) {
544
case sDIRS:
545
result = (dirs->win != 0) && (data_of(dirs) != 0);
546
break;
547
case sFILES:
548
result = (files->win != 0) && (data_of(files) != 0);
549
break;
550
default:
551
result = TRUE;
552
break;
553
}
554
return result;
555
}
556
557
#define which_list() ((state == sFILES) \
558
? &f_list \
559
: ((state == sDIRS) \
560
? &d_list \
561
: 0))
562
#define NAVIGATE_BINDINGS \
563
DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), \
564
DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), \
565
DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), \
566
DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), \
567
DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), \
568
DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NEXT ), \
569
DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), \
570
DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), \
571
DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), \
572
DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE )
573
574
/*
575
* Display a dialog box for entering a filename
576
*/
577
static int
578
dlg_fselect(const char *title, const char *path, int height, int width, int dselect)
579
{
580
/* *INDENT-OFF* */
581
static DLG_KEYS_BINDING binding[] = {
582
HELPKEY_BINDINGS,
583
ENTERKEY_BINDINGS,
584
NAVIGATE_BINDINGS,
585
TOGGLEKEY_BINDINGS,
586
END_KEYS_BINDING
587
};
588
static DLG_KEYS_BINDING binding2[] = {
589
INPUTSTR_BINDINGS,
590
HELPKEY_BINDINGS,
591
ENTERKEY_BINDINGS,
592
NAVIGATE_BINDINGS,
593
TOGGLEKEY_BINDINGS,
594
END_KEYS_BINDING
595
};
596
/* *INDENT-ON* */
597
598
#ifdef KEY_RESIZE
599
int old_height = height;
600
int old_width = width;
601
bool resized = FALSE;
602
#endif
603
int tbox_y, tbox_x, tbox_width, tbox_height;
604
int dbox_y, dbox_x, dbox_width, dbox_height;
605
int fbox_y, fbox_x, fbox_width, fbox_height;
606
int show_buttons = TRUE;
607
int offset = 0;
608
int key = 0;
609
int fkey = FALSE;
610
int code;
611
int result = DLG_EXIT_UNKNOWN;
612
int state = dialog_vars.default_button >= 0 ? dlg_default_button() : sTEXT;
613
int button;
614
bool first = (state == sTEXT);
615
bool first_trace = TRUE;
616
char *input;
617
char *completed;
618
char current[MAX_LEN + 1];
619
WINDOW *dialog = 0;
620
WINDOW *w_text = 0;
621
WINDOW *w_work = 0;
622
const char **buttons = dlg_ok_labels();
623
const char *d_label = _("Directories");
624
const char *f_label = _("Files");
625
char *partial = 0;
626
int min_wide = MIN_WIDE;
627
int min_items = height ? 0 : 4;
628
LIST d_list, f_list;
629
630
DLG_TRACE(("# %s args:\n", dselect ? "dselect" : "fselect"));
631
DLG_TRACE2S("title", title);
632
DLG_TRACE2S("path", path);
633
DLG_TRACE2N("height", height);
634
DLG_TRACE2N("width", width);
635
636
dlg_does_output();
637
638
/* Set up the initial value */
639
input = dlg_set_result(path);
640
offset = (int) strlen(input);
641
*current = 0;
642
643
dlg_button_layout(buttons, &min_wide);
644
645
#ifdef KEY_RESIZE
646
retry:
647
#endif
648
dlg_auto_size(title, "", &height, &width, MIN_HIGH + min_items, min_wide);
649
650
dlg_print_size(height, width);
651
dlg_ctl_size(height, width);
652
653
dialog = dlg_new_window(height, width,
654
dlg_box_y_ordinate(height),
655
dlg_box_x_ordinate(width));
656
dlg_register_window(dialog, "fselect", binding);
657
dlg_register_buttons(dialog, "fselect", buttons);
658
659
dlg_mouse_setbase(0, 0);
660
661
dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr);
662
dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr);
663
dlg_draw_title(dialog, title);
664
665
dlg_attrset(dialog, dialog_attr);
666
667
/* Draw the input field box */
668
tbox_height = 1;
669
tbox_width = width - (4 * MARGIN + 2);
670
tbox_y = height - (BTN_HIGH * 2) + MARGIN;
671
tbox_x = (width - tbox_width) / 2;
672
673
w_text = dlg_der_window(dialog, tbox_height, tbox_width, tbox_y, tbox_x);
674
if (w_text == 0) {
675
result = DLG_EXIT_ERROR;
676
goto finish;
677
}
678
679
dlg_draw_box(dialog, tbox_y - MARGIN, tbox_x - MARGIN,
680
(2 * MARGIN + 1), tbox_width + (MARGIN + EXT_WIDE),
681
menubox_border_attr, menubox_border2_attr);
682
dlg_mouse_mkbigregion(getbegy(dialog) + tbox_y - MARGIN,
683
getbegx(dialog) + tbox_x - MARGIN,
684
1 + (2 * MARGIN),
685
tbox_width + (MARGIN + EXT_WIDE),
686
MOUSE_T, 1, 1, 3 /* doesn't matter */ );
687
688
dlg_register_window(w_text, "fselect2", binding2);
689
690
/* Draw the directory listing box */
691
if (dselect)
692
dbox_width = (width - (6 * MARGIN));
693
else
694
dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2;
695
dbox_height = height - MIN_HIGH;
696
dbox_y = (2 * MARGIN + 1);
697
dbox_x = tbox_x;
698
699
w_work = dlg_der_window(dialog, dbox_height, dbox_width, dbox_y, dbox_x);
700
if (w_work == 0) {
701
result = DLG_EXIT_ERROR;
702
goto finish;
703
}
704
705
(void) mvwaddstr(dialog, dbox_y - (MARGIN + 1), dbox_x - MARGIN, d_label);
706
dlg_draw_box(dialog,
707
dbox_y - MARGIN, dbox_x - MARGIN,
708
dbox_height + (MARGIN + 1), dbox_width + (MARGIN + 1),
709
menubox_border_attr, menubox_border2_attr);
710
init_list(&d_list, dialog, w_work, MOUSE_D);
711
712
if (!dselect) {
713
/* Draw the filename listing box */
714
fbox_height = dbox_height;
715
fbox_width = dbox_width;
716
fbox_y = dbox_y;
717
fbox_x = tbox_x + dbox_width + (2 * MARGIN);
718
719
w_work = dlg_der_window(dialog, fbox_height, fbox_width, fbox_y, fbox_x);
720
if (w_work == 0) {
721
result = DLG_EXIT_ERROR;
722
goto finish;
723
}
724
725
(void) mvwaddstr(dialog, fbox_y - (MARGIN + 1), fbox_x - MARGIN, f_label);
726
dlg_draw_box(dialog,
727
fbox_y - MARGIN, fbox_x - MARGIN,
728
fbox_height + (MARGIN + 1), fbox_width + (MARGIN + 1),
729
menubox_border_attr, menubox_border2_attr);
730
init_list(&f_list, dialog, w_work, MOUSE_F);
731
} else {
732
memset(&f_list, 0, sizeof(f_list));
733
}
734
735
while (result == DLG_EXIT_UNKNOWN) {
736
737
if (fill_lists(current, input, &d_list, &f_list, state < sTEXT))
738
show_buttons = TRUE;
739
740
#ifdef KEY_RESIZE
741
if (resized) {
742
resized = FALSE;
743
dlg_show_string(w_text, input, offset, inputbox_attr,
744
0, 0, tbox_width, FALSE, first);
745
}
746
#endif
747
748
/*
749
* The last field drawn determines where the cursor is shown:
750
*/
751
if (show_buttons) {
752
show_buttons = FALSE;
753
button = (state < 0) ? 0 : state;
754
dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width);
755
}
756
757
if (first_trace) {
758
first_trace = FALSE;
759
dlg_trace_win(dialog);
760
}
761
762
if (state < 0) {
763
switch (state) {
764
case sTEXT:
765
dlg_set_focus(dialog, w_text);
766
break;
767
case sFILES:
768
dlg_set_focus(dialog, f_list.win);
769
break;
770
case sDIRS:
771
dlg_set_focus(dialog, d_list.win);
772
break;
773
}
774
}
775
776
if (first) {
777
(void) wrefresh(dialog);
778
} else {
779
fix_arrows(&d_list);
780
fix_arrows(&f_list);
781
key = dlg_mouse_wgetch((state == sTEXT) ? w_text : dialog, &fkey);
782
if (dlg_result_key(key, fkey, &result)) {
783
if (!dlg_button_key(result, &button, &key, &fkey))
784
break;
785
}
786
}
787
788
if (key == DLGK_TOGGLE) {
789
key = DLGK_SELECT;
790
fkey = TRUE;
791
}
792
793
if (fkey) {
794
switch (key) {
795
case DLGK_MOUSE(KEY_PREVIOUS):
796
state = sDIRS;
797
scroll_list(-1, which_list());
798
continue;
799
case DLGK_MOUSE(KEY_NEXT):
800
state = sDIRS;
801
scroll_list(1, which_list());
802
continue;
803
case DLGK_MOUSE(KEY_PPAGE):
804
state = sFILES;
805
scroll_list(-1, which_list());
806
continue;
807
case DLGK_MOUSE(KEY_NPAGE):
808
state = sFILES;
809
scroll_list(1, which_list());
810
continue;
811
case DLGK_PAGE_PREV:
812
scroll_list(-1, which_list());
813
continue;
814
case DLGK_PAGE_NEXT:
815
scroll_list(1, which_list());
816
continue;
817
case DLGK_ITEM_PREV:
818
if (change_list(-1, which_list()))
819
continue;
820
/* FALLTHRU */
821
case DLGK_FIELD_PREV:
822
show_buttons = TRUE;
823
do {
824
state = dlg_prev_ok_buttonindex(state, sDIRS);
825
} while (!usable_state(state, &d_list, &f_list));
826
continue;
827
case DLGK_ITEM_NEXT:
828
if (change_list(1, which_list()))
829
continue;
830
/* FALLTHRU */
831
case DLGK_FIELD_NEXT:
832
show_buttons = TRUE;
833
do {
834
state = dlg_next_ok_buttonindex(state, sDIRS);
835
} while (!usable_state(state, &d_list, &f_list));
836
continue;
837
case DLGK_SELECT:
838
completed = 0;
839
if (partial != 0) {
840
free(partial);
841
partial = 0;
842
}
843
if (state == sFILES && !dselect) {
844
completed = data_of(&f_list);
845
} else if (state == sDIRS) {
846
completed = data_of(&d_list);
847
} else {
848
if (complete(input, &d_list, &f_list, &partial)) {
849
completed = partial;
850
}
851
}
852
if (completed != 0) {
853
state = sTEXT;
854
show_buttons = TRUE;
855
strcpy(leaf_of(input), completed);
856
offset = (int) strlen(input);
857
dlg_show_string(w_text, input, offset, inputbox_attr,
858
0, 0, tbox_width, 0, first);
859
if (partial != NULL) {
860
free(partial);
861
partial = 0;
862
}
863
continue;
864
} else { /* if (state < sTEXT) */
865
(void) beep();
866
continue;
867
}
868
/* FALLTHRU */
869
case DLGK_ENTER:
870
result = (state > 0) ? dlg_enter_buttoncode(state) : DLG_EXIT_OK;
871
continue;
872
case DLGK_LEAVE:
873
if (state >= 0)
874
result = dlg_ok_buttoncode(state);
875
break;
876
#ifdef KEY_RESIZE
877
case KEY_RESIZE:
878
dlg_will_resize(dialog);
879
/* reset data */
880
height = old_height;
881
width = old_width;
882
show_buttons = TRUE;
883
*current = 0;
884
resized = TRUE;
885
/* repaint */
886
free_list(&d_list, FALSE);
887
free_list(&f_list, FALSE);
888
_dlg_resize_cleanup(dialog);
889
goto retry;
890
#endif
891
default:
892
if (key >= DLGK_MOUSE(MOUSE_T)) {
893
state = sTEXT;
894
continue;
895
} else if (key >= DLGK_MOUSE(MOUSE_F)) {
896
if (f_list.win != 0) {
897
state = sFILES;
898
f_list.choice = (key - DLGK_MOUSE(MOUSE_F)) + f_list.offset;
899
display_list(&f_list);
900
}
901
continue;
902
} else if (key >= DLGK_MOUSE(MOUSE_D)) {
903
if (d_list.win != 0) {
904
state = sDIRS;
905
d_list.choice = (key - DLGK_MOUSE(MOUSE_D)) + d_list.offset;
906
display_list(&d_list);
907
}
908
continue;
909
} else if (is_DLGK_MOUSE(key)
910
&& (code = dlg_ok_buttoncode(key - M_EVENT)) >= 0) {
911
result = code;
912
continue;
913
}
914
break;
915
}
916
}
917
918
if (state < 0) { /* Input box selected if we're editing */
919
int edit = dlg_edit_string(input, &offset, key, fkey, first);
920
921
if (edit) {
922
dlg_show_string(w_text, input, offset, inputbox_attr,
923
0, 0, tbox_width, 0, first);
924
first = FALSE;
925
state = sTEXT;
926
}
927
} else if ((code = dlg_char_to_button(key, buttons)) >= 0) {
928
result = dlg_ok_buttoncode(code);
929
break;
930
}
931
}
932
AddLastKey();
933
934
dlg_unregister_window(w_text);
935
dlg_del_window(dialog);
936
dlg_mouse_free_regions();
937
free_list(&d_list, FALSE);
938
free_list(&f_list, FALSE);
939
940
finish:
941
if (partial != 0)
942
free(partial);
943
return result;
944
}
945
946
/*
947
* Display a dialog box for entering a filename
948
*/
949
int
950
dialog_fselect(const char *title, const char *path, int height, int width)
951
{
952
return dlg_fselect(title, path, height, width, FALSE);
953
}
954
955
/*
956
* Display a dialog box for entering a directory
957
*/
958
int
959
dialog_dselect(const char *title, const char *path, int height, int width)
960
{
961
return dlg_fselect(title, path, height, width, TRUE);
962
}
963
964