--- hatari/src/gui-sdl/dlgFileSelect.c 2019/04/01 07:13:16 1.1.1.3 +++ hatari/src/gui-sdl/dlgFileSelect.c 2019/04/09 08:52:15 1.1.1.10 @@ -6,7 +6,7 @@ A file selection dialog for the graphical user interface for Hatari. */ -const char DlgFileSelect_rcsid[] = "Hatari $Id: dlgFileSelect.c,v 1.1.1.3 2019/04/01 07:13:16 root Exp $"; +const char DlgFileSelect_fileid[] = "Hatari dlgFileSelect.c : " __DATE__ " " __TIME__; #include #include @@ -16,24 +16,28 @@ const char DlgFileSelect_rcsid[] = "Hata #include "scandir.h" #include "sdlgui.h" #include "file.h" +#include "paths.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 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 */ @@ -74,26 +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 */ - { SGCHECKBOX, SG_EXIT, SG_SELECTED, 2,23, 18,1, "Show hidden files" }, - { SGBUTTON, 0, 0, 32,23, 8,1, "Okay" }, - { SGBUTTON, 0, 0, 50,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 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) +/** + * 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); @@ -101,13 +114,13 @@ static int DlgFileSelect_RefreshEntries( if (!tempstr) { perror("DlgFileSelect_RefreshEntries"); - return FALSE; + return false; } /* Copy entries to dialog: */ - for(i=0; id_name); - if( browsingzip ) + if (browsingzip) { if (File_DoesFileNameEndWithSlash(tempstr)) dlgfilenames[i][0] = SGFOLDER; /* Mark folders */ @@ -127,7 +140,7 @@ static int DlgFileSelect_RefreshEntries( { 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 */ } } @@ -136,15 +149,15 @@ static int DlgFileSelect_RefreshEntries( } free(tempstr); - return TRUE; + return true; } /*-----------------------------------------------------------------------*/ -/* - Remove all hidden files (files with file names that begin with a dot) from - the list. -*/ +/** + * 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; @@ -186,39 +199,122 @@ static void DlgFileSelect_RemoveHiddenFi /*-----------------------------------------------------------------------*/ -/* - Prepare to scroll up one entry. -*/ +/** + * Prepare to scroll up one entry. + */ static void DlgFileSelect_ScrollUp(void) { if (ypos > 0) { --ypos; - refreshentries = TRUE; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + refreshentries = true; } } /*-----------------------------------------------------------------------*/ -/* - Prepare to scroll down one entry. -*/ +/** + * Prepare to scroll down one entry. + */ static void DlgFileSelect_ScrollDown(void) { if (ypos+SGFS_NUMENTRIES < entries) { ++ypos; - refreshentries = TRUE; + 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. -*/ +/** + * Handle SDL events. + */ static void DlgFileSelect_HandleSdlEvents(SDL_Event *pEvent) { + int oldypos = ypos; switch (pEvent->type) { case SDL_MOUSEBUTTONDOWN: @@ -227,73 +323,173 @@ static void DlgFileSelect_HandleSdlEvent else if (pEvent->button.button == SDL_BUTTON_WHEELDOWN) DlgFileSelect_ScrollDown(); break; - case SDL_KEYDOWN: + 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; + 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; - refreshentries = TRUE; + DlgFileSelect_Convert_ypos_to_scrollbar_Ypos(); + break; + default: break; - default: break; } break; + default: + break; } + + if (ypos < 0) { + ypos = 0; + scrollbar_Ypos = 0.0; + } + + if (ypos != oldypos) + refreshentries = 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. +/** + * 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=SGFSDLG_ENTRY1 && retbut<=SGFSDLG_ENTRY16 && retbut-SGFSDLG_ENTRY1+ypos=SGFSDLG_ENTRYFIRST && retbut<=SGFSDLG_ENTRYLAST && retbut-SGFSDLG_ENTRYFIRST+yposd_name); + if (!strcat_maxlen(tempstr, FILENAME_MAX, + zipdir, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name)) + { + fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n"); + goto clean_exit; + } + /* directory? */ if (File_DoesFileNameEndWithSlash(tempstr)) { /* handle the ../ directory */ - if(strcmp(files[retbut-SGFSDLG_ENTRY1+ypos]->d_name, "../") == 0) + if (strcmp(files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name, "../") == 0) { /* close the zip file */ - if( strcmp(tempstr, "../") == 0 ) + if (strcmp(tempstr, "../") == 0) { - reloaddir = refreshentries = TRUE; /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, path, DLGPATH_SIZE); - browsingzip = FALSE; + browsingzip = false; } else { - i=strlen(tempstr)-1; - n=0; - while(i > 0 && n < 3) - if (tempstr[i--] == PATHSEP) - n++; - if (tempstr[i+1] == PATHSEP) - tempstr[i+2] = '\0'; - else - tempstr[0] = '\0'; - + /* remove "../" and previous dir from path */ + File_PathShorten(tempstr, 2); + correct_zip_root(tempstr); strcpy(zipdir, tempstr); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); } @@ -441,79 +636,64 @@ int SDLGui_FileSelect(char *path_and_nam strcpy(zipdir, tempstr); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); } - reloaddir = TRUE; + reloaddir = true; /* Copy the path name to the dialog */ - selection = -1; /* Remove old selection */ zipfilename[0] = '\0'; dlgfname[0] = 0; ypos = 0; - + scrollbar_Ypos = 0.0; } else { - /* Select a file in the zip */ - selection = retbut-SGFSDLG_ENTRY1+ypos; + /* not dir, select a file in the zip */ + selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(zipfilename, files[selection]->d_name); File_ShrinkName(dlgfname, zipfilename, DLGFNAME_SIZE); } - } /* if browsingzip */ - else + } + else /* not browsingzip */ { - strcpy(tempstr, path); - strcat(tempstr, files[retbut-SGFSDLG_ENTRY1+ypos]->d_name); - if( stat(tempstr, &filestat)==0 && S_ISDIR(filestat.st_mode) ) + if (!strcat_maxlen(tempstr, FILENAME_MAX, + path, files[retbut-SGFSDLG_ENTRYFIRST+ypos]->d_name)) { - /* Set the new directory */ - strcpy(path, tempstr); - if( strlen(path)>=3 ) - { - if (path[strlen(path)-2] == PATHSEP && path[strlen(path)-1]=='.') - path[strlen(path)-2] = 0; /* Strip a single dot at the end of the path name */ - if (path[strlen(path)-3] == PATHSEP && path[strlen(path)-2]=='.' && path[strlen(path)-1]=='.') - { - /* Handle the ".." folder */ - char *ptr; - if( strlen(path)==3 ) - path[1] = 0; - else - { - path[strlen(path)-3] = 0; - ptr = strrchr(path, PATHSEP); - if(ptr) - *(ptr+1) = 0; - } - } - } - File_AddSlashToEndFileName(path); - reloaddir = TRUE; + fprintf(stderr, "SDLGui_FileSelect: Path name too long!\n"); + goto clean_exit; + } + if (File_DirExists(tempstr)) + { + File_HandleDotDirs(tempstr); + File_AddSlashToEndFileName(tempstr); /* Copy the path name to the dialog */ - File_ShrinkName(dlgpath, path, DLGPATH_SIZE); - selection = -1; /* Remove old selection */ + File_ShrinkName(dlgpath, tempstr, DLGPATH_SIZE); + strcpy(path, tempstr); + reloaddir = true; dlgfname[0] = 0; ypos = 0; + scrollbar_Ypos = 0.0; } else if (ZIP_FileNameIsZIP(tempstr) && zip_path != NULL) { /* open a zip file */ zipfiles = ZIP_GetFiles(tempstr); - if( zipfiles != NULL && browsingzip == FALSE ) + if (zipfiles != NULL && browsingzip == false) { - selection = retbut-SGFSDLG_ENTRY1+ypos; + selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(fname, files[selection]->d_name); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); - browsingzip=TRUE; - strcpy(zipdir, ""); + browsingzip = true; + zipdir[0] = '\0'; /* zip root */ File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); - reloaddir = refreshentries = TRUE; + reloaddir = true; ypos = 0; + scrollbar_Ypos = 0.0; } } else { /* Select a file */ - selection = retbut-SGFSDLG_ENTRY1+ypos; + selection = retbut-SGFSDLG_ENTRYFIRST+ypos; strcpy(fname, files[selection]->d_name); File_ShrinkName(dlgfname, fname, DLGFNAME_SIZE); } @@ -527,98 +707,62 @@ int SDLGui_FileSelect(char *path_and_nam switch(retbut) { case SGFSDLG_UPDIR: /* Change path to parent directory */ - - if( browsingzip ) + if (browsingzip) { - /* close the zip file */ - if( strcmp(zipdir, "") == 0 ) + /* close the zip file? */ + if (!zipdir[0]) { - reloaddir = refreshentries = TRUE; /* free zip file entries */ ZIP_FreeZipDir(zipfiles); + browsingzip = false; zipfiles = NULL; - /* Copy the path name to the dialog */ File_ShrinkName(dlgpath, path, DLGPATH_SIZE); - browsingzip = FALSE; - reloaddir = TRUE; - selection = -1; /* Remove old selection */ - fname[0] = 0; - dlgfname[0] = 0; - ypos = 0; } else { - i=strlen(zipdir)-1; - n=0; - while(i > 0 && n < 2) - if (zipdir[i--] == PATHSEP) - n++; - if (zipdir[i+1] == PATHSEP) - zipdir[i+2] = '\0'; - else - zipdir[0] = '\0'; - + /* remove last dir from zipdir path */ + File_PathShorten(zipdir, 1); + correct_zip_root(zipdir); File_ShrinkName(dlgpath, zipdir, DLGPATH_SIZE); - reloaddir = TRUE; - selection = -1; /* Remove old selection */ zipfilename[0] = '\0'; - dlgfname[0] = 0; - ypos = 0; } } /* not a zip file: */ - else if( strlen(path)>2 ) + else { - char *ptr; - File_CleanFileName(path); - ptr = strrchr(path, PATHSEP); - if(ptr) - *(ptr+1) = 0; - File_AddSlashToEndFileName(path); - reloaddir = TRUE; - File_ShrinkName(dlgpath, path, DLGPATH_SIZE); /* Copy the path name to the dialog */ - selection = -1; /* Remove old selection */ - fname[0] = 0; - dlgfname[0] = 0; - ypos = 0; + File_PathShorten(path, 1); + File_ShrinkName(dlgpath, path, DLGPATH_SIZE); } + reloaddir = true; break; case SGFSDLG_HOMEDIR: /* Change to home directory */ - if (getenv("HOME") == NULL) + home = Paths_GetUserHome(); + if (home == NULL || !*home) break; if (browsingzip) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; - browsingzip = FALSE; + browsingzip = false; } - strcpy(path, getenv("HOME")); + strcpy(path, home); File_AddSlashToEndFileName(path); - reloaddir = TRUE; - strcpy(dlgpath, path); - selection = -1; /* Remove old selection */ - fname[0] = 0; - dlgfname[0] = 0; - ypos = 0; + File_ShrinkName(dlgpath, path, DLGPATH_SIZE); + reloaddir = true; break; case SGFSDLG_ROOTDIR: /* Change to root directory */ - if( browsingzip ) + if (browsingzip) { /* free zip file entries */ ZIP_FreeZipDir(zipfiles); zipfiles = NULL; - browsingzip = FALSE; + browsingzip = false; } - - strcpy(path, "/"); - reloaddir = TRUE; + path[0] = PATHSEP; path[1] = '\0'; strcpy(dlgpath, path); - selection = -1; /* Remove old selection */ - fname[0] = 0; - dlgfname[0] = 0; - ypos = 0; + reloaddir = true; break; case SGFSDLG_UP: /* Scroll up */ DlgFileSelect_ScrollUp(); @@ -628,38 +772,39 @@ int SDLGui_FileSelect(char *path_and_nam DlgFileSelect_ScrollDown(); SDL_Delay(10); break; + case SGFSDLG_SCROLLBAR: /* Scrollbar selected */ + DlgFileSelect_ManageScrollbar(); + SDL_Delay(10); + break; case SGFSDLG_FILENAME: /* User entered new filename */ strcpy(fname, dlgfname); break; case SGFSDLG_SHOWHIDDEN: /* Show/hide hidden files */ - reloaddir = TRUE; + reloaddir = true; ypos = 0; + scrollbar_Ypos = 0.0; break; case SDLGUI_UNKNOWNEVENT: DlgFileSelect_HandleSdlEvents(&sdlEvent); break; } /* switch */ + + if (reloaddir) + { + /* Remove old selection */ + fname[0] = 0; + dlgfname[0] = 0; + ypos = 0; + scrollbar_Ypos = 0.0; + } } /* other button code */ } /* do */ - while (retbut!=SGFSDLG_OKAY && retbut!=SGFSDLG_CANCEL && retbut!=SDLGUI_QUIT && !bQuitProgram); - - if (oldcursorstate == SDL_DISABLE) - SDL_ShowCursor(SDL_DISABLE); + while (retbut!=SGFSDLG_OKAY && retbut!=SGFSDLG_CANCEL + && retbut!=SDLGUI_QUIT && retbut != SDLGUI_ERROR && !bQuitProgram); - File_makepath(path_and_name, path, fname, NULL); - - /* Free old allocated memory: */ - if (files != NULL) - { - for(i=0; i