/**************************************************************************** * 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 "listfiles.h" #include "language/gettext.h" #include "PromptWindows.h" #include "libwiigui/gui.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; iname, "stdnull")) { snprintf(rootdir, sizeof(rootdir) , "%s:/", devoptab_list[i]->name); if(DIR_ITER *dir = diropen(rootdir)) { dirclose(dir); BROWSERINFO browser; browser.dir[0] = '\0'; 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 stricmp(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_ITER *dir = NULL; char fulldir[MAXPATHLEN]; char filename[MAXPATHLEN]; 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; idir, &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 = diropen(fulldir)) == NULL) { if(Flags & FB_TRYROOTDIR) { browser->dir[0] = 0; if((dir = diropen(browser->rootdir)) == NULL) return -1; } else return -1; } while (dirnext(dir,filename,&filestat) == 0) { if (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 dirclose(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; /* GuiText titleTxt("Browse Files", 28, (GXColor){0, 0, 0, 230}); titleTxt.SetAlignment(ALIGN_LEFT, ALIGN_TOP); titleTxt.SetPosition(70,20); */ 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); GuiSound btnSoundOver(button_over_pcm, button_over_pcm_size, SOUND_PCM, Settings.sfxvolume); GuiSound btnClick(button_click2_pcm, button_click2_pcm_size, SOUND_PCM, Settings.sfxvolume); GuiImageData folderImgData(icon_folder_png); GuiImage folderImg(&folderImgData); GuiButton folderBtn(folderImg.GetWidth(), folderImg.GetHeight()); folderBtn.SetAlignment(ALIGN_CENTRE, ALIGN_MIDDLE); folderBtn.SetPosition(-210, -145); folderBtn.SetImage(&folderImg); folderBtn.SetTrigger(&trigA); folderBtn.SetEffectGrow(); char imgPath[100]; snprintf(imgPath, sizeof(imgPath), "%sbutton_dialogue_box.png", CFG.theme_path); GuiImageData btnOutline(imgPath, button_dialogue_box_png); GuiText ExitBtnTxt(tr("Cancel"), 24, (GXColor) {0, 0, 0, 255}); GuiImage ExitBtnImg(&btnOutline); if (Settings.wsprompt == yes) { ExitBtnTxt.SetWidescreen(CFG.widescreen); ExitBtnImg.SetWidescreen(CFG.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 == yes) { usbBtnTxt.SetWidescreen(CFG.widescreen); usbBtnImg.SetWidescreen(CFG.widescreen); } GuiButton usbBtn(btnOutline.GetWidth(), btnOutline.GetHeight()); usbBtn.SetAlignment(ALIGN_CENTRE, ALIGN_BOTTOM); usbBtn.SetPosition(0, -35); usbBtn.SetLabel(&usbBtnTxt); usbBtn.SetImage(&usbBtnImg); usbBtn.SetTrigger(&trigA); usbBtn.SetEffectGrow(); GuiText okBtnTxt(tr("Ok"), 22, THEME.prompttext); GuiImage okBtnImg(&btnOutline); if (Settings.wsprompt == yes) { okBtnTxt.SetWidescreen(CFG.widescreen); okBtnImg.SetWidescreen(CFG.widescreen); } GuiButton okBtn(&okBtnImg,&okBtnImg, 0, 4, 40, -35, &trigA, &btnSoundOver, &btnClick,1); okBtn.SetLabel(&okBtnTxt); GuiFileBrowser fileBrowser(396, 248); fileBrowser.SetAlignment(ALIGN_CENTRE, ALIGN_TOP); fileBrowser.SetPosition(0, 120); GuiImageData Address(addressbar_textbox_png); GuiText AdressText(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, GuiText::SCROLL); GuiImage AdressbarImg(&Address); GuiButton Adressbar(Address.GetWidth(), Address.GetHeight()); Adressbar.SetAlignment(ALIGN_CENTRE, 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; iGetState() == 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 */ sprintf(browser->dir, "%s%s/",browser->dir, browser->browserList[clickedIndex].filename); pathCanged = true; } } if (pathCanged) { LOCK(&fileBrowser); ParseDirectory((char*)NULL, Flags, Filter); fileBrowser.ResetState(); fileBrowser.TriggerUpdate(); 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) { 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; irootdir, 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[100]; char oldfolder[100]; snprintf(newfolder, sizeof(newfolder), "%s%s", browser->rootdir, browser->dir); strcpy(oldfolder,newfolder); 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 (subfoldercreate(newfolder) == false) WindowPrompt(tr("Error !"),tr("Can't create directory"),tr("OK")); } if(ParseDirectory(newfolder, Flags, Filter)==0) { fileBrowser.ResetState(); fileBrowser.TriggerUpdate(); 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); }