/**************************************************************************** * libwiigui Template * Tantric 2009 * * modified by dimok * * filebrowser.cpp * * Generic file routines - reading, writing, browsing ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include "menu.h" #include "themes/CTheme.h" #include "FileOperations/fileops.h" #include "language/gettext.h" #include "PromptWindows.h" #include "GUI/gui_filebrowser.h" #include "sys.h" #include "filebrowser.h" /*** Extern variables ***/ extern GuiWindow * mainWindow; extern u8 shutdown; extern u8 reset; /*** Extern functions ***/ extern void ResumeGui(); extern void HaltGui(); static int curDevice = -1; static std::vector browsers; BROWSERINFO *browser = NULL; /**************************************************************************** * FileFilterCallbacks * return: 1-visible 0-hidden ***************************************************************************/ int noDIRS(BROWSERENTRY *Entry, void* Args) { return !Entry->isdir; } int noFILES(BROWSERENTRY *Entry, void* Args) { return Entry->isdir; } int noEXT(BROWSERENTRY *Entry, void* Args) { if (!Entry->isdir) { char *cptr = strrchr(Entry->displayname, '.'); if (cptr && cptr != Entry->displayname) *cptr = 0; } return 1; } void ResetBrowser(BROWSERINFO *browser); /**************************************************************************** * InitBrowsers() * Clears the file browser memory, and allocates one initial entry ***************************************************************************/ int InitBrowsers() { curDevice = -1; browsers.clear(); browser = NULL; char rootdir[ROOTDIRLEN]; for (int i = 3; i < STD_MAX; i++) { if (strcmp(devoptab_list[i]->name, "stdnull") && devoptab_list[i]->write_r != NULL) { snprintf(rootdir, sizeof(rootdir), "%s:/", devoptab_list[i]->name); if ( DIR *dir = opendir( rootdir ) ) { closedir(dir); BROWSERINFO browser = {}; strcpy(browser.rootdir, rootdir); ResetBrowser(&browser); browsers.push_back(browser); } } } if (!browsers.size()) return -1; curDevice = 0; browser = &browsers[curDevice]; return 0; } /**************************************************************************** * ResetBrowser() * Clears the file browser memory, and allocates one initial entry ***************************************************************************/ void ResetBrowser(BROWSERINFO *browser) { browser->pageIndex = 0; browser->browserList.clear(); /* // Clear any existing values if (browser->browserList != NULL) { free(browser->browserList); browser->browserList = NULL; } // set aside space for 1 entry browser->browserList = (BROWSERENTRY *)malloc(sizeof(BROWSERENTRY)); if(browser->browserList) memset(browser->browserList, 0, sizeof(BROWSERENTRY)); */ } /**************************************************************************** * FileSortCallback * * sort callback to sort file entries with the following order: * . * .. * * ***************************************************************************/ //int FileSortCallback(const void *f1, const void *f2) { bool operator<(const BROWSERENTRY &f1, const BROWSERENTRY &f2) { /* Special case for implicit directories */ if (f1.filename[0] == '.' || f2.filename[0] == '.') { if (strcmp(f1.filename, ".") == 0) { return true; } if (strcmp(f2.filename, ".") == 0) { return false; } if (strcmp(f1.filename, "..") == 0) { return true; } if (strcmp(f2.filename, "..") == 0) { return false; } } /* If one is a file and one is a directory the directory is first. */ if (f1.isdir && !(f2.isdir)) return true; if (!(f1.isdir) && f2.isdir) return false; return strcasecmp(f1.filename, f2.filename) < 0; } int ParseFilter(FILTERCASCADE *Filter, BROWSERENTRY* Entry) { while (Filter) { if (Filter->filter && Filter->filter(Entry, Filter->filter_args) == 0) return 0; Filter = Filter->next; } return 1; } /*************************************************************************** * Browse subdirectories **************************************************************************/ int ParseDirectory(const char* Path, int Flags, FILTERCASCADE *Filter) { DIR *dir = NULL; char fulldir[1024 * 5]; char filename[1024 * 10]; struct stat filestat; unsigned int i; if (curDevice == -1) if (InitBrowsers()) return -1; // InitBrowser fails if (Path) // note in this codeblock use filename temporary { strlcpy(fulldir, Path, sizeof(fulldir)); if (*fulldir && fulldir[strlen(fulldir) - 1] != '/') // a file { char * chrp = strrchr(fulldir, '/'); if (chrp) chrp[1] = 0; } if (strchr(fulldir, ':') == NULL) // Path has no device device { getcwd(filename, sizeof(filename)); // save the current working dir if (*fulldir == 0) // if path is empty strlcpy(fulldir, filename, sizeof(fulldir)); // we use the current working dir else { // path is not empty if (chdir(fulldir)) // sets the path to concatenate and validate { if (Flags & (FB_TRYROOTDIR | FB_TRYSTDDEV)) { if (chdir("/") && !(Flags & FB_TRYSTDDEV)) // try to set root if is needed return -1; } } if (getcwd(fulldir, sizeof(fulldir))) return -1; // gets the concatenated current working dir chdir(filename); // restore the saved cwd } } for (i = 0; i < browsers.size(); i++) // searchs the browser who match the path { if (strncasecmp(fulldir, browsers[i].rootdir, strlen(browsers[i].rootdir) - 1 /*means without trailing '/'*/) == 0) { browser = &browsers[curDevice]; break; } } if (i != browsers.size()) // found browser { curDevice = i; browser = &browsers[curDevice]; strcpy(browser->dir, &fulldir[strlen(browser->rootdir)]); } else if (Flags & FB_TRYSTDDEV) { curDevice = 0; browser = &browsers[curDevice]; // when no browser was found and browser->dir[0] = 0; // we alowed try StdDevice and try RootDir strlcpy(fulldir, browser->rootdir, sizeof(fulldir)); // set the first browser with root-dir } else return -1; } else snprintf(fulldir, sizeof(fulldir), "%s%s", browser->rootdir, browser->dir); // reset browser ResetBrowser(browser); // open the directory if ((dir = opendir(fulldir)) == NULL) { if (Flags & FB_TRYROOTDIR) { snprintf(fulldir, sizeof(fulldir), browser->rootdir); browser->dir[0] = 0; if ((dir = opendir(browser->rootdir)) == NULL) return -1; } else return -1; } struct dirent *dirent = NULL; // Adds parent directory ".." manually if in a subdirectory to fix NTFS folder browsing. if (strcmp(fulldir, browser->rootdir) != 0) { snprintf(filename, sizeof(filename), ".."); BROWSERENTRY newEntry; memset(&newEntry, 0, sizeof(BROWSERENTRY)); // clear the new entry strlcpy(newEntry.filename, filename, sizeof(newEntry.filename)); strlcpy(newEntry.displayname, filename, sizeof(newEntry.displayname)); newEntry.isdir = 1; // flag this as a dir if (ParseFilter(Filter, &newEntry)) browser->browserList.push_back(newEntry); } while ((dirent = readdir(dir)) != 0) { snprintf(filename, sizeof(filename), "%s/%s", fulldir, dirent->d_name); if(stat(filename, &filestat) != 0) continue; snprintf(filename, sizeof(filename), dirent->d_name); if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0) { BROWSERENTRY newEntry; memset(&newEntry, 0, sizeof(BROWSERENTRY)); // clear the new entry strlcpy(newEntry.filename, filename, sizeof(newEntry.filename)); strlcpy(newEntry.displayname, filename, sizeof(newEntry.displayname)); newEntry.length = filestat.st_size; newEntry.isdir = (filestat.st_mode & S_IFDIR) == 0 ? 0 : 1; // flag this as a dir if (ParseFilter(Filter, &newEntry)) browser->browserList.push_back(newEntry); } } // close directory closedir(dir); // Sort the file list std::sort(browser->browserList.begin(), browser->browserList.end()); return 0; } int ParseDirectory(int Device, int Flags, FILTERCASCADE *Filter) { if (Device >= 0 && Device < (int) browsers.size()) { int old_curDevice = curDevice; curDevice = Device; browser = &browsers[curDevice]; if (ParseDirectory((char*) NULL, Flags, Filter) == 0) return 0; curDevice = old_curDevice; browser = &browsers[old_curDevice]; } return -1; } /**************************************************************************** * BrowseDevice * Displays a list of files on the selected path ***************************************************************************/ int BrowseDevice(char * Path, int Path_size, int Flags, FILTERCASCADE *Filter/*=NULL*/) { int result = -1; int i; if (InitBrowsers() || ParseDirectory(Path, Flags, Filter)) { WindowPrompt(tr( "Error" ), 0, tr( "OK" )); return -1; } int menu = MENU_NONE; GuiTrigger trigA; trigA.SetSimpleTrigger(-1, WPAD_BUTTON_A | WPAD_CLASSIC_BUTTON_A, PAD_BUTTON_A); GuiTrigger trigB; trigB.SetButtonOnlyTrigger(-1, WPAD_BUTTON_B | WPAD_CLASSIC_BUTTON_B, PAD_BUTTON_B); GuiImageData folderImgData(Resources::GetFile("icon_folder.png"), Resources::GetFileSize("icon_folder.png")); GuiImage folderImg(&folderImgData); GuiButton folderBtn(folderImg.GetWidth(), folderImg.GetHeight()); folderBtn.SetAlignment(ALIGN_CENTER, ALIGN_MIDDLE); folderBtn.SetPosition(-210, -145); folderBtn.SetImage(&folderImg); folderBtn.SetTrigger(&trigA); folderBtn.SetEffectGrow(); GuiImageData btnOutline(Resources::GetFile("button_dialogue_box.png"), Resources::GetFileSize("button_dialogue_box.png")); GuiText ExitBtnTxt(tr( "Cancel" ), 24, ( GXColor ) {0, 0, 0, 255}); GuiImage ExitBtnImg(&btnOutline); if (Settings.wsprompt) { ExitBtnTxt.SetWidescreen(Settings.widescreen); ExitBtnImg.SetWidescreen(Settings.widescreen); } GuiButton ExitBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); ExitBtn.SetAlignment(ALIGN_RIGHT, ALIGN_BOTTOM); ExitBtn.SetPosition(-40, -35); ExitBtn.SetLabel(&ExitBtnTxt); ExitBtn.SetImage(&ExitBtnImg); ExitBtn.SetTrigger(&trigA); ExitBtn.SetTrigger(&trigB); ExitBtn.SetEffectGrow(); GuiText usbBtnTxt(browsers[(curDevice + 1) % browsers.size()].rootdir, 24, ( GXColor ) {0, 0, 0, 255}); GuiImage usbBtnImg(&btnOutline); if (Settings.wsprompt) { usbBtnTxt.SetWidescreen(Settings.widescreen); usbBtnImg.SetWidescreen(Settings.widescreen); } GuiButton usbBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); usbBtn.SetAlignment(ALIGN_CENTER, ALIGN_BOTTOM); usbBtn.SetPosition(0, -35); usbBtn.SetLabel(&usbBtnTxt); usbBtn.SetImage(&usbBtnImg); usbBtn.SetTrigger(&trigA); usbBtn.SetEffectGrow(); GuiText okBtnTxt(tr( "OK" ), 22, thColor("r=0 g=0 b=0 a=255 - prompt windows button text color")); GuiImage okBtnImg(&btnOutline); if (Settings.wsprompt) { okBtnTxt.SetWidescreen(Settings.widescreen); okBtnImg.SetWidescreen(Settings.widescreen); } GuiButton okBtn(&okBtnImg, &okBtnImg, 0, 4, 40, -35, &trigA, btnSoundOver, btnSoundClick2, 1); okBtn.SetLabel(&okBtnTxt); GuiFileBrowser fileBrowser(396, 248); fileBrowser.SetAlignment(ALIGN_CENTER, ALIGN_TOP); fileBrowser.SetPosition(0, 120); GuiImageData Address(Resources::GetFile("addressbar_textbox.png"), Resources::GetFileSize("addressbar_textbox.png")); GuiText AdressText((char*) NULL, 20, ( GXColor ) {0, 0, 0, 255}); AdressText.SetTextf("%s%s", browser->rootdir, browser->dir); AdressText.SetAlignment(ALIGN_LEFT, ALIGN_MIDDLE); AdressText.SetPosition(20, 0); AdressText.SetMaxWidth(Address.GetWidth() - 40, SCROLL_HORIZONTAL); GuiImage AdressbarImg(&Address); GuiButton Adressbar(Address.GetWidth(), Address.GetHeight()); Adressbar.SetAlignment(ALIGN_CENTER, ALIGN_TOP); Adressbar.SetPosition(0, fileBrowser.GetTop() - 45); Adressbar.SetImage(&AdressbarImg); Adressbar.SetLabel(&AdressText); HaltGui(); GuiWindow w(screenwidth, screenheight); w.Append(&ExitBtn); // w.Append(&titleTxt); w.Append(&fileBrowser); w.Append(&Adressbar); w.Append(&okBtn); if (!(Flags & FB_NOFOLDER_BTN)) w.Append(&folderBtn); if (browsers.size() > 1 && !(Flags & FB_NODEVICE_BTN)) w.Append(&usbBtn); mainWindow->Append(&w); ResumeGui(); int clickedIndex = -1; while (menu == MENU_NONE) { VIDEO_WaitVSync(); if (shutdown == 1) Sys_Shutdown(); if (reset == 1) Sys_Reboot(); for (i = 0; i < FILEBROWSERSIZE; i++) { if (fileBrowser.fileList[i]->GetState() == STATE_CLICKED) { fileBrowser.fileList[i]->ResetState(); clickedIndex = browser->pageIndex + i; bool pathCanged = false; // check corresponding browser entry if (browser->browserList[clickedIndex].isdir) { /* go up to parent directory */ if (strcmp(browser->browserList[clickedIndex].filename, "..") == 0) { /* remove last subdirectory name */ int len = strlen(browser->dir); while (browser->dir[0] && browser->dir[len - 1] == '/') browser->dir[--len] = '\0'; // remove all trailing '/' char *cptr = strrchr(browser->dir, '/'); if (cptr) *++cptr = 0; else browser->dir[0] = '\0'; // remove trailing dir pathCanged = true; } /* Open a directory */ /* current directory doesn't change */ else if (strcmp(browser->browserList[clickedIndex].filename, ".")) { /* test new directory namelength */ if ((strlen(browser->dir) + strlen(browser->browserList[clickedIndex].filename) + 1/*'/'*/) < MAXPATHLEN) { /* update current directory name */ strcat(browser->dir, browser->browserList[clickedIndex].filename); sprintf(browser->dir + strlen(browser->dir), "/"); pathCanged = true; } } if (pathCanged) { LOCK( &fileBrowser ); ParseDirectory((char*) NULL, Flags, Filter); fileBrowser.ResetState(); fileBrowser.UpdateList(); AdressText.SetTextf("%s%s", browser->rootdir, browser->dir); } clickedIndex = -1; } else /* isFile */ { AdressText.SetTextf("%s%s%s", browser->rootdir, browser->dir, browser->browserList[clickedIndex].filename); } } } if (ExitBtn.GetState() == STATE_CLICKED) { result = 0; break; } else if (okBtn.GetState() == STATE_CLICKED) { if (clickedIndex >= 0) snprintf(Path, Path_size, "%s%s%s", browser->rootdir, browser->dir, browser->browserList[clickedIndex].filename); else snprintf(Path, Path_size, "%s%s", browser->rootdir, browser->dir); result = 1; break; } else if (usbBtn.GetState() == STATE_CLICKED) { usbBtn.ResetState(); for (u32 i = 1; i < browsers.size(); i++) { LOCK( &fileBrowser ); if (ParseDirectory((curDevice + i) % browsers.size(), Flags, Filter) == 0) { fileBrowser.ResetState(); fileBrowser.UpdateList(); AdressText.SetTextf("%s%s", browser->rootdir, browser->dir); usbBtnTxt.SetText(browsers[(curDevice + 1) % browsers.size()].rootdir); break; } } } else if (folderBtn.GetState() == STATE_CLICKED) { folderBtn.ResetState(); HaltGui(); mainWindow->Remove(&w); ResumeGui(); char newfolder[1024 * 5]; snprintf(newfolder, sizeof(newfolder), "%s%s", browser->rootdir, browser->dir); int result = OnScreenKeyboard(newfolder, sizeof(newfolder), strlen(browser->rootdir)); if (result == 1) { unsigned int len = strlen(newfolder); if (len > 0 && len + 1 < sizeof(newfolder) && newfolder[len - 1] != '/') { newfolder[len] = '/'; newfolder[len + 1] = '\0'; } struct stat st; if (stat(newfolder, &st) != 0) { if (WindowPrompt(tr( "Directory does not exist!" ), tr( "The entered directory does not exist. Would you like to create it?" ), tr( "OK" ), tr( "Cancel" )) == 1) if (CreateSubfolder(newfolder) == false) WindowPrompt( tr( "Error !" ), tr( "Can't create directory" ), tr( "OK" )); } if (ParseDirectory(newfolder, Flags, Filter) == 0) { fileBrowser.ResetState(); fileBrowser.UpdateList(); AdressText.SetTextf("%s%s", browser->rootdir, browser->dir); usbBtnTxt.SetText(browsers[(curDevice + 1) % browsers.size()].rootdir); } } HaltGui(); mainWindow->Append(&w); ResumeGui(); } } HaltGui(); mainWindow->Remove(&w); ResumeGui(); //} return result; } int BrowseDevice(char * Path, int Path_size, int Flags, FILEFILTERCALLBACK Filter, void *FilterArgs) { if (Filter) { FILTERCASCADE filter = { Filter, FilterArgs, NULL }; return BrowseDevice(Path, Path_size, Flags, &filter); } return BrowseDevice(Path, Path_size, Flags); }