--- hatari/src/gui-sdl/dlgFileSelect.c 2019/04/01 07:11:38 1.1.1.1 +++ hatari/src/gui-sdl/dlgFileSelect.c 2019/04/09 08:49:43 1.1.1.9 @@ -6,31 +6,39 @@ A file selection dialog for the graphical user interface for Hatari. */ -char DlgFileSelect_rcsid[] = "Hatari $Id: dlgFileSelect.c,v 1.1.1.1 2019/04/01 07:11:38 root Exp $"; +const char DlgFileSelect_fileid[] = "Hatari dlgFileSelect.c : " __DATE__ " " __TIME__; #include #include #include -#include #include "main.h" -#include "screen.h" +#include "scandir.h" #include "sdlgui.h" #include "file.h" +#include "paths.h" #include "zip.h" -#define SGFSDLG_FILENAME 5 -#define SGFSDLG_UPDIR 6 -#define SGFSDLG_ROOTDIR 7 -#define SGFSDLG_ENTRY1 10 -#define SGFSDLG_ENTRY16 25 -#define SGFSDLG_UP 26 -#define SGFSDLG_DOWN 27 -#define SGFSDLG_OKAY 28 -#define SGFSDLG_CANCEL 29 +#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_ENTRYFIRST 11 +#define SGFSDLG_ENTRYLAST 26 +#define SGFSDLG_SCROLLBAR 27 +#define SGFSDLG_UP 28 +#define SGFSDLG_DOWN 29 +#define SGFSDLG_SHOWHIDDEN 30 +#define SGFSDLG_OKAY 31 +#define SGFSDLG_CANCEL 32 + +#define SCROLLOUT_ABOVE 1 +#define SCROLLOUT_UNDER 2 + #define DLGPATH_SIZE 62 static char dlgpath[DLGPATH_SIZE+1]; /* Path name in the dialog */ @@ -38,7 +46,7 @@ static char dlgpath[DLGPATH_SIZE+1]; static char dlgfname[DLGFNAME_SIZE+1]; /* Name of the selected file in the dialog */ #define DLGFILENAMES_SIZE 59 -static char dlgfilenames[16][DLGFILENAMES_SIZE+1]; /* Visible file names in the dialog */ +static char dlgfilenames[SGFS_NUMENTRIES][DLGFILENAMES_SIZE+1]; /* Visible file names in the dialog */ /* The dialog data: */ static SGOBJ fsdlg[] = @@ -49,7 +57,8 @@ static SGOBJ fsdlg[] = { 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, 55,1, 4,1, ".." }, + { 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 }, @@ -69,22 +78,35 @@ static SGOBJ fsdlg[] = { 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 */ - { SGBUTTON, 0, 0, 14,23, 8,1, "Okay" }, - { SGBUTTON, 0, 0, 34,23, 8,1, "Cancel" }, + { SGSCROLLBAR, SG_TOUCHEXIT, 0, 62, 7, 0, 0, NULL }, /* Scrollbar */ + { 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, 0, 2,23, 18,1, "Show hidden files" }, + { SGBUTTON, SG_DEFAULT, 0, 32,23, 8,1, "Okay" }, + { SGBUTTON, SG_CANCEL, 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? */ +static int oldMouseY = 0; /* Keep the latest Y mouse position for scrollbar move computing */ +static int mouseClicked = 0; /* used to know if mouse if down for the first time or not */ +static int mouseIsOut = 0; /* used to keep info that mouse if above or under the scrollbar when mousebutton is down */ +static float scrollbar_Ypos = 0.0; /* scrollbar heigth */ + +/* Convert file position (in file list) to scrollbar y position */ +static void DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(void); + + /*-----------------------------------------------------------------------*/ -/* - 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 entries, int ypos) +/** + * 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); @@ -92,13 +114,13 @@ static int DlgFileSelect_RefreshEntries( if (!tempstr) { perror("DlgFileSelect_RefreshEntries"); - return FALSE; + return false; } /* Copy entries to dialog: */ - for(i=0; i<16; i++) + for (i=0; id_name); - if( browsingzip ) + if (browsingzip) { - if( tempstr[strlen(tempstr)-1] == '/' ) + 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) + if (ZIP_FileNameIsZIP(tempstr) && browsingzip == false) dlgfilenames[i][0] = SGFOLDER; /* Mark .ZIP archives as folders */ } } @@ -127,44 +149,343 @@ static int DlgFileSelect_RefreshEntries( } free(tempstr); - return TRUE; + return true; } /*-----------------------------------------------------------------------*/ -/* - 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. -*/ -int SDLGui_FileSelect(char *path_and_name, char *zip_path, BOOL bAllowNew) +/** + * 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; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + refreshentries = true; + } +} + + +/*-----------------------------------------------------------------------*/ +/** + * Prepare to scroll down one entry. + */ +static void DlgFileSelect_ScrollDown(void) +{ + if (ypos+SGFS_NUMENTRIES < entries) + { + ++ypos; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + refreshentries = true; + } +} + +/*-----------------------------------------------------------------------*/ +/** + * Manage the scrollbar up or down. + */ +static void DlgFileSelect_ManageScrollbar(void) +{ + int b, x, y; + int scrollY, scrollYmin, scrollYmax, scrollH_half; + float scrollMove; + + b = SDL_GetMouseState(&x, &y); + + /* If mouse is down on the scrollbar for the first time */ + if (fsdlg[SGFSDLG_SCROLLBAR].state & SG_MOUSEDOWN) { + if (mouseClicked == 0) { + mouseClicked = 1; + mouseIsOut = 0; + oldMouseY = y; + } + } + /* Mouse button is up on the scrollbar */ + else { + mouseClicked = 0; + oldMouseY = y; + mouseIsOut = 0; + } + + /* If mouse Y position didn't change */ + if (oldMouseY == y) + return; + + /* Compute scrollbar ymin and ymax values */ + + scrollYmin = (fsdlg[SGFSDLG_SCROLLBAR].y + fsdlg[0].y) * sdlgui_fontheight; + scrollYmax = (fsdlg[SGFSDLG_DOWN].y + fsdlg[0].y) * sdlgui_fontheight; + + scrollY = fsdlg[SGFSDLG_SCROLLBAR].y * sdlgui_fontheight + fsdlg[SGFSDLG_SCROLLBAR].h + fsdlg[0].y * sdlgui_fontheight; + scrollH_half = scrollY + fsdlg[SGFSDLG_SCROLLBAR].w / 2; + scrollMove = (float)(y-oldMouseY)/sdlgui_fontheight; + + /* Verify if mouse is not above the scrollbar area */ + if (y < scrollYmin) { + mouseIsOut = SCROLLOUT_ABOVE; + oldMouseY = y; + return; + } + if (mouseIsOut == SCROLLOUT_ABOVE && y < scrollH_half) { + oldMouseY = y; + return; + } + + /* Verify if mouse is not under the scrollbar area */ + if (y > scrollYmax) { + mouseIsOut = SCROLLOUT_UNDER; + oldMouseY = y; + return; + } + if (mouseIsOut == SCROLLOUT_UNDER && y > scrollH_half) { + oldMouseY = y; + return; + } + + mouseIsOut = 0; + + scrollbar_Ypos += scrollMove; + oldMouseY = y; + + /* Verifiy if scrollbar is in correct inferior boundary */ + if (scrollbar_Ypos < 0) + scrollbar_Ypos = 0.0; + + /* Verifiy if scrollbar is in correct superior boundary */ + b = (int) (scrollbar_Ypos * ((float)entries/(float)(SGFS_NUMENTRIES-2)) + 0.5); + if (b+SGFS_NUMENTRIES >= entries) { + ypos = entries - SGFS_NUMENTRIES; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + } + + refreshentries = true; +} + +/*-----------------------------------------------------------------------*/ +/** + * Handle SDL events. + */ +static void DlgFileSelect_HandleSdlEvents(SDL_Event *pEvent) +{ + int oldypos = ypos; + 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; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + break; + case SDLK_END: + ypos = entries-SGFS_NUMENTRIES; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + break; + case SDLK_PAGEUP: + ypos -= SGFS_NUMENTRIES; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + break; + case SDLK_PAGEDOWN: + if (ypos+2*SGFS_NUMENTRIES < entries) + ypos += SGFS_NUMENTRIES; + else + ypos = entries-SGFS_NUMENTRIES; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + break; + default: + break; + } + break; + default: + break; + } + + if (ypos < 0) { + ypos = 0; + scrollbar_Ypos = 0.0; + } + + if (ypos != oldypos) + refreshentries = true; +} + + +/*-----------------------------------------------------------------------*/ +/** + * Free file entries + */ +static struct dirent **files_free(struct dirent **files) +{ + int i; + if (files != NULL) + { + for(i=0; i= 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