1112158Sdas/* 2112158Sdas * $Id: fselect.c,v 1.93 2012/12/30 20:52:25 tom Exp $ 3112158Sdas * 4112158Sdas * fselect.c -- implements the file-selector box 5112158Sdas * 6112158Sdas * Copyright 2000-2011,2012 Thomas E. Dickey 7112158Sdas * 8112158Sdas * This program is free software; you can redistribute it and/or modify 9112158Sdas * it under the terms of the GNU Lesser General Public License, version 2.1 10112158Sdas * as published by the Free Software Foundation. 11112158Sdas * 12112158Sdas * This program is distributed in the hope that it will be useful, but 13112158Sdas * WITHOUT ANY WARRANTY; without even the implied warranty of 14112158Sdas * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15112158Sdas * Lesser General Public License for more details. 16112158Sdas * 17112158Sdas * You should have received a copy of the GNU Lesser General Public 18112158Sdas * License along with this program; if not, write to 19112158Sdas * Free Software Foundation, Inc. 20112158Sdas * 51 Franklin St., Fifth Floor 21112158Sdas * Boston, MA 02110, USA. 22112158Sdas */ 23112158Sdas 24112158Sdas#include <dialog.h> 25112158Sdas#include <dlg_keys.h> 26112158Sdas 27112158Sdas#include <sys/types.h> 28112158Sdas#include <sys/stat.h> 29165743Sdas 30165743Sdas#if HAVE_DIRENT_H 31112158Sdas# include <dirent.h> 32174679Sdas# define NAMLEN(dirent) strlen((dirent)->d_name) 33174679Sdas#else 34112158Sdas# define dirent direct 35165743Sdas# define NAMLEN(dirent) (dirent)->d_namlen 36165743Sdas# if HAVE_SYS_NDIR_H 37165743Sdas# include <sys/ndir.h> 38112158Sdas# endif 39112158Sdas# if HAVE_SYS_DIR_H 40112158Sdas# include <sys/dir.h> 41112158Sdas# endif 42112158Sdas# if HAVE_NDIR_H 43112158Sdas# include <ndir.h> 44112158Sdas# endif 45112158Sdas#endif 46112158Sdas 47182709Sdas# if defined(_FILE_OFFSET_BITS) && defined(HAVE_STRUCT_DIRENT64) 48112158Sdas# if !defined(_LP64) && (_FILE_OFFSET_BITS == 64) 49112158Sdas# define DIRENT struct dirent64 50182709Sdas# else 51112158Sdas# define DIRENT struct dirent 52112158Sdas# endif 53112158Sdas# else 54112158Sdas# define DIRENT struct dirent 55112158Sdas# endif 56112158Sdas 57112158Sdas#define EXT_WIDE 1 58112158Sdas#define HDR_HIGH 1 59112158Sdas#define BTN_HIGH (1 + 2 * MARGIN) /* Ok/Cancel, also input-box */ 60112158Sdas#define MIN_HIGH (HDR_HIGH - MARGIN + (BTN_HIGH * 2) + 4 * MARGIN) 61112158Sdas#define MIN_WIDE (2 * MAX(dlg_count_columns(d_label), dlg_count_columns(f_label)) + 6 * MARGIN + 2 * EXT_WIDE) 62219557Sdas 63219557Sdas#define MOUSE_D (KEY_MAX + 0) 64219557Sdas#define MOUSE_F (KEY_MAX + 10000) 65219557Sdas#define MOUSE_T (KEY_MAX + 20000) 66219557Sdas 67219557Sdastypedef enum { 68219557Sdas sDIRS = -3 69219557Sdas ,sFILES = -2 70219557Sdas ,sTEXT = -1 71219557Sdas} STATES; 72219557Sdas 73219557Sdastypedef struct { 74219557Sdas WINDOW *par; /* parent window */ 75219557Sdas WINDOW *win; /* this window */ 76219557Sdas int length; /* length of the data[] array */ 77219557Sdas int offset; /* index of first item on screen */ 78219557Sdas int choice; /* index of the selection */ 79219557Sdas int mousex; /* base of mouse-code return-values */ 80219557Sdas unsigned allocd; 81219557Sdas char **data; 82219557Sdas} LIST; 83219557Sdas 84112158Sdastypedef struct { 85227753Stheraven int length; 86112158Sdas char **data; 87227753Stheraven} MATCH; 88112158Sdas 89227753Stheravenstatic void 90112158Sdasinit_list(LIST * list, WINDOW *par, WINDOW *win, int mousex) 91112158Sdas{ 92112158Sdas list->par = par; 93112158Sdas list->win = win; 94112158Sdas list->length = 0; 95165743Sdas list->offset = 0; 96112158Sdas list->choice = 0; 97112158Sdas list->mousex = mousex; 98219557Sdas list->allocd = 0; 99112158Sdas list->data = 0; 100219557Sdas dlg_mouse_mkbigregion(getbegy(win), getbegx(win), 101112158Sdas getmaxy(win), getmaxx(win), 102112158Sdas mousex, 1, 1, 1 /* by lines */ ); 103219557Sdas} 104219557Sdas 105219557Sdasstatic char * 106112158Sdasleaf_of(char *path) 107112158Sdas{ 108112158Sdas char *leaf = strrchr(path, '/'); 109187808Sdas if (leaf != 0) 110187808Sdas leaf++; 111227753Stheraven else 112187808Sdas leaf = path; 113187808Sdas return leaf; 114187808Sdas} 115187808Sdas 116187808Sdasstatic char * 117187808Sdasdata_of(LIST * list) 118227753Stheraven{ 119219557Sdas if (list != 0 120187808Sdas && list->data != 0) 121187808Sdas return list->data[list->choice]; 122187808Sdas return 0; 123187808Sdas} 124187808Sdas 125187808Sdasstatic void 126187808Sdasfree_list(LIST * list, int reinit) 127187808Sdas{ 128187808Sdas int n; 129187808Sdas 130187808Sdas if (list->data != 0) { 131182709Sdas for (n = 0; list->data[n] != 0; n++) 132182709Sdas free(list->data[n]); 133182709Sdas free(list->data); 134182709Sdas list->data = 0; 135182709Sdas } 136182709Sdas if (reinit) 137182709Sdas init_list(list, list->par, list->win, list->mousex); 138182709Sdas} 139182709Sdas 140182709Sdasstatic void 141182709Sdasadd_to_list(LIST * list, char *text) 142182709Sdas{ 143182709Sdas unsigned need; 144112158Sdas 145165743Sdas need = (unsigned) (list->length + 1); 146219557Sdas if (need + 1 > list->allocd) { 147112158Sdas list->allocd = 2 * (need + 1); 148112158Sdas if (list->data == 0) { 149112158Sdas list->data = dlg_malloc(char *, list->allocd); 150112158Sdas } else { 151112158Sdas list->data = dlg_realloc(char *, list->allocd, list->data); 152112158Sdas } 153112158Sdas assert_ptr(list->data, "add_to_list"); 154112158Sdas } 155112158Sdas list->data[list->length++] = dlg_strclone(text); 156112158Sdas list->data[list->length] = 0; 157112158Sdas} 158112158Sdas 159112158Sdasstatic void 160112158Sdaskeep_visible(LIST * list) 161112158Sdas{ 162112158Sdas int high = getmaxy(list->win); 163112158Sdas 164112158Sdas if (list->choice < list->offset) { 165112158Sdas list->offset = list->choice; 166112158Sdas } 167112158Sdas if (list->choice - list->offset >= high) 168112158Sdas list->offset = list->choice - high + 1; 169187808Sdas} 170112158Sdas 171112158Sdas#define Value(c) (int)((c) & 0xff) 172112158Sdas 173112158Sdasstatic int 174112158Sdasfind_choice(char *target, LIST * list) 175112158Sdas{ 176112158Sdas int n; 177165743Sdas int choice = list->choice; 178219557Sdas int len_1, len_2, cmp_1, cmp_2; 179165743Sdas 180182709Sdas if (*target == 0) { 181219557Sdas list->choice = 0; 182165743Sdas } else { 183219557Sdas /* find the match with the longest length. If more than one has the 184165743Sdas * same length, choose the one with the closest match of the final 185112158Sdas * character. 186112158Sdas */ 187112158Sdas len_1 = 0; 188112158Sdas cmp_1 = 256; 189112158Sdas for (n = 0; n < list->length; n++) { 190112158Sdas char *a = target; 191124703Sdas char *b = list->data[n]; 192124703Sdas 193124703Sdas len_2 = 0; 194124703Sdas while ((*a != 0) && (*b != 0) && (*a == *b)) { 195112158Sdas a++; 196165743Sdas b++; 197112158Sdas len_2++; 198112158Sdas } 199112158Sdas cmp_2 = Value(*a) - Value(*b); 200187808Sdas if (cmp_2 < 0) 201112158Sdas cmp_2 = -cmp_2; 202112158Sdas if ((len_2 > len_1) 203112158Sdas || (len_1 == len_2 && cmp_2 < cmp_1)) { 204112158Sdas len_1 = len_2; 205112158Sdas cmp_1 = cmp_2; 206112158Sdas list->choice = n; 207112158Sdas } 208112158Sdas } 209112158Sdas } 210112158Sdas if (choice != list->choice) { 211112158Sdas keep_visible(list); 212112158Sdas } 213112158Sdas return (choice != list->choice); 214112158Sdas} 215187808Sdas 216187808Sdasstatic void 217187808Sdasdisplay_list(LIST * list) 218187808Sdas{ 219187808Sdas int n; 220187808Sdas int x; 221112415Sdas int y; 222187808Sdas int top; 223187808Sdas int bottom; 224112158Sdas 225165743Sdas if (list->win != 0) { 226112158Sdas dlg_attr_clear(list->win, getmaxy(list->win), getmaxx(list->win), item_attr); 227112158Sdas for (n = list->offset; n < list->length && list->data[n]; n++) { 228112158Sdas y = n - list->offset; 229112158Sdas if (y >= getmaxy(list->win)) 230112158Sdas break; 231112158Sdas (void) wmove(list->win, y, 0); 232112158Sdas if (n == list->choice) 233112158Sdas (void) wattrset(list->win, item_selected_attr); 234112158Sdas (void) waddstr(list->win, list->data[n]); 235112158Sdas (void) wattrset(list->win, item_attr); 236112158Sdas } 237112158Sdas (void) wattrset(list->win, item_attr); 238112158Sdas 239112158Sdas getparyx(list->win, y, x); 240112158Sdas 241112158Sdas top = y - 1; 242112158Sdas bottom = y + getmaxy(list->win); 243112158Sdas dlg_draw_scrollbar(list->par, 244112158Sdas (long) list->offset, 245112158Sdas (long) list->offset, 246112158Sdas (long) (list->offset + getmaxy(list->win)), 247112158Sdas (long) (list->length), 248112158Sdas x + 1, 249112158Sdas x + getmaxx(list->win), 250112158Sdas top, 251112158Sdas bottom, 252112158Sdas menubox_border2_attr, 253112158Sdas menubox_border_attr); 254187808Sdas 255112158Sdas (void) wmove(list->win, list->choice - list->offset, 0); 256112158Sdas (void) wnoutrefresh(list->win); 257112158Sdas } 258112158Sdas} 259112158Sdas 260112158Sdas/* FIXME: see arrows.c 261112158Sdas * This workaround is used to allow two lists to have scroll-tabs at the same 262112158Sdas * time, by reassigning their return-values to be different. Just for 263112158Sdas * readability, we use the names of keys with similar connotations, though all 264112158Sdas * that is really required is that they're distinct, so we can put them in a 265112158Sdas * switch statement. 266112158Sdas */ 267112158Sdasstatic void 268112158Sdasfix_arrows(LIST * list) 269112158Sdas{ 270112158Sdas int x; 271112158Sdas int y; 272112158Sdas int top; 273112158Sdas int right; 274112158Sdas int bottom; 275112158Sdas 276112158Sdas if (list->win != 0) { 277112158Sdas getparyx(list->win, y, x); 278112158Sdas top = y - 1; 279112158Sdas right = getmaxx(list->win); 280112158Sdas bottom = y + getmaxy(list->win); 281112158Sdas 282112158Sdas mouse_mkbutton(top, x, right, 283112158Sdas ((list->mousex == MOUSE_D) 284112158Sdas ? KEY_PREVIOUS 285112158Sdas : KEY_PPAGE)); 286112158Sdas mouse_mkbutton(bottom, x, right, 287112158Sdas ((list->mousex == MOUSE_D) 288112158Sdas ? KEY_NEXT 289112158Sdas : KEY_NPAGE)); 290112158Sdas } 291112158Sdas} 292112158Sdas 293112158Sdasstatic int 294112158Sdasshow_list(char *target, LIST * list, int keep) 295112158Sdas{ 296112158Sdas int changed = keep || find_choice(target, list); 297112158Sdas display_list(list); 298112158Sdas return changed; 299112158Sdas} 300165743Sdas 301165743Sdas/* 302112158Sdas * Highlight the closest match to 'target' in the given list, setting offset 303112158Sdas * to match. 304112158Sdas */ 305112158Sdasstatic int 306112158Sdasshow_both_lists(char *input, LIST * d_list, LIST * f_list, int keep) 307112158Sdas{ 308219557Sdas char *leaf = leaf_of(input); 309219557Sdas 310112158Sdas return show_list(leaf, d_list, keep) | show_list(leaf, f_list, keep); 311112158Sdas} 312112158Sdas 313112158Sdas/* 314112158Sdas * Move up/down in the given list 315112158Sdas */ 316112158Sdasstatic bool 317112158Sdaschange_list(int choice, LIST * list) 318112158Sdas{ 319112158Sdas if (data_of(list) != 0) { 320219557Sdas int last = list->length - 1; 321219557Sdas 322112158Sdas choice += list->choice; 323112158Sdas if (choice < 0) 324165743Sdas choice = 0; 325219557Sdas if (choice > last) 326219557Sdas choice = last; 327165743Sdas list->choice = choice; 328112158Sdas keep_visible(list); 329112158Sdas display_list(list); 330112158Sdas return TRUE; 331112158Sdas } 332112158Sdas return FALSE; 333112158Sdas} 334112158Sdas 335112158Sdasstatic void 336112158Sdasscroll_list(int direction, LIST * list) 337112158Sdas{ 338112158Sdas if (data_of(list) != 0) { 339112158Sdas int length = getmaxy(list->win); 340112158Sdas if (change_list(direction * length, list)) 341112158Sdas return; 342112158Sdas } 343112158Sdas beep(); 344112158Sdas} 345112158Sdas 346112158Sdasstatic int 347112158Sdascompar(const void *a, const void *b) 348112158Sdas{ 349112158Sdas return strcmp(*(const char *const *) a, *(const char *const *) b); 350219557Sdas} 351112158Sdas 352112158Sdasstatic void 353112158Sdasmatch(char *name, LIST * d_list, LIST * f_list, MATCH * match_list) 354112158Sdas{ 355112158Sdas char *test = leaf_of(name); 356219557Sdas size_t test_len = strlen(test); 357112158Sdas char **matches = dlg_malloc(char *, (size_t) (d_list->length + f_list->length)); 358112158Sdas size_t data_len = 0; 359112158Sdas int i; 360112158Sdas for (i = 2; i < d_list->length; i++) { 361112158Sdas if (strncmp(test, d_list->data[i], test_len) == 0) { 362112158Sdas matches[data_len++] = d_list->data[i]; 363112158Sdas } 364112158Sdas } 365112158Sdas for (i = 0; i < f_list->length; i++) { 366112158Sdas if (strncmp(test, f_list->data[i], test_len) == 0) { 367112158Sdas matches[data_len++] = f_list->data[i]; 368219557Sdas } 369112158Sdas } 370112158Sdas matches = dlg_realloc(char *, data_len + 1, matches); 371112158Sdas match_list->data = matches; 372112158Sdas match_list->length = (int) data_len; 373112158Sdas} 374112158Sdas 375112158Sdasstatic void 376112158Sdasfree_match(MATCH * match_list) 377219557Sdas{ 378112158Sdas free(match_list->data); 379112158Sdas match_list->length = 0; 380112158Sdas} 381219557Sdas 382112158Sdasstatic int 383112158Sdascomplete(char *name, LIST * d_list, LIST * f_list, char **buff_ptr) 384112158Sdas{ 385112158Sdas MATCH match_list; 386112158Sdas char *test; 387112158Sdas size_t test_len; 388112158Sdas size_t i; 389112158Sdas int j; 390112158Sdas char *buff; 391112158Sdas 392112158Sdas match(name, d_list, f_list, &match_list); 393219557Sdas if (match_list.length == 0) { 394112158Sdas *buff_ptr = NULL; 395112158Sdas return 0; 396112158Sdas } 397112158Sdas 398219557Sdas test = match_list.data[0]; 399112158Sdas test_len = strlen(test); 400112158Sdas buff = dlg_malloc(char, test_len + 2); 401112158Sdas if (match_list.length == 1) { 402112158Sdas strcpy(buff, test); 403112158Sdas i = test_len; 404219557Sdas if (test == data_of(d_list)) { 405219557Sdas buff[test_len] = '/'; 406219557Sdas i++; 407112158Sdas } 408112158Sdas } else { 409219557Sdas for (i = 0; i < test_len; i++) { 410112158Sdas char test_char = test[i]; 411219557Sdas if (test_char == '\0') 412112158Sdas break; 413112158Sdas for (j = 0; j < match_list.length; j++) { 414112158Sdas if (match_list.data[j][i] != test_char) { 415112158Sdas break; 416112158Sdas } 417112158Sdas } 418112158Sdas if (j == match_list.length) { 419112158Sdas (buff)[i] = test_char; 420112158Sdas } else 421219557Sdas break; 422112158Sdas } 423112158Sdas buff = dlg_realloc(char, i + 1, buff); 424112158Sdas } 425219557Sdas free_match(&match_list); 426112158Sdas buff[i] = '\0'; 427112158Sdas *buff_ptr = buff; 428112158Sdas return (i != 0); 429219557Sdas} 430112158Sdas 431112158Sdasstatic bool 432112158Sdasfill_lists(char *current, char *input, LIST * d_list, LIST * f_list, int keep) 433112158Sdas{ 434112158Sdas bool result = TRUE; 435112158Sdas bool rescan = FALSE; 436112158Sdas DIR *dp; 437112158Sdas DIRENT *de; 438112158Sdas struct stat sb; 439112158Sdas int n; 440112158Sdas char path[MAX_LEN + 1]; 441112158Sdas char *leaf; 442112158Sdas 443182709Sdas /* check if we've updated the lists */ 444112158Sdas for (n = 0; current[n] && input[n]; n++) { 445182709Sdas if (current[n] != input[n]) 446112158Sdas break; 447182709Sdas } 448182709Sdas 449112158Sdas if (current[n] == input[n]) { 450112158Sdas result = FALSE; 451112158Sdas rescan = (n == 0 && d_list->length == 0); 452112158Sdas } else if (strchr(current + n, '/') == 0 453112158Sdas && strchr(input + n, '/') == 0) { 454112158Sdas result = show_both_lists(input, d_list, f_list, keep); 455112158Sdas } else { 456112158Sdas rescan = TRUE; 457219557Sdas } 458112158Sdas 459112158Sdas if (rescan) { 460112158Sdas size_t have = strlen(input); 461112158Sdas 462112158Sdas if (have > MAX_LEN) 463112158Sdas have = MAX_LEN; 464182709Sdas memcpy(current, input, have); 465112158Sdas current[have] = '\0'; 466112158Sdas 467219557Sdas /* refill the lists */ 468219557Sdas free_list(d_list, TRUE); 469112158Sdas free_list(f_list, TRUE); 470112158Sdas memcpy(path, current, have); 471219557Sdas path[have] = '\0'; 472219557Sdas if ((leaf = strrchr(path, '/')) != 0) { 473112158Sdas *++leaf = 0; 474112158Sdas } else { 475219557Sdas strcpy(path, "./"); 476219557Sdas leaf = path + strlen(path); 477112158Sdas } 478112158Sdas dlg_trace_msg("opendir '%s'\n", path); 479112158Sdas if ((dp = opendir(path)) != 0) { 480219557Sdas while ((de = readdir(dp)) != 0) { 481219557Sdas strncpy(leaf, de->d_name, NAMLEN(de))[NAMLEN(de)] = 0; 482112158Sdas if (stat(path, &sb) == 0) { 483112158Sdas if ((sb.st_mode & S_IFMT) == S_IFDIR) 484219557Sdas add_to_list(d_list, leaf); 485219557Sdas else if (f_list->win) 486112158Sdas add_to_list(f_list, leaf); 487219557Sdas } 488219557Sdas } 489219557Sdas (void) closedir(dp); 490219557Sdas /* sort the lists */ 491219557Sdas if (d_list->data != 0 && d_list->length > 1) { 492219557Sdas qsort(d_list->data, 493219557Sdas (size_t) d_list->length, 494219557Sdas sizeof(d_list->data[0]), 495219557Sdas compar); 496219557Sdas } 497219557Sdas if (f_list->data != 0 && f_list->length > 1) { 498112158Sdas qsort(f_list->data, 499112158Sdas (size_t) f_list->length, 500112158Sdas sizeof(f_list->data[0]), 501112158Sdas compar); 502112158Sdas } 503219557Sdas } 504112158Sdas 505219557Sdas (void) show_both_lists(input, d_list, f_list, FALSE); 506219557Sdas d_list->offset = d_list->choice; 507219557Sdas f_list->offset = f_list->choice; 508112158Sdas result = TRUE; 509112158Sdas } 510112158Sdas return result; 511112158Sdas} 512112158Sdas 513219557Sdasstatic bool 514219557Sdasusable_state(int state, LIST * dirs, LIST * files) 515112158Sdas{ 516112158Sdas bool result; 517219557Sdas 518112158Sdas switch (state) { 519112158Sdas case sDIRS: 520112158Sdas result = (dirs->win != 0) && (data_of(dirs) != 0); 521112158Sdas break; 522112158Sdas case sFILES: 523219557Sdas result = (files->win != 0) && (data_of(files) != 0); 524112158Sdas break; 525112158Sdas default: 526112158Sdas result = TRUE; 527112158Sdas break; 528112158Sdas } 529112158Sdas return result; 530112158Sdas} 531112158Sdas 532219557Sdas#define which_list() ((state == sFILES) \ 533219557Sdas ? &f_list \ 534112158Sdas : ((state == sDIRS) \ 535112158Sdas ? &d_list \ 536112158Sdas : 0)) 537219557Sdas#define NAVIGATE_BINDINGS \ 538112158Sdas DLG_KEYS_DATA( DLGK_FIELD_NEXT, KEY_RIGHT ), \ 539219557Sdas DLG_KEYS_DATA( DLGK_FIELD_NEXT, TAB ), \ 540112158Sdas DLG_KEYS_DATA( DLGK_FIELD_PREV, KEY_BTAB ), \ 541219557Sdas DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_DOWN ), \ 542112158Sdas DLG_KEYS_DATA( DLGK_ITEM_NEXT, CHR_NEXT ), \ 543112158Sdas DLG_KEYS_DATA( DLGK_ITEM_NEXT, KEY_NEXT ), \ 544219557Sdas DLG_KEYS_DATA( DLGK_ITEM_PREV, CHR_PREVIOUS ), \ 545112158Sdas DLG_KEYS_DATA( DLGK_ITEM_PREV, KEY_UP ), \ 546112158Sdas DLG_KEYS_DATA( DLGK_PAGE_NEXT, KEY_NPAGE ), \ 547112158Sdas DLG_KEYS_DATA( DLGK_PAGE_PREV, KEY_PPAGE ) 548112158Sdas 549219557Sdas/* 550112158Sdas * Display a dialog box for entering a filename 551219557Sdas */ 552219557Sdasstatic int 553219557Sdasdlg_fselect(const char *title, const char *path, int height, int width, int dselect) 554219557Sdas{ 555219557Sdas /* *INDENT-OFF* */ 556112158Sdas static DLG_KEYS_BINDING binding[] = { 557219557Sdas HELPKEY_BINDINGS, 558112158Sdas ENTERKEY_BINDINGS, 559219557Sdas NAVIGATE_BINDINGS, 560219557Sdas END_KEYS_BINDING 561112158Sdas }; 562112158Sdas static DLG_KEYS_BINDING binding2[] = { 563219557Sdas INPUTSTR_BINDINGS, 564219557Sdas HELPKEY_BINDINGS, 565112158Sdas ENTERKEY_BINDINGS, 566112158Sdas NAVIGATE_BINDINGS, 567112158Sdas END_KEYS_BINDING 568112158Sdas }; 569112158Sdas /* *INDENT-ON* */ 570112158Sdas 571112158Sdas#ifdef KEY_RESIZE 572112158Sdas int old_height = height; 573112158Sdas int old_width = width; 574112158Sdas bool resized = FALSE; 575112158Sdas#endif 576112158Sdas int tbox_y, tbox_x, tbox_width, tbox_height; 577187808Sdas int dbox_y, dbox_x, dbox_width, dbox_height; 578112158Sdas int fbox_y, fbox_x, fbox_width, fbox_height; 579112158Sdas int show_buttons = TRUE; 580112158Sdas int offset = 0; 581112158Sdas int key = 0; 582219557Sdas int fkey = FALSE; 583112158Sdas int code; 584112158Sdas int result = DLG_EXIT_UNKNOWN; 585112158Sdas int state = dialog_vars.default_button >= 0 ? dlg_default_button() : sTEXT; 586112158Sdas int button; 587112158Sdas int first = (state == sTEXT); 588112158Sdas int first_trace = TRUE; 589112158Sdas char *input; 590112158Sdas char *completed; 591112158Sdas char current[MAX_LEN + 1]; 592112158Sdas WINDOW *dialog = 0; 593112158Sdas WINDOW *w_text = 0; 594112158Sdas WINDOW *w_work = 0; 595112158Sdas const char **buttons = dlg_ok_labels(); 596112158Sdas const char *d_label = _("Directories"); 597112158Sdas const char *f_label = _("Files"); 598112158Sdas char *partial = 0; 599182709Sdas int min_wide = MIN_WIDE; 600112158Sdas int min_items = height ? 0 : 4; 601112158Sdas LIST d_list, f_list; 602112158Sdas 603219557Sdas dlg_does_output(); 604219557Sdas 605112158Sdas /* Set up the initial value */ 606112158Sdas input = dlg_set_result(path); 607219557Sdas offset = (int) strlen(input); 608219557Sdas *current = 0; 609219557Sdas 610219557Sdas dlg_button_layout(buttons, &min_wide); 611219557Sdas 612219557Sdas#ifdef KEY_RESIZE 613219557Sdas retry: 614219557Sdas#endif 615219557Sdas dlg_auto_size(title, (char *) 0, &height, &width, 6, 25); 616112158Sdas height += MIN_HIGH + min_items; 617112158Sdas if (width < min_wide) 618112158Sdas width = min_wide; 619112158Sdas dlg_print_size(height, width); 620112158Sdas dlg_ctl_size(height, width); 621112158Sdas 622112158Sdas dialog = dlg_new_window(height, width, 623112158Sdas dlg_box_y_ordinate(height), 624112158Sdas dlg_box_x_ordinate(width)); 625219557Sdas dlg_register_window(dialog, "fselect", binding); 626112158Sdas dlg_register_buttons(dialog, "fselect", buttons); 627112158Sdas 628112158Sdas dlg_mouse_setbase(0, 0); 629112158Sdas 630112158Sdas dlg_draw_box2(dialog, 0, 0, height, width, dialog_attr, border_attr, border2_attr); 631112158Sdas dlg_draw_bottom_box2(dialog, border_attr, border2_attr, dialog_attr); 632112158Sdas dlg_draw_title(dialog, title); 633112158Sdas 634112158Sdas (void) wattrset(dialog, dialog_attr); 635112158Sdas 636112158Sdas /* Draw the input field box */ 637112158Sdas tbox_height = 1; 638112158Sdas tbox_width = width - (4 * MARGIN + 2); 639112158Sdas tbox_y = height - (BTN_HIGH * 2) + MARGIN; 640112158Sdas tbox_x = (width - tbox_width) / 2; 641112158Sdas 642112158Sdas w_text = derwin(dialog, tbox_height, tbox_width, tbox_y, tbox_x); 643112158Sdas if (w_text == 0) { 644112158Sdas result = DLG_EXIT_ERROR; 645112158Sdas goto finish; 646112158Sdas } 647112158Sdas 648112158Sdas (void) keypad(w_text, TRUE); 649112158Sdas dlg_draw_box(dialog, tbox_y - MARGIN, tbox_x - MARGIN, 650112158Sdas (2 * MARGIN + 1), tbox_width + (MARGIN + EXT_WIDE), 651112158Sdas menubox_border_attr, menubox_border2_attr); 652112158Sdas dlg_mouse_mkbigregion(getbegy(dialog) + tbox_y - MARGIN, 653112158Sdas getbegx(dialog) + tbox_x - MARGIN, 654112158Sdas 1 + (2 * MARGIN), 655112158Sdas tbox_width + (MARGIN + EXT_WIDE), 656112158Sdas MOUSE_T, 1, 1, 3 /* doesn't matter */ ); 657112158Sdas 658112158Sdas dlg_register_window(w_text, "fselect2", binding2); 659112158Sdas 660112158Sdas /* Draw the directory listing box */ 661112158Sdas if (dselect) 662112158Sdas dbox_width = (width - (6 * MARGIN)); 663112158Sdas else 664182709Sdas dbox_width = (width - (6 * MARGIN + 2 * EXT_WIDE)) / 2; 665112158Sdas dbox_height = height - MIN_HIGH; 666112158Sdas dbox_y = (2 * MARGIN + 1); 667112158Sdas dbox_x = tbox_x; 668112158Sdas 669112158Sdas w_work = derwin(dialog, dbox_height, dbox_width, dbox_y, dbox_x); 670112158Sdas if (w_work == 0) { 671112158Sdas result = DLG_EXIT_ERROR; 672112158Sdas goto finish; 673112158Sdas } 674182709Sdas 675112158Sdas (void) keypad(w_work, TRUE); 676219557Sdas (void) mvwaddstr(dialog, dbox_y - (MARGIN + 1), dbox_x - MARGIN, d_label); 677112158Sdas dlg_draw_box(dialog, 678112158Sdas dbox_y - MARGIN, dbox_x - MARGIN, 679112158Sdas dbox_height + (MARGIN + 1), dbox_width + (MARGIN + 1), 680112158Sdas menubox_border_attr, menubox_border2_attr); 681219557Sdas init_list(&d_list, dialog, w_work, MOUSE_D); 682219557Sdas 683219557Sdas if (!dselect) { 684219557Sdas /* Draw the filename listing box */ 685112158Sdas fbox_height = dbox_height; 686112158Sdas fbox_width = dbox_width; 687112158Sdas fbox_y = dbox_y; 688112158Sdas fbox_x = tbox_x + dbox_width + (2 * MARGIN); 689112158Sdas 690112158Sdas w_work = derwin(dialog, fbox_height, fbox_width, fbox_y, fbox_x); 691112158Sdas if (w_work == 0) { 692112158Sdas result = DLG_EXIT_ERROR; 693219557Sdas goto finish; 694112158Sdas } 695112158Sdas 696112158Sdas (void) keypad(w_work, TRUE); 697112158Sdas (void) mvwaddstr(dialog, fbox_y - (MARGIN + 1), fbox_x - MARGIN, f_label); 698219557Sdas dlg_draw_box(dialog, 699112158Sdas fbox_y - MARGIN, fbox_x - MARGIN, 700219557Sdas fbox_height + (MARGIN + 1), fbox_width + (MARGIN + 1), 701112158Sdas menubox_border_attr, menubox_border2_attr); 702112158Sdas init_list(&f_list, dialog, w_work, MOUSE_F); 703219557Sdas } else { 704112158Sdas memset(&f_list, 0, sizeof(f_list)); 705219557Sdas } 706219557Sdas 707219557Sdas while (result == DLG_EXIT_UNKNOWN) { 708112158Sdas 709112158Sdas if (fill_lists(current, input, &d_list, &f_list, state < sTEXT)) 710112158Sdas show_buttons = TRUE; 711112158Sdas 712219557Sdas#ifdef KEY_RESIZE 713112158Sdas if (resized) { 714112158Sdas resized = FALSE; 715112158Sdas dlg_show_string(w_text, input, offset, inputbox_attr, 716219557Sdas 0, 0, tbox_width, (bool) 0, (bool) first); 717219557Sdas } 718219557Sdas#endif 719219557Sdas 720219557Sdas /* 721219557Sdas * The last field drawn determines where the cursor is shown: 722219557Sdas */ 723182709Sdas if (show_buttons) { 724112158Sdas show_buttons = FALSE; 725219557Sdas button = (state < 0) ? 0 : state; 726112158Sdas dlg_draw_buttons(dialog, height - 2, 0, buttons, button, FALSE, width); 727112158Sdas } 728112158Sdas 729219557Sdas if (first_trace) { 730219557Sdas first_trace = FALSE; 731112158Sdas dlg_trace_win(dialog); 732112158Sdas } 733219557Sdas 734219557Sdas if (state < 0) { 735219557Sdas switch (state) { 736112158Sdas case sTEXT: 737219557Sdas dlg_set_focus(dialog, w_text); 738112158Sdas break; 739219557Sdas case sFILES: 740219557Sdas dlg_set_focus(dialog, f_list.win); 741112158Sdas break; 742112158Sdas case sDIRS: 743112158Sdas dlg_set_focus(dialog, d_list.win); 744112158Sdas break; 745219557Sdas } 746182709Sdas } 747219557Sdas 748182709Sdas if (first) { 749219557Sdas (void) wrefresh(dialog); 750182709Sdas } else { 751112158Sdas fix_arrows(&d_list); 752219557Sdas fix_arrows(&f_list); 753112158Sdas key = dlg_mouse_wgetch((state == sTEXT) ? w_text : dialog, &fkey); 754112158Sdas if (dlg_result_key(key, fkey, &result)) 755112158Sdas break; 756112158Sdas } 757112158Sdas 758112158Sdas if (!fkey && key == ' ') { 759112158Sdas key = DLGK_SELECT; 760112158Sdas fkey = TRUE; 761219557Sdas } 762112158Sdas 763112158Sdas if (fkey) { 764219557Sdas switch (key) { 765112158Sdas case DLGK_MOUSE(KEY_PREVIOUS): 766219557Sdas state = sDIRS; 767112158Sdas scroll_list(-1, which_list()); 768112158Sdas continue; 769112158Sdas case DLGK_MOUSE(KEY_NEXT): 770112158Sdas state = sDIRS; 771112158Sdas scroll_list(1, which_list()); 772112158Sdas continue; 773112158Sdas case DLGK_MOUSE(KEY_PPAGE): 774112158Sdas state = sFILES; 775112158Sdas scroll_list(-1, which_list()); 776112158Sdas continue; 777112158Sdas case DLGK_MOUSE(KEY_NPAGE): 778112158Sdas state = sFILES; 779112158Sdas scroll_list(1, which_list()); 780112158Sdas continue; 781112158Sdas case DLGK_PAGE_PREV: 782112158Sdas scroll_list(-1, which_list()); 783112158Sdas continue; 784112158Sdas case DLGK_PAGE_NEXT: 785112158Sdas scroll_list(1, which_list()); 786112158Sdas continue; 787112158Sdas case DLGK_ITEM_PREV: 788112158Sdas if (change_list(-1, which_list())) 789112158Sdas continue; 790112158Sdas /* FALLTHRU */ 791219557Sdas case DLGK_FIELD_PREV: 792219557Sdas show_buttons = TRUE; 793112158Sdas do { 794219557Sdas state = dlg_prev_ok_buttonindex(state, sDIRS); 795112158Sdas } while (!usable_state(state, &d_list, &f_list)); 796112158Sdas continue; 797112158Sdas case DLGK_ITEM_NEXT: 798112158Sdas if (change_list(1, which_list())) 799219557Sdas continue; 800219557Sdas /* FALLTHRU */ 801219557Sdas case DLGK_FIELD_NEXT: 802112158Sdas show_buttons = TRUE; 803112158Sdas do { 804112158Sdas state = dlg_next_ok_buttonindex(state, sDIRS); 805112158Sdas } while (!usable_state(state, &d_list, &f_list)); 806112158Sdas continue; 807219557Sdas case DLGK_SELECT: 808112158Sdas completed = 0; 809112158Sdas if (partial != 0) { 810112158Sdas free(partial); 811112158Sdas partial = 0; 812112158Sdas } 813112158Sdas if (state == sFILES && !dselect) { 814219557Sdas completed = data_of(&f_list); 815112158Sdas } else if (state == sDIRS) { 816112158Sdas completed = data_of(&d_list); 817112158Sdas } else { 818219557Sdas if (complete(input, &d_list, &f_list, &partial)) { 819112158Sdas completed = partial; 820112158Sdas } 821112158Sdas } 822112158Sdas if (completed != 0) { 823112158Sdas state = sTEXT; 824112158Sdas show_buttons = TRUE; 825112158Sdas strcpy(leaf_of(input), completed); 826112158Sdas offset = (int) strlen(input); 827112158Sdas dlg_show_string(w_text, input, offset, inputbox_attr, 828112158Sdas 0, 0, tbox_width, 0, first); 829112158Sdas if (partial != NULL) { 830112158Sdas free(partial); 831112158Sdas partial = 0; 832112158Sdas } 833219557Sdas continue; 834112158Sdas } else { /* if (state < sTEXT) */ 835112158Sdas (void) beep(); 836112158Sdas continue; 837112158Sdas } 838112158Sdas /* FALLTHRU */ 839112158Sdas case DLGK_ENTER: 840112158Sdas result = (state > 0) ? dlg_enter_buttoncode(state) : DLG_EXIT_OK; 841112158Sdas continue; 842112158Sdas#ifdef KEY_RESIZE 843112158Sdas case KEY_RESIZE: 844219557Sdas /* reset data */ 845182709Sdas height = old_height; 846219557Sdas width = old_width; 847219557Sdas show_buttons = TRUE; 848112158Sdas *current = 0; 849112158Sdas resized = TRUE; 850112158Sdas /* repaint */ 851112158Sdas dlg_clear(); 852112158Sdas dlg_del_window(dialog); 853112158Sdas refresh(); 854112158Sdas dlg_mouse_free_regions(); 855219557Sdas goto retry; 856219557Sdas#endif 857219557Sdas default: 858219557Sdas if (key >= DLGK_MOUSE(MOUSE_T)) { 859219557Sdas state = sTEXT; 860219557Sdas continue; 861112158Sdas } else if (key >= DLGK_MOUSE(MOUSE_F)) { 862219557Sdas if (f_list.win != 0) { 863219557Sdas state = sFILES; 864219557Sdas f_list.choice = (key - DLGK_MOUSE(MOUSE_F)) + f_list.offset; 865112158Sdas display_list(&f_list); 866219557Sdas } 867112158Sdas continue; 868219557Sdas } else if (key >= DLGK_MOUSE(MOUSE_D)) { 869219557Sdas if (d_list.win != 0) { 870219557Sdas state = sDIRS; 871219557Sdas d_list.choice = (key - DLGK_MOUSE(MOUSE_D)) + d_list.offset; 872219557Sdas display_list(&d_list); 873112158Sdas } 874112158Sdas continue; 875219557Sdas } else if (is_DLGK_MOUSE(key) 876219557Sdas && (code = dlg_ok_buttoncode(key - M_EVENT)) >= 0) { 877219557Sdas result = code; 878219557Sdas continue; 879219557Sdas } 880112158Sdas break; 881219557Sdas } 882112158Sdas } 883112158Sdas 884112158Sdas if (state < 0) { /* Input box selected if we're editing */ 885112158Sdas int edit = dlg_edit_string(input, &offset, key, fkey, first); 886112158Sdas 887112158Sdas if (edit) { 888112158Sdas dlg_show_string(w_text, input, offset, inputbox_attr, 889112158Sdas 0, 0, tbox_width, 0, first); 890112158Sdas first = FALSE; 891112158Sdas state = sTEXT; 892112158Sdas } 893219557Sdas } else if (state >= 0 && 894219557Sdas (code = dlg_char_to_button(key, buttons)) >= 0) { 895112158Sdas result = dlg_ok_buttoncode(code); 896219557Sdas break; 897112158Sdas } 898112158Sdas } 899112158Sdas 900219557Sdas dlg_unregister_window(w_text); 901112158Sdas dlg_del_window(dialog); 902112158Sdas dlg_mouse_free_regions(); 903112158Sdas free_list(&d_list, FALSE); 904112158Sdas free_list(&f_list, FALSE); 905112158Sdas 906112158Sdas finish: 907112158Sdas if (partial != 0) 908112158Sdas free(partial); 909112158Sdas return result; 910219557Sdas} 911112158Sdas 912112158Sdas/* 913112158Sdas * Display a dialog box for entering a filename 914112158Sdas */ 915219557Sdasint 916112158Sdasdialog_fselect(const char *title, const char *path, int height, int width) 917112158Sdas{ 918112158Sdas return dlg_fselect(title, path, height, width, FALSE); 919219557Sdas} 920112158Sdas 921112158Sdas/* 922112158Sdas * Display a dialog box for entering a directory 923219557Sdas */ 924112158Sdasint 925112158Sdasdialog_dselect(const char *title, const char *path, int height, int width) 926112158Sdas{ 927219557Sdas return dlg_fselect(title, path, height, width, TRUE); 928112158Sdas} 929112158Sdas