diff --git a/src/gui/newFilePicker.cpp b/src/gui/newFilePicker.cpp index 4e5f4fd00..192ec1bea 100644 --- a/src/gui/newFilePicker.cpp +++ b/src/gui/newFilePicker.cpp @@ -47,6 +47,89 @@ static void _fileThread(void* item) { ((FurnaceFilePicker*)item)->readDirectorySub(); } +static void _searchThread(void* item) { + ((FurnaceFilePicker*)item)->searchSub("",0); +} + +#ifdef _WIN32 +#else +FurnaceFilePicker::FileEntry* FurnaceFilePicker::makeEntry(void* _entry, const char* prefix) { + struct dirent* entry=(struct dirent*)_entry; + FileEntry* newEntry=new FileEntry; + if (prefix!=NULL) { + if (prefix[0]=='/') { + newEntry->name=String(&prefix[1])+"/"+entry->d_name; + } else { + + } + } else { + newEntry->name=entry->d_name; + } + newEntry->nameLower=entry->d_name; + newEntry->isHidden=(entry->d_name[0]=='.'); + for (char& i: newEntry->nameLower) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + + switch (entry->d_type) { + case DT_REG: + newEntry->type=FP_TYPE_NORMAL; + break; + case DT_DIR: + newEntry->type=FP_TYPE_DIR; + newEntry->isDir=true; + break; + case DT_LNK: { + newEntry->type=FP_TYPE_LINK; + // resolve link to determine whether this is a directory + String readLinkPath; + DIR* readLinkDir=NULL; + if (path.empty()) { + readLinkPath=newEntry->name; + } else if (*path.rbegin()=='/') { + readLinkPath=path+newEntry->name; + } else { + readLinkPath=path+'/'+newEntry->name; + } + // silly, but works. + readLinkDir=opendir(readLinkPath.c_str()); + if (readLinkDir!=NULL) { + newEntry->isDir=true; + closedir(readLinkDir); + } + break; + } + case DT_SOCK: + newEntry->type=FP_TYPE_SOCKET; + break; + case DT_FIFO: + newEntry->type=FP_TYPE_PIPE; + break; + case DT_CHR: + newEntry->type=FP_TYPE_CHARDEV; + break; + case DT_BLK: + newEntry->type=FP_TYPE_BLOCKDEV; + break; + default: + newEntry->type=FP_TYPE_UNKNOWN; + break; + } + + if (!newEntry->isDir) { + size_t extPos=newEntry->name.rfind('.'); + if (extPos!=String::npos) { + newEntry->ext=newEntry->name.substr(extPos); + for (char& i: newEntry->ext) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + } + } + + return newEntry; +} +#endif + #ifdef _WIN32 // Windows implementation void FurnaceFilePicker::readDirectorySub() { @@ -160,7 +243,9 @@ void FurnaceFilePicker::readDirectorySub() { newEntry->hasTime=true; } + entryLock.lock(); entries.push_back(newEntry); + entryLock.unlock(); if (stopReading) { break; } @@ -192,71 +277,11 @@ void FurnaceFilePicker::readDirectorySub() { if (strcmp(entry->d_name,".")==0) continue; if (strcmp(entry->d_name,"..")==0) continue; - FileEntry* newEntry=new FileEntry; - - newEntry->name=entry->d_name; - newEntry->nameLower=entry->d_name; - newEntry->isHidden=(entry->d_name[0]=='.'); - for (char& i: newEntry->nameLower) { - if (i>='A' && i<='Z') i+='a'-'A'; - } - - switch (entry->d_type) { - case DT_REG: - newEntry->type=FP_TYPE_NORMAL; - break; - case DT_DIR: - newEntry->type=FP_TYPE_DIR; - newEntry->isDir=true; - break; - case DT_LNK: { - newEntry->type=FP_TYPE_LINK; - // resolve link to determine whether this is a directory - String readLinkPath; - DIR* readLinkDir=NULL; - if (path.empty()) { - readLinkPath=newEntry->name; - } else if (*path.rbegin()=='/') { - readLinkPath=path+newEntry->name; - } else { - readLinkPath=path+'/'+newEntry->name; - } - // silly, but works. - readLinkDir=opendir(readLinkPath.c_str()); - if (readLinkDir!=NULL) { - newEntry->isDir=true; - closedir(readLinkDir); - } - break; - } - case DT_SOCK: - newEntry->type=FP_TYPE_SOCKET; - break; - case DT_FIFO: - newEntry->type=FP_TYPE_PIPE; - break; - case DT_CHR: - newEntry->type=FP_TYPE_CHARDEV; - break; - case DT_BLK: - newEntry->type=FP_TYPE_BLOCKDEV; - break; - default: - newEntry->type=FP_TYPE_UNKNOWN; - break; - } - - if (!newEntry->isDir) { - size_t extPos=newEntry->name.rfind('.'); - if (extPos!=String::npos) { - newEntry->ext=newEntry->name.substr(extPos); - for (char& i: newEntry->ext) { - if (i>='A' && i<='Z') i+='a'-'A'; - } - } - } + FileEntry* newEntry=makeEntry(entry,NULL); + entryLock.lock(); entries.push_back(newEntry); + entryLock.unlock(); if (stopReading) { break; } @@ -304,6 +329,68 @@ void FurnaceFilePicker::readDirectorySub() { } #endif +#ifdef _WIN32 +// Windows implementation +void FurnaceFilePicker::searchSub(String subPath, int depth) { +} +#else +// Linux/Unix implementation +void FurnaceFilePicker::searchSub(String subPath, int depth) { + /// STAGE 1: get file list + if (depth>15) logW("searchSub(%s,%d)",subPath,depth); + String actualPath=path+subPath; + DIR* dir=opendir(actualPath.c_str()); + + if (dir==NULL) { + if (depth==0) { + failMessage=strerror(errno); + haveFiles=true; + haveStat=true; + } + return; + } + + struct dirent* entry=NULL; + while (true) { + entry=readdir(dir); + if (entry==NULL) break; + if (strcmp(entry->d_name,".")==0) continue; + if (strcmp(entry->d_name,"..")==0) continue; + + if (entry->d_type==DT_DIR) { + //if (depth<20) { + searchSub(subPath+String("/")+entry->d_name,depth+1); + //} + } else { + String lower=entry->d_name; + for (char& i: lower) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + + if (lower.find(searchQuery)!=String::npos) { + FileEntry* newEntry=makeEntry(entry,subPath.c_str()); + entryLock.lock(); + entries.push_back(newEntry); + entryLock.unlock(); + } + } + + if (stopReading) { + break; + } + } + if (closedir(dir)!=0) { + // ?! + } + + if (depth==0) { + haveFiles=true; + haveStat=true; + return; + } +} +#endif + String FurnaceFilePicker::normalizePath(const String& which) { String ret; #ifdef _WIN32 @@ -407,7 +494,11 @@ void FurnaceFilePicker::readDirectory(String path) { haveStat=false; stopReading=false; scheduledSort=1; - fileThread=new std::thread(_fileThread,this); + if (isSearch) { + fileThread=new std::thread(_searchThread,this); + } else { + fileThread=new std::thread(_fileThread,this); + } // check whether this path is bookmarked isPathBookmarked=false; @@ -556,7 +647,7 @@ void FurnaceFilePicker::sortFiles() { } void FurnaceFilePicker::filterFiles() { - if (filter.empty()) { + if (filter.empty() || isSearch) { filteredEntries=sortedEntries; return; } @@ -643,7 +734,7 @@ void FurnaceFilePicker::setSizeConstraints(const ImVec2& min, const ImVec2& max) void FurnaceFilePicker::drawFileList(ImVec2& tableSize, bool& acknowledged) { // display a message on empty dir, no matches or error - if (!haveFiles) { + if (!haveFiles && !isSearch) { if (ImGui::BeginTable("LoadingFiles",1,ImGuiTableFlags_BordersOuter,tableSize)) { ImGui::EndTable(); } @@ -975,6 +1066,7 @@ bool FurnaceFilePicker::draw(ImGuiWindowFlags winFlags) { String newDir; bool acknowledged=false; bool readDrives=false; + bool wantSearch=false; bool began=false; @@ -1220,12 +1312,21 @@ bool FurnaceFilePicker::draw(ImGuiWindowFlags winFlags) { filterFiles(); } ImGui::SameLine(); + if (ImGui::Button("Recurse")) { + newDir=path; + searchQuery=filter; + for (char& i: searchQuery) { + if (i>='A' && i<='Z') i+='a'-'A'; + } + wantSearch=true; + } + ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::InputTextWithHint("##Filter","Search",&filter)) { filterFiles(); } - if (scheduledSort && haveFiles) { + if (scheduledSort && (haveFiles || isSearch)) { if (haveStat) { scheduledSort=0; } @@ -1487,6 +1588,7 @@ bool FurnaceFilePicker::draw(ImGuiWindowFlags winFlags) { if (!newDir.empty() || readDrives) { // change directory if (clearSearchOnDirChange) filter=""; + isSearch=wantSearch; readDirectory(newDir); } return (curStatus!=FP_STATUS_WAITING); @@ -1518,6 +1620,8 @@ bool FurnaceFilePicker::open(String name, String pa, String hint, int flags, con } curFilterType=0; + if (isSearch) this->filter=""; + isSearch=false; readDirectory(pa); windowName=name; hint=entryNameHint; @@ -1613,6 +1717,7 @@ FurnaceFilePicker::FurnaceFilePicker(): isEmbed(false), hasSizeConstraints(false), isPathBookmarked(false), + isSearch(false), scheduledSort(0), curFilterType(0), sortMode(FP_SORT_NAME), diff --git a/src/gui/newFilePicker.h b/src/gui/newFilePicker.h index 95b879bd1..26e4718b3 100644 --- a/src/gui/newFilePicker.h +++ b/src/gui/newFilePicker.h @@ -99,7 +99,7 @@ class FurnaceFilePicker { std::thread* fileThread; std::mutex entryLock; String windowName; - String path, filter; + String path, filter, searchQuery; String failMessage; String homeDir; String entryName; @@ -110,7 +110,7 @@ class FurnaceFilePicker { bool sortInvert[FP_SORT_MAX]; bool multiSelect; bool confirmOverwrite, dirSelect, noClose, isModal, isEmbed, hasSizeConstraints; - bool isPathBookmarked; + bool isPathBookmarked, isSearch; int scheduledSort, imguiFlags; size_t curFilterType; SortModes sortMode; @@ -147,12 +147,14 @@ class FurnaceFilePicker { String normalizePath(const String& which); bool isPathAbsolute(const String& p); void addBookmark(const String& p, String n=""); + FileEntry* makeEntry(void* _entry, const char* prefix=NULL); void drawFileList(ImVec2& tableSize, bool& acknowledged); void drawBookmarks(ImVec2& tableSize, String& newDir); public: void readDirectorySub(); + void searchSub(String subPath, int depth); void setHomeDir(String where); FilePickerStatus getStatus(); const String& getPath();