/* Hatari - dlgFileSelect.c This file is distributed under the GNU Public License, version 2 or at your option any later version. Read the file gpl.txt for details. A file selection dialog for the graphical user interface for Hatari. */ const char DlgFileSelect_rcsid[] = "Hatari $Id: dlgFileSelect.c,v 1.17 2007/01/13 11:57:41 thothy Exp $"; #include #include #include #include "main.h" #include "scandir.h" #include "sdlgui.h" #include "file.h" #include "zip.h" #define SGFS_NUMENTRIES 16 /* How many entries are displayed at once */ #define SGFSDLG_FILENAME 5 #define SGFSDLG_UPDIR 6 #define SGFSDLG_HOMEDIR 7 #define SGFSDLG_ROOTDIR 8 #define SGFSDLG_ENTRY1 11 #define SGFSDLG_ENTRY16 26 #define SGFSDLG_UP 27 #define SGFSDLG_DOWN 28 #define SGFSDLG_SHOWHIDDEN 29 #define SGFSDLG_OKAY 30 #define SGFSDLG_CANCEL 31 #define DLGPATH_SIZE 62 static char dlgpath[DLGPATH_SIZE+1]; /* Path name in the dialog */ #define DLGFNAME_SIZE 56 static char dlgfname[DLGFNAME_SIZE+1]; /* Name of the selected file in the dialog */ #define DLGFILENAMES_SIZE 59 static char dlgfilenames[SGFS_NUMENTRIES][DLGFILENAMES_SIZE+1]; /* Visible file names in the dialog */ /* The dialog data: */ static SGOBJ fsdlg[] = { { SGBOX, 0, 0, 0,0, 64,25, NULL }, { SGTEXT, 0, 0, 25,1, 13,1, "Choose a file" }, { SGTEXT, 0, 0, 1,2, 7,1, "Folder:" }, { SGTEXT, 0, 0, 1,3, DLGPATH_SIZE,1, dlgpath }, { SGTEXT, 0, 0, 1,4, 6,1, "File:" }, { SGTEXT, 0, 0, 7,4, DLGFNAME_SIZE,1, dlgfname }, { SGBUTTON, 0, 0, 51,1, 4,1, ".." }, { SGBUTTON, 0, 0, 56,1, 3,1, "~" }, { SGBUTTON, 0, 0, 60,1, 3,1, "/" }, { SGBOX, 0, 0, 1,6, 62,16, NULL }, { SGBOX, 0, 0, 62,7, 1,14, NULL }, { SGTEXT, SG_EXIT, 0, 2,6, DLGFILENAMES_SIZE,1, dlgfilenames[0] }, { SGTEXT, SG_EXIT, 0, 2,7, DLGFILENAMES_SIZE,1, dlgfilenames[1] }, { SGTEXT, SG_EXIT, 0, 2,8, DLGFILENAMES_SIZE,1, dlgfilenames[2] }, { SGTEXT, SG_EXIT, 0, 2,9, DLGFILENAMES_SIZE,1, dlgfilenames[3] }, { SGTEXT, SG_EXIT, 0, 2,10, DLGFILENAMES_SIZE,1, dlgfilenames[4] }, { SGTEXT, SG_EXIT, 0, 2,11, DLGFILENAMES_SIZE,1, dlgfilenames[5] }, { SGTEXT, SG_EXIT, 0, 2,12, DLGFILENAMES_SIZE,1, dlgfilenames[6] }, { SGTEXT, SG_EXIT, 0, 2,13, DLGFILENAMES_SIZE,1, dlgfilenames[7] }, { SGTEXT, SG_EXIT, 0, 2,14, DLGFILENAMES_SIZE,1, dlgfilenames[8] }, { SGTEXT, SG_EXIT, 0, 2,15, DLGFILENAMES_SIZE,1, dlgfilenames[9] }, { SGTEXT, SG_EXIT, 0, 2,16, DLGFILENAMES_SIZE,1, dlgfilenames[10] }, { SGTEXT, SG_EXIT, 0, 2,17, DLGFILENAMES_SIZE,1, dlgfilenames[11] }, { SGTEXT, SG_EXIT, 0, 2,18, DLGFILENAMES_SIZE,1, dlgfilenames[12] }, { SGTEXT, SG_EXIT, 0, 2,19, DLGFILENAMES_SIZE,1, dlgfilenames[13] }, { SGTEXT, SG_EXIT, 0, 2,20, DLGFILENAMES_SIZE,1, dlgfilenames[14] }, { SGTEXT, SG_EXIT, 0, 2,21, DLGFILENAMES_SIZE,1, dlgfilenames[15] }, { SGBUTTON, SG_TOUCHEXIT, 0, 62,6, 1,1, "\x01" }, /* Arrow up */ { SGBUTTON, SG_TOUCHEXIT, 0, 62,21, 1,1, "\x02" }, /* Arrow down */ { SGCHECKBOX, SG_EXIT, SG_SELECTED, 2,23, 18,1, "Show hidden files" }, { SGBUTTON, SG_DEFAULT, 0, 32,23, 8,1, "Okay" }, { SGBUTTON, 0, 0, 50,23, 8,1, "Cancel" }, { -1, 0, 0, 0,0, 0,0, NULL } }; static int ypos; /* First entry number to be displayed */ static BOOL refreshentries; /* Do we have to update the file names in the dialog? */ static int entries; /* How many files are in the actual directory? */ /*-----------------------------------------------------------------------*/ /* Update the file name strings in the dialog. Returns FALSE if it failed, TRUE on success. */ static int DlgFileSelect_RefreshEntries(struct dirent **files, char *path, BOOL browsingzip) { int i; char *tempstr = malloc(FILENAME_MAX); if (!tempstr) { perror("DlgFileSelect_RefreshEntries"); return FALSE; } /* Copy entries to dialog: */ for(i=0; id_name); File_ShrinkName(dlgfilenames[i], tempstr, DLGFILENAMES_SIZE); /* Mark folders: */ strcpy(tempstr, path); strcat(tempstr, files[i+ypos]->d_name); if( browsingzip ) { if (File_DoesFileNameEndWithSlash(tempstr)) dlgfilenames[i][0] = SGFOLDER; /* Mark folders */ } else { if( stat(tempstr, &filestat)==0 && S_ISDIR(filestat.st_mode) ) dlgfilenames[i][0] = SGFOLDER; /* Mark folders */ if (ZIP_FileNameIsZIP(tempstr) && browsingzip == FALSE) dlgfilenames[i][0] = SGFOLDER; /* Mark .ZIP archives as folders */ } } else dlgfilenames[i][0] = 0; /* Clear entry */ } free(tempstr); return TRUE; } /*-----------------------------------------------------------------------*/ /* Remove all hidden files (files with file names that begin with a dot) from the list. */ static void DlgFileSelect_RemoveHiddenFiles(struct dirent **files) { int i; int nActPos = -1; int nOldEntries; nOldEntries = entries; /* Scan list for hidden files and remove them. */ for (i = 0; i < nOldEntries; i++) { /* Does file name start with a dot? -> hidden file! */ if (files[i]->d_name[0] == '.') { if (nActPos == -1) nActPos = i; /* Remove file from list: */ free(files[i]); files[i] = NULL; entries -= 1; } } /* Now close the gaps in the list: */ if (nActPos != -1) { for (i = nActPos; i < nOldEntries; i++) { if (files[i] != NULL) { /* Move entry to earlier position: */ files[nActPos] = files[i]; files[i] = NULL; nActPos += 1; } } } } /*-----------------------------------------------------------------------*/ /* Prepare to scroll up one entry. */ static void DlgFileSelect_ScrollUp(void) { if (ypos > 0) { --ypos; refreshentries = TRUE; } } /*-----------------------------------------------------------------------*/ /* Prepare to scroll down one entry. */ static void DlgFileSelect_ScrollDown(void) { if (ypos+SGFS_NUMENTRIES < entries) { ++ypos; refreshentries = TRUE; } } /*-----------------------------------------------------------------------*/ /* Handle SDL events. */ static void DlgFileSelect_HandleSdlEvents(SDL_Event *pEvent) { switch (pEvent->type) { case SDL_MOUSEBUTTONDOWN: if (pEvent->button.button == SDL_BUTTON_WHEELUP) DlgFileSelect_ScrollUp(); else if (pEvent->button.button == SDL_BUTTON_WHEELDOWN) DlgFileSelect_ScrollDown(); break; case SDL_KEYDOWN: switch (pEvent->key.keysym.sym) { case SDLK_UP: DlgFileSelect_ScrollUp(); break; case SDLK_DOWN: DlgFileSelect_ScrollDown(); break; case SDLK_HOME: ypos = 0; refreshentries = TRUE; break; case SDLK_END: ypos = entries-SGFS_NUMENTRIES; refreshentries = TRUE; break; case SDLK_PAGEUP: if (ypos > SGFS_NUMENTRIES) ypos -= SGFS_NUMENTRIES; else ypos = 0; refreshentries = TRUE; break; case SDLK_PAGEDOWN: if (ypos+2*SGFS_NUMENTRIES < entries) ypos += SGFS_NUMENTRIES; else ypos = entries-SGFS_NUMENTRIES; refreshentries = TRUE; break; default: break; } break; } } /*-----------------------------------------------------------------------*/ /* Show and process a file selection dialog. Returns TRUE if the use selected "okay", FALSE if "cancel". input: zip_path = pointer to buffer to contain file path within a selected zip file, or NULL if browsing zip files is disallowed. bAllowNew: TRUE if the user is allowed to insert new file names. TODO: This function urgently needs refactoring... it's way too big! */ int SDLGui_FileSelect(char *path_and_name, char *zip_path, BOOL bAllowNew) { int i,n; struct dirent **files = NULL; char *pStringMem; char *path, *fname; /* The actual file and path names */ BOOL reloaddir = TRUE; /* Do we have to reload the directory file list? */ int retbut; int oldcursorstate; int selection = -1; /* The actual selection, -1 if none selected */ char *zipfilename; /* Filename in zip file */ char *zipdir; BOOL browsingzip = FALSE; /* Are we browsing an archive? */ zip_dir *zipfiles = NULL; SDL_Event sdlEvent; struct stat filestat; ypos = 0; refreshentries = TRUE; entries = 0; /* Allocate memory for the file and path name strings: */ pStringMem = malloc(4 * FILENAME_MAX); path = pStringMem; fname = pStringMem + FILENAME_MAX; zipfilename = pStringMem + 2 * FILENAME_MAX; zipdir = pStringMem + 3 * FILENAME_MAX; zipfilename[0] = 0; SDLGui_CenterDlg(fsdlg); if (bAllowNew) { fsdlg[SGFSDLG_FILENAME].type = SGEDITFIELD; fsdlg[SGFSDLG_FILENAME].flags |= SG_EXIT; } else { fsdlg[SGFSDLG_FILENAME].type = SGTEXT; fsdlg[SGFSDLG_FILENAME].flags &= ~SG_EXIT; } /* Prepare the path and filename variables */ if (stat(path_and_name, &filestat) == 0 && S_ISDIR(filestat.st_mode)) { /* assure that a directory name ends with a '/' */ File_AddSlashToEndFileName(path_and_name); } File_splitpath(path_and_name, path, fname, NULL); File_MakeAbsoluteName(path); File_MakeValidPathName(path); File_ShrinkName(dlgpath, path, DLGPATH_SIZE); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); /* Save old mouse cursor state and enable cursor anyway */ oldcursorstate = SDL_ShowCursor(SDL_QUERY); if (oldcursorstate == SDL_DISABLE) SDL_ShowCursor(SDL_ENABLE); do { if (reloaddir) { if (strlen(path) >= FILENAME_MAX) { fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n"); free(pStringMem); return FALSE; } /* Free old allocated memory: */ if (files != NULL) { for(i=0; i