From 9f8d2a75ecfc313d986dfd8678e8df07ca9b4d98 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sat, 29 Oct 2022 20:06:32 +0200 Subject: [PATCH 01/21] NW triple null horror --- main.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/main.cpp b/main.cpp index 03123db..d228b47 100644 --- a/main.cpp +++ b/main.cpp @@ -98,17 +98,22 @@ int listDirectory(std::string path, std::vector *directoryContents){ */ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest){ + static int lmao = 0; char* nextOcurrence = strchr(volumePaths, separator); - if (nextOcurrence[0] == nextOcurrence[sizeof(TCHAR)]) return 0; - + //TODO Test de cadena original + char* itemPath; itemPath = (char*)calloc(1, volumePathsBufferSize); - for (int i = 0; &volumePaths[i] == nextOcurrence; i++) { + //for (int i = 0; &volumePaths[i] == nextOcurrence; i++) { + for (int i = 0; volumePaths[i] != nextOcurrence[0]; i++) { itemPath[i] = volumePaths[i]; } dest->push_back(itemPath); - - + //TODO Test de recursividad + //if (debug) { std::cout << dest->at(0) << std::endl; exit(1); } + if (debug && lmao == 3) { std::cout << volumePaths << std::endl; exit(1); } + lmao++; + if (nextOcurrence[0] == nextOcurrence[sizeof(TCHAR)]) return 0; return explodePaths(nextOcurrence, volumePathsBufferSize, separator, dest); } @@ -116,7 +121,7 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, int listVolumes(std::vector *onPresentPaths){ //int listVolumes(){ HANDLE hFind = INVALID_HANDLE_VALUE; - char separator = '\0'; + const char separator = '\0'; int numVolumes = 0; //Volume name @@ -126,7 +131,14 @@ int listVolumes(std::vector *onPresentPaths){ int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); unsigned long volumePathLength; TCHAR volumePaths[volumePathsBufferSize]; - + //TODO BORRAR + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '4'); + for (int i = 0; i < volumePathsBufferSize; i++){ + std::cout << std::to_string(i) + ": "; + std::cout << volumePaths[i]; + std::cout << " | "; + } + std::cout << std::endl << "I IA" << std::endl; hFind = FindFirstVolume(volumeName, volumeNameSize); //if (debug) std::cout << volumeNameSize << std::endl; @@ -139,6 +151,17 @@ int listVolumes(std::vector *onPresentPaths){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } if (debug) std::cout << volumeName; if (debug) { + if (debug && volumePaths[0] == 'E') { + std::cout << volumePathLength << std::endl; + /*exit(1);*/ + for (int i = 0; i < volumePathsBufferSize; i++){ + std::cout << std::to_string(i) + ": "; + std::cout << volumePaths[i]; + std::cout << " | "; + } + std::cout << std::endl << "I IA" << std::endl; + exit(1); + } if(explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ if (debug) std::cout << volumePaths << std::endl; for (int i = 0; i < onPresentPaths->size(); i++){ From a7a8ba613f3f8caa36f231af392f30d86d6d50e9 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sat, 29 Oct 2022 20:51:45 +0200 Subject: [PATCH 02/21] Listar volumenes en consola --- main.cpp | 84 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/main.cpp b/main.cpp index d228b47..d3c7917 100644 --- a/main.cpp +++ b/main.cpp @@ -97,24 +97,47 @@ int listDirectory(std::string path, std::vector *directoryContents){ if (nextOcurrence == NULL) return 0; */ -int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest){ - static int lmao = 0; +int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 0){ + //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. + //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew + + static int lmao = 0; char* nextOcurrence = strchr(volumePaths, separator); - //TODO Test de cadena original + //TODO Test de cadena original; + // if (debug && lmao) { + // std::cout << std::to_string(lmao) + ":lmao " << std::endl; + // for (int i = 0; i < volumePathsBufferSize; i++){ + // std::cout << std::to_string(i) + ": "; + // std::cout << volumePaths[i]; + // std::cout << " | "; + // } + // std::cout << std::endl << "I IA" << std::endl; + // exit(1); + // } + // if (debug && volumePaths[0] == 'E') lmao = 1; char* itemPath; itemPath = (char*)calloc(1, volumePathsBufferSize); //for (int i = 0; &volumePaths[i] == nextOcurrence; i++) { - for (int i = 0; volumePaths[i] != nextOcurrence[0]; i++) { - itemPath[i] = volumePaths[i]; + int pathLengthIdx = 0; + for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { + if (debug) { std::cout << volumePaths[0]; std::cout << " ENTERS " + std::to_string(pathLengthIdx) << std::endl; } + itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; } dest->push_back(itemPath); + if (debug) { + + std::cout << dest->at(dest->size() - 1); + std::cout << " EXPLODEPATH" << std::endl; + + } //TODO Test de recursividad //if (debug) { std::cout << dest->at(0) << std::endl; exit(1); } - if (debug && lmao == 3) { std::cout << volumePaths << std::endl; exit(1); } - lmao++; - if (nextOcurrence[0] == nextOcurrence[sizeof(TCHAR)]) return 0; - return explodePaths(nextOcurrence, volumePathsBufferSize, separator, dest); + //if (debug && lmao == 3) { std::cout << volumePaths << std::endl; exit(1); } + //lmao++; + depth++; + if (nextOcurrence[0] == nextOcurrence[1]) return depth; + return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest); } @@ -131,17 +154,10 @@ int listVolumes(std::vector *onPresentPaths){ int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); unsigned long volumePathLength; TCHAR volumePaths[volumePathsBufferSize]; - //TODO BORRAR - std::fill(volumePaths, volumePaths + volumePathsBufferSize, '4'); - for (int i = 0; i < volumePathsBufferSize; i++){ - std::cout << std::to_string(i) + ": "; - std::cout << volumePaths[i]; - std::cout << " | "; - } - std::cout << std::endl << "I IA" << std::endl; + //TODO necesario? di + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); - hFind = FindFirstVolume(volumeName, volumeNameSize); - //if (debug) std::cout << volumeNameSize << std::endl; + hFind = FindFirstVolume(volumeName, volumeNameSize); //if (debug) std::cout << volumeNameSize << std::endl; if (INVALID_HANDLE_VALUE == hFind) return -2; do { //if (debug) std::cout << volumeName << std::endl; @@ -150,20 +166,19 @@ int listVolumes(std::vector *onPresentPaths){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } if (debug) std::cout << volumeName; - if (debug) { - if (debug && volumePaths[0] == 'E') { - std::cout << volumePathLength << std::endl; - /*exit(1);*/ - for (int i = 0; i < volumePathsBufferSize; i++){ - std::cout << std::to_string(i) + ": "; - std::cout << volumePaths[i]; - std::cout << " | "; - } - std::cout << std::endl << "I IA" << std::endl; - exit(1); - } + if (debug) { + // if (debug && volumePaths[0] == 'E') { + // std::cout << volumePathLength << std::endl; + // /*exit(1);*/ + // for (int i = 0; i < volumePathsBufferSize; i++){ + // std::cout << std::to_string(i) + ": "; + // std::cout << volumePaths[i]; + // std::cout << " | "; + // } + // std::cout << std::endl << "I IA" << std::endl; + // exit(1); + // } if(explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ - if (debug) std::cout << volumePaths << std::endl; for (int i = 0; i < onPresentPaths->size(); i++){ std::cout << onPresentPaths->at(i) << std::endl; } @@ -178,7 +193,8 @@ int listVolumes(std::vector *onPresentPaths){ } numVolumes++; - + //TODO necesario? di CAMBIAR 4 A NUL + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '4'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); FindVolumeClose(hFind); @@ -309,7 +325,7 @@ int main(int, char**) static bool isListVolumesAdequate = true; if (debug && isListVolumesAdequate) { isListVolumesAdequate = false; - std::cout << isListVolumesAdequate << std::endl; + //std::cout << isListVolumesAdequate << std::endl; std::vector onPresentPaths; listVolumes(&onPresentPaths); } From 1506cbf5c1c4d2e137541835036c440decd1e43f Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sun, 30 Oct 2022 01:44:17 +0200 Subject: [PATCH 03/21] Cambio de volumen funcional --- main.cpp | 96 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/main.cpp b/main.cpp index d3c7917..aa1d885 100644 --- a/main.cpp +++ b/main.cpp @@ -42,6 +42,7 @@ bool retrieveCurrentDirectory(char** currentPath){ } bool moveDirectory(char** currentPath){ + if (debug) std::cout << *currentPath << std::endl; if(SetCurrentDirectory(*currentPath)) return true; return false; } @@ -70,7 +71,7 @@ int listDirectory(std::string path, std::vector *directoryContents){ itemPath[idx] = ffd.cFileName[idx]; idx++; } while (ffd.cFileName[idx]); - + //TODO multi info Struct if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //strcat(itemPath, " "); } else { @@ -97,47 +98,31 @@ int listDirectory(std::string path, std::vector *directoryContents){ if (nextOcurrence == NULL) return 0; */ -int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 0){ +int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 1){ //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew - static int lmao = 0; char* nextOcurrence = strchr(volumePaths, separator); - //TODO Test de cadena original; - // if (debug && lmao) { - // std::cout << std::to_string(lmao) + ":lmao " << std::endl; - // for (int i = 0; i < volumePathsBufferSize; i++){ - // std::cout << std::to_string(i) + ": "; - // std::cout << volumePaths[i]; - // std::cout << " | "; - // } - // std::cout << std::endl << "I IA" << std::endl; - // exit(1); - // } - // if (debug && volumePaths[0] == 'E') lmao = 1; char* itemPath; itemPath = (char*)calloc(1, volumePathsBufferSize); - //for (int i = 0; &volumePaths[i] == nextOcurrence; i++) { + int pathLengthIdx = 0; for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { if (debug) { std::cout << volumePaths[0]; std::cout << " ENTERS " + std::to_string(pathLengthIdx) << std::endl; } itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; } + itemPath[pathLengthIdx + 1] = '\0'; dest->push_back(itemPath); + if (debug) { - std::cout << dest->at(dest->size() - 1); - std::cout << " EXPLODEPATH" << std::endl; - + std::cout << " EXPLODEPATH" << std::endl; } - //TODO Test de recursividad - //if (debug) { std::cout << dest->at(0) << std::endl; exit(1); } - //if (debug && lmao == 3) { std::cout << volumePaths << std::endl; exit(1); } - //lmao++; - depth++; + if (nextOcurrence[0] == nextOcurrence[1]) return depth; - return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest); + depth++; + return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest, depth); } @@ -167,6 +152,7 @@ int listVolumes(std::vector *onPresentPaths){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } if (debug) std::cout << volumeName; if (debug) { + //pathSchecker // if (debug && volumePaths[0] == 'E') { // std::cout << volumePathLength << std::endl; // /*exit(1);*/ @@ -178,10 +164,11 @@ int listVolumes(std::vector *onPresentPaths){ // std::cout << std::endl << "I IA" << std::endl; // exit(1); // } - if(explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ for (int i = 0; i < onPresentPaths->size(); i++){ std::cout << onPresentPaths->at(i) << std::endl; } + } //std::cout << volumePaths << std::endl; } @@ -192,11 +179,11 @@ int listVolumes(std::vector *onPresentPaths){ if (debug) std::cout << "cagaste" << std::endl; } - numVolumes++; //TODO necesario? di CAMBIAR 4 A NUL std::fill(volumePaths, volumePaths + volumePathsBufferSize, '4'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); + if (debug) std::cout << std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN" << std::endl; FindVolumeClose(hFind); return numVolumes; } @@ -320,21 +307,28 @@ int main(int, char**) } { - ImGui::Begin("File Picker in 4K"); - //TODO test listar volumenes + ImGui::Begin("File Picker in 4K"); + static bool isListVolumesAdequate = true; + static bool isListDirectoriesAdequate = true; + //Listar UNA VEZ volumenes + + static int numVolumes; + static std::vector onPresentPaths; + if (debug && isListVolumesAdequate) { isListVolumesAdequate = false; //std::cout << isListVolumesAdequate << std::endl; - std::vector onPresentPaths; - listVolumes(&onPresentPaths); + numVolumes = listVolumes(&onPresentPaths); + if (debug) std::cout << std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size()) << std::endl; + //TODO tiyuria } - // - static bool renderListbox = true; + + //Listar UNA VEZ directorios //cursed C momento: necesario ptr en otra var para mandar a char** de firma static char currentPath[MAX_PATH]; - static char* ptr = ¤tPath[0]; + static char* currentPathPtr = ¤tPath[0]; static int currentItemIdx = -1; static std::vector displayContents; @@ -343,10 +337,10 @@ int main(int, char**) if (debug) ImGui::Text("%s primir", currentPath); - if(renderListbox) { + if(isListDirectoriesAdequate) { if (debug) std::cout << "renderiso" << std::endl; - renderListbox = false; - if(!retrieveCurrentDirectory(&ptr)) { + isListDirectoriesAdequate = false; + if(!retrieveCurrentDirectory(¤tPathPtr)) { if (debug) std::cout << "pencaste"; goto filepickerFailure; } @@ -360,9 +354,33 @@ int main(int, char**) displayContents = std::move(directoryContents); } + //It's renderin' time if(numFiles > 0){ ImGui::Text("Select a file:"); if (debug) ImGui::Text("%s %d", currentPath, currentItemIdx); + + //Y a que los VOLUMENES pinta2 pa100pre + //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; + if (numVolumes > 0){ + for (int i = 0; i < onPresentPaths.size(); i++) { + if (i > 0) ImGui::SameLine(); + // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; + // ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); + if(ImGui::Button(onPresentPaths.at(i))){ + moveDirectory(&onPresentPaths.at(i)); + isListDirectoriesAdequate = true; + } + ImGui::PopStyleColor(3); + + // ImGui::PopID(); + } + } + + + //DIRECTORIOS ENCONTRADOS bien pinta2 if (ImGui::BeginListBox("fpLb", ImVec2(-FLT_MIN, 25 * ImGui::GetTextLineHeightWithSpacing()))){ for (int i = 0; i < numFiles; i++) { @@ -373,9 +391,9 @@ int main(int, char**) if (isSelected) { strcat(currentPath, "\\"); strcat(currentPath, displayContents.at(i)); - moveDirectory(&ptr); + moveDirectory(¤tPathPtr); currentItemIdx = -1; - renderListbox = true; + isListDirectoriesAdequate = true; } //ImGui::SetItemDefaultFocus(); From 0fa6d6b0568211d262784f5d951cb00640cc16c8 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sun, 30 Oct 2022 03:08:19 +0100 Subject: [PATCH 04/21] W MoveUp pero random bug al entrar en carpeta --- main.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/main.cpp b/main.cpp index aa1d885..08e396b 100644 --- a/main.cpp +++ b/main.cpp @@ -47,10 +47,11 @@ bool moveDirectory(char** currentPath){ return false; } -int listDirectory(std::string path, std::vector *directoryContents){ +int listDirectory(std::string path, std::vector *directoryContents, std::vector *desiredExtensions = nullptr){ HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA ffd; LARGE_INTEGER filesize; + bool skipDueToExtension = false; int numFiles = 0; if (path.length() > (MAX_PATH - 3)) return -1; @@ -58,6 +59,42 @@ int listDirectory(std::string path, std::vector *directoryContents){ hFind = FindFirstFile(path.c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) return -2; do { + + //TODO laberga k momento educativo + if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; + + //Codigo pa saltarse arxibus k no estiendan + /*if(desiredExtensions != nullptr){ + TCHAR* fileExtension = strrchr(ffd.cFileName, '.'); + if (debug && fileExtension != NULL) { std::cout << fileExtension; std::cout << " <- FILTERING EXTENSION" << std::endl; } + std::cout << ffd.cFileName << std::endl; + + //TODO tabien no??? + if (fileExtension == NULL) continue; + + for (int i = 0; i < desiredExtensions->size(); i++) { + if (debug) { std::cout << std::to_string(strcmp(desiredExtensions->at(i).c_str(), fileExtension)) << std::endl; } + if (strcmp(desiredExtensions->at(i).c_str(), fileExtension)){ + skipDueToExtension = true; + } + } + }*/ + + + if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && skipDueToExtension) continue; + + //TODO multi info Struct + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + //strcat(itemPath, " "); + } else { + + // filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); + // char buf[16]; buf[0] = ' '; + // itoa(filesize.QuadPart, buf + 1 , 10 ); + // strcat(itemPath, buf); + } + + char* itemPath; if (directoryContents->size() <= numFiles){ itemPath = (char*)calloc(1, MAX_PATH * sizeof(WCHAR)); @@ -65,21 +102,12 @@ int listDirectory(std::string path, std::vector *directoryContents){ } else { itemPath = directoryContents->at(numFiles); } - + uint64_t idx = 0; do { itemPath[idx] = ffd.cFileName[idx]; idx++; } while (ffd.cFileName[idx]); - //TODO multi info Struct - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - //strcat(itemPath, " "); - } else { - // filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); - // char buf[16]; buf[0] = ' '; - // itoa(filesize.QuadPart, buf + 1 , 10 ); - // strcat(itemPath, buf); - } numFiles++; } while (FindNextFile(hFind, &ffd) != 0); @@ -89,7 +117,6 @@ int listDirectory(std::string path, std::vector *directoryContents){ /* VOLUMES */ - //new char*[CharCount * sizeof(WCHAR) /*idea, si quisiera que empezase en punto X del string coger el param opcional y crear un puntero a la posicion X del array para empezar desde ahi @@ -97,7 +124,6 @@ int listDirectory(std::string path, std::vector *directoryContents){ char separator, std::vector *dest, , int startingFrom = 0); if (nextOcurrence == NULL) return 0; */ - int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 1){ //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew @@ -176,7 +202,7 @@ int listVolumes(std::vector *onPresentPaths){ if (debug) std::cout << "THE END" << std::endl; }else{ - if (debug) std::cout << "cagaste" << std::endl; + if (debug) std::cout << "no volumes found wtf" << std::endl; } //TODO necesario? di CAMBIAR 4 A NUL @@ -191,7 +217,6 @@ int listVolumes(std::vector *onPresentPaths){ /* FIN FILE PICKER MOMENTO */ - int main(int, char**) { // Setup window @@ -345,7 +370,9 @@ int main(int, char**) goto filepickerFailure; } // - numFiles = (listDirectory(std::string(currentPath), &directoryContents)); + //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE + static std::vector restrictToExe = { "exe" }; + numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); //std::cout << numFiles; if (numFiles < 0) { if (debug) std::cout << "pencaste 2 el repencazo"; @@ -361,10 +388,11 @@ int main(int, char**) //Y a que los VOLUMENES pinta2 pa100pre //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; - if (numVolumes > 0){ + if (numVolumes > 0) { for (int i = 0; i < onPresentPaths.size(); i++) { if (i > 0) ImGui::SameLine(); // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; + //TODO dabid aiuda // ImGui::PushID(i); ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); @@ -374,10 +402,18 @@ int main(int, char**) isListDirectoriesAdequate = true; } ImGui::PopStyleColor(3); - // ImGui::PopID(); } } + //Permanentes e increiblemente utilitarios botones en Cuatro K + //c pervirtio con unicode std::wstring s(L"←→↑↓"); + //TODO TREMENDA MANDING DICK + static char moveUp[2] = {'.', '.'}; + static char* moveUpPtr = &moveUp[0]; + if(ImGui::Button("Move Up")){ + moveDirectory(&moveUpPtr); + isListDirectoriesAdequate = true; + } //DIRECTORIOS ENCONTRADOS bien pinta2 From af3af6bc4f181ccd0da86bc303e50f340f7d85d2 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sun, 30 Oct 2022 22:04:39 +0100 Subject: [PATCH 05/21] bug fixes --- main.cpp | 80 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/main.cpp b/main.cpp index 08e396b..21756c4 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,14 @@ #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION +#define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) +#define log_debugcpp(str) do { \ + if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << (str) << std::endl; \ + } while (0) +#define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) +#define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) +#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) + #include "imgui/misc/single_file/imgui_single_file.h" #include #include @@ -25,6 +33,17 @@ static void glfw_error_callback(int error, const char* description) bool debug = true; +// int debugVerbosity = 0; +// enum enumDebugVerbosity { + // DEBUG_DIRECTORY = (1<<0), + // DEBUG_VOLUME = (1<<1), + // DEBUG_EXTENSION = (1<<2) +// }; +// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; +// debugVerbosity &= ~DEBUG_DIRECTORY; +// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); + + typedef struct { std::vector size; std::vector name; @@ -165,7 +184,6 @@ int listVolumes(std::vector *onPresentPaths){ int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); unsigned long volumePathLength; TCHAR volumePaths[volumePathsBufferSize]; - //TODO necesario? di std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); hFind = FindFirstVolume(volumeName, volumeNameSize); //if (debug) std::cout << volumeNameSize << std::endl; @@ -177,27 +195,26 @@ int listVolumes(std::vector *onPresentPaths){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } if (debug) std::cout << volumeName; - if (debug) { - //pathSchecker - // if (debug && volumePaths[0] == 'E') { - // std::cout << volumePathLength << std::endl; - // /*exit(1);*/ - // for (int i = 0; i < volumePathsBufferSize; i++){ - // std::cout << std::to_string(i) + ": "; - // std::cout << volumePaths[i]; - // std::cout << " | "; - // } - // std::cout << std::endl << "I IA" << std::endl; - // exit(1); + + //pathSchecker + // if (debug && volumePaths[0] == 'E') { + // std::cout << volumePathLength << std::endl; + // /*exit(1);*/ + // for (int i = 0; i < volumePathsBufferSize; i++){ + // std::cout << std::to_string(i) + ": "; + // std::cout << volumePaths[i]; + // std::cout << " | "; // } - if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ - for (int i = 0; i < onPresentPaths->size(); i++){ - std::cout << onPresentPaths->at(i) << std::endl; - } - + // std::cout << std::endl << "I IA" << std::endl; + // exit(1); + // } + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ + for (int i = 0; i < onPresentPaths->size(); i++){ + std::cout << onPresentPaths->at(i) << std::endl; } - //std::cout << volumePaths << std::endl; } + //std::cout << volumePaths << std::endl; + if (debug) std::cout << std::to_string(volumePathLength) + " " ; if (debug) std::cout << "THE END" << std::endl; @@ -205,8 +222,7 @@ int listVolumes(std::vector *onPresentPaths){ if (debug) std::cout << "no volumes found wtf" << std::endl; } - //TODO necesario? di CAMBIAR 4 A NUL - std::fill(volumePaths, volumePaths + volumePathsBufferSize, '4'); + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); if (debug) std::cout << std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN" << std::endl; @@ -341,12 +357,11 @@ int main(int, char**) static int numVolumes; static std::vector onPresentPaths; - if (debug && isListVolumesAdequate) { + if (isListVolumesAdequate) { isListVolumesAdequate = false; //std::cout << isListVolumesAdequate << std::endl; numVolumes = listVolumes(&onPresentPaths); if (debug) std::cout << std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size()) << std::endl; - //TODO tiyuria } @@ -356,6 +371,7 @@ int main(int, char**) static char* currentPathPtr = ¤tPath[0]; static int currentItemIdx = -1; + //TODO David momento static std::vector displayContents; std::vector directoryContents; static int numFiles = 0; @@ -363,7 +379,7 @@ int main(int, char**) if (debug) ImGui::Text("%s primir", currentPath); if(isListDirectoriesAdequate) { - if (debug) std::cout << "renderiso" << std::endl; + log_debugcpp("renderiso"); isListDirectoriesAdequate = false; if(!retrieveCurrentDirectory(¤tPathPtr)) { if (debug) std::cout << "pencaste"; @@ -371,14 +387,22 @@ int main(int, char**) } // //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE - static std::vector restrictToExe = { "exe" }; + static std::vector restrictToExe{"exe", "DSDFD"}; numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); //std::cout << numFiles; if (numFiles < 0) { + ImGui::Text("Path not valid"); if (debug) std::cout << "pencaste 2 el repencazo"; goto filepickerFailure; } + if (numFiles == 0) { + if (debug) std::cout << std::to_string (numFiles) << "<- NUM FILES" << std::endl; + char empty[] = "Empty"; + numFiles++; + directoryContents.push_back(empty); + } displayContents = std::move(directoryContents); + directoryContents.clear(); } //It's renderin' time @@ -392,7 +416,6 @@ int main(int, char**) for (int i = 0; i < onPresentPaths.size(); i++) { if (i > 0) ImGui::SameLine(); // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; - //TODO dabid aiuda // ImGui::PushID(i); ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); @@ -405,10 +428,11 @@ int main(int, char**) // ImGui::PopID(); } } + + //Permanentes e increiblemente utilitarios botones en Cuatro K //c pervirtio con unicode std::wstring s(L"←→↑↓"); - //TODO TREMENDA MANDING DICK - static char moveUp[2] = {'.', '.'}; + static char moveUp[] = ".."; static char* moveUpPtr = &moveUp[0]; if(ImGui::Button("Move Up")){ moveDirectory(&moveUpPtr); From b4ff0148ee84123c93fde018f1edac2a1e9e7471 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Mon, 31 Oct 2022 15:33:42 +0100 Subject: [PATCH 06/21] =?UTF-8?q?peque=C3=B1os=20bugixes;=20debug=20facher?= =?UTF-8?q?ito?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- main.cpp | 137 +++++++++++++++++++++++++---------------------------- 2 files changed, 68 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index fabee7f..2abc06d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.ini -*.exe \ No newline at end of file +*.exe +*.rdbg +*.pdb \ No newline at end of file diff --git a/main.cpp b/main.cpp index 21756c4..d49334d 100644 --- a/main.cpp +++ b/main.cpp @@ -78,8 +78,6 @@ int listDirectory(std::string path, std::vector *directoryContents, std:: hFind = FindFirstFile(path.c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) return -2; do { - - //TODO laberga k momento educativo if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; //Codigo pa saltarse arxibus k no estiendan @@ -127,6 +125,7 @@ int listDirectory(std::string path, std::vector *directoryContents, std:: itemPath[idx] = ffd.cFileName[idx]; idx++; } while (ffd.cFileName[idx]); + itemPath[idx] = '\0'; numFiles++; } while (FindNextFile(hFind, &ffd) != 0); @@ -370,10 +369,10 @@ int main(int, char**) static char currentPath[MAX_PATH]; static char* currentPathPtr = ¤tPath[0]; - static int currentItemIdx = -1; + //static int currentItemIdx = -1; //TODO David momento - static std::vector displayContents; - std::vector directoryContents; + //static std::vector displayContents; + static std::vector directoryContents; static int numFiles = 0; if (debug) ImGui::Text("%s primir", currentPath); @@ -391,82 +390,76 @@ int main(int, char**) numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); //std::cout << numFiles; if (numFiles < 0) { - ImGui::Text("Path not valid"); + //ImGui::Text("Path not valid"); if (debug) std::cout << "pencaste 2 el repencazo"; goto filepickerFailure; } - if (numFiles == 0) { - if (debug) std::cout << std::to_string (numFiles) << "<- NUM FILES" << std::endl; - char empty[] = "Empty"; - numFiles++; - directoryContents.push_back(empty); - } - displayContents = std::move(directoryContents); - directoryContents.clear(); + + //displayContents = std::move(directoryContents); + //directoryContents.clear(); } //It's renderin' time - if(numFiles > 0){ - ImGui::Text("Select a file:"); - if (debug) ImGui::Text("%s %d", currentPath, currentItemIdx); - - //Y a que los VOLUMENES pinta2 pa100pre - //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; - if (numVolumes > 0) { - for (int i = 0; i < onPresentPaths.size(); i++) { - if (i > 0) ImGui::SameLine(); - // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; - // ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); - if(ImGui::Button(onPresentPaths.at(i))){ - moveDirectory(&onPresentPaths.at(i)); - isListDirectoriesAdequate = true; - } - ImGui::PopStyleColor(3); - // ImGui::PopID(); + ImGui::Text("Select a file:"); + if (debug) ImGui::Text("%s ", currentPath); + + //Y a que los VOLUMENES pinta2 pa100pre + //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; + if (numVolumes > 0) { + for (int i = 0; i < onPresentPaths.size(); i++) { + if (i > 0) ImGui::SameLine(); + // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; + // ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); + if(ImGui::Button(onPresentPaths.at(i))){ + moveDirectory(&onPresentPaths.at(i)); + isListDirectoriesAdequate = true; } + ImGui::PopStyleColor(3); + // ImGui::PopID(); } - - - //Permanentes e increiblemente utilitarios botones en Cuatro K - //c pervirtio con unicode std::wstring s(L"←→↑↓"); - static char moveUp[] = ".."; - static char* moveUpPtr = &moveUp[0]; - if(ImGui::Button("Move Up")){ - moveDirectory(&moveUpPtr); - isListDirectoriesAdequate = true; - } - - - //DIRECTORIOS ENCONTRADOS bien pinta2 - if (ImGui::BeginListBox("fpLb", ImVec2(-FLT_MIN, 25 * ImGui::GetTextLineHeightWithSpacing()))){ - for (int i = 0; i < numFiles; i++) { - - const bool isSelected = (currentItemIdx == i); - if (ImGui::Selectable(displayContents.at(i), isSelected)) - currentItemIdx = i; - - if (isSelected) { - strcat(currentPath, "\\"); - strcat(currentPath, displayContents.at(i)); - moveDirectory(¤tPathPtr); - currentItemIdx = -1; - isListDirectoriesAdequate = true; - } - //ImGui::SetItemDefaultFocus(); - - } - ImGui::EndListBox(); - } - } else { - ImGui::Text("cagaste"); } - //ImGui::TreePop(); - filepickerFailure: - ImGui::End(); - + + + //Permanentes e increiblemente utilitarios botones en Cuatro K + //c pervirtio con unicode std::wstring s(L"←→↑↓"); + static char moveUp[] = ".."; + static char* moveUpPtr = &moveUp[0]; + if(ImGui::Button("Move Up")){ + moveDirectory(&moveUpPtr); + isListDirectoriesAdequate = true; + } + + + //DIRECTORIOS ENCONTRADOS bien pinta2 + if (ImGui::BeginListBox("fpLb", ImVec2(-FLT_MIN, 25 * ImGui::GetTextLineHeightWithSpacing()))){ + int idx = 0; + for (; idx < numFiles; idx++) { + + //const bool isSelected = (currentItemIdx == i); + bool isSelected = false; + if (ImGui::Selectable(directoryContents.at(idx), &isSelected)){ + //currentItemIdx = i; + strcat(currentPath, "\\"); + strcat(currentPath, directoryContents.at(idx)); + moveDirectory(¤tPathPtr); + //currentItemIdx = -1; + isListDirectoriesAdequate = true; + } + + //ImGui::SetItemDefaultFocus(); + + } + if (!idx) ImGui::Text("Folder is empty"); + ImGui::EndListBox(); + } + + //ImGui::TreePop(); + filepickerFailure: + ImGui::End(); + } From 60acf709c1ebf8e517d5b5324913ec7ce5ff9591 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Mon, 31 Oct 2022 17:24:20 +0100 Subject: [PATCH 07/21] implementado struct muchoinfo directory --- main.cpp | 112 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/main.cpp b/main.cpp index d49334d..861dda6 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,7 @@ #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION + #define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) #define log_debugcpp(str) do { \ if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << (str) << std::endl; \ @@ -13,6 +14,9 @@ #define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) #define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) + +#define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) + #include "imgui/misc/single_file/imgui_single_file.h" #include #include @@ -45,9 +49,16 @@ bool debug = true; typedef struct { - std::vector size; - std::vector name; -} directoriesInfo, volumesInfo; + long long size; + //TODO yel spacsio NELLA MEMoria + TCHAR name[MAX_LISTDIR_PATH_LENGTH]; + SYSTEMTIME createTime; + SYSTEMTIME lastAccessTime; + SYSTEMTIME lastWriteTime; + bool isFile; + bool isHidden; + +} directoriesInfo; //TODO UNICORDEO /* FILE PICKER MOMENTO */ @@ -61,25 +72,25 @@ bool retrieveCurrentDirectory(char** currentPath){ } bool moveDirectory(char** currentPath){ - if (debug) std::cout << *currentPath << std::endl; + log_debugcpp(*currentPath); if(SetCurrentDirectory(*currentPath)) return true; return false; } -int listDirectory(std::string path, std::vector *directoryContents, std::vector *desiredExtensions = nullptr){ +int listDirectory(std::string path, std::vector *directoryContents, std::vector *desiredExtensions = nullptr){ HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA ffd; LARGE_INTEGER filesize; bool skipDueToExtension = false; int numFiles = 0; - if (path.length() > (MAX_PATH - 3)) return -1; + if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return -1; path = path + "\\*"; hFind = FindFirstFile(path.c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) return -2; do { if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; - + log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); //Codigo pa saltarse arxibus k no estiendan /*if(desiredExtensions != nullptr){ TCHAR* fileExtension = strrchr(ffd.cFileName, '.'); @@ -95,38 +106,45 @@ int listDirectory(std::string path, std::vector *directoryContents, std:: skipDueToExtension = true; } } - }*/ - - - if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && skipDueToExtension) continue; - - //TODO multi info Struct - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - //strcat(itemPath, " "); - } else { - - // filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); - // char buf[16]; buf[0] = ' '; - // itoa(filesize.QuadPart, buf + 1 , 10 ); - // strcat(itemPath, buf); } + if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && skipDueToExtension) continue;*/ - - char* itemPath; + //TODO Borrar legado + // char* itemPath; + // if (directoryContents->size() <= numFiles){ + // itemPath = (char*)calloc(1, MAX_PATH * sizeof(WCHAR)); + // directoryContents->push_back(itemPath); + // } else { + // itemPath = directoryContents->at(numFiles); + // } + directoriesInfo* itemInfo; if (directoryContents->size() <= numFiles){ - itemPath = (char*)calloc(1, MAX_PATH * sizeof(WCHAR)); - directoryContents->push_back(itemPath); + itemInfo = new directoriesInfo(); + directoryContents->push_back(itemInfo); } else { - itemPath = directoryContents->at(numFiles); + itemInfo = directoryContents->at(numFiles); } - + log_debugcpp("MEMORIA ASIGNADA iteracion " + std::to_string(numFiles)); + + //A registrar info, fiera + itemInfo->isFile = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; + itemInfo->isHidden = (ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? true : false; + //Tamaño + filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); + itemInfo->size = filesize.QuadPart; + //Fechitssss + FileTimeToSystemTime(&ffd.ftCreationTime, &itemInfo->createTime); + FileTimeToSystemTime(&ffd.ftLastAccessTime, &itemInfo->lastAccessTime); + FileTimeToSystemTime(&ffd.ftLastWriteTime, &itemInfo->lastWriteTime); + //Nombre de la cosita uint64_t idx = 0; do { - itemPath[idx] = ffd.cFileName[idx]; + itemInfo->name[idx] = ffd.cFileName[idx]; idx++; } while (ffd.cFileName[idx]); - itemPath[idx] = '\0'; - + itemInfo->name[idx] = '\0'; + log_debugcpp("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); + numFiles++; } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); @@ -214,17 +232,16 @@ int listVolumes(std::vector *onPresentPaths){ } //std::cout << volumePaths << std::endl; - if (debug) std::cout << std::to_string(volumePathLength) + " " ; - if (debug) std::cout << "THE END" << std::endl; + log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); + log_debugcpp("THE END"); }else{ - if (debug) std::cout << "no volumes found wtf" << std::endl; + log_debugcpp("no volumes found wtf"); } std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); - - if (debug) std::cout << std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN" << std::endl; + log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); FindVolumeClose(hFind); return numVolumes; } @@ -351,8 +368,8 @@ int main(int, char**) static bool isListVolumesAdequate = true; static bool isListDirectoriesAdequate = true; - //Listar UNA VEZ volumenes + //Listar UNA VEZ volumenes static int numVolumes; static std::vector onPresentPaths; @@ -360,7 +377,7 @@ int main(int, char**) isListVolumesAdequate = false; //std::cout << isListVolumesAdequate << std::endl; numVolumes = listVolumes(&onPresentPaths); - if (debug) std::cout << std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size()) << std::endl; + log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); } @@ -370,28 +387,27 @@ int main(int, char**) static char* currentPathPtr = ¤tPath[0]; //static int currentItemIdx = -1; - //TODO David momento - //static std::vector displayContents; - static std::vector directoryContents; + //static std::vector directoryContents; + static std::vector directoryContents; static int numFiles = 0; - if (debug) ImGui::Text("%s primir", currentPath); + ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); if(isListDirectoriesAdequate) { - log_debugcpp("renderiso"); + log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); isListDirectoriesAdequate = false; if(!retrieveCurrentDirectory(¤tPathPtr)) { - if (debug) std::cout << "pencaste"; + log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); goto filepickerFailure; } // //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE - static std::vector restrictToExe{"exe", "DSDFD"}; + //static std::vector restrictToExe{"exe", "DSDFD"}; numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); //std::cout << numFiles; if (numFiles < 0) { //ImGui::Text("Path not valid"); - if (debug) std::cout << "pencaste 2 el repencazo"; + log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO"); goto filepickerFailure; } @@ -401,7 +417,7 @@ int main(int, char**) //It's renderin' time ImGui::Text("Select a file:"); - if (debug) ImGui::Text("%s ", currentPath); + if (debug) ImGui::Text("%s TEMPADDRBAR", currentPath); //Y a que los VOLUMENES pinta2 pa100pre //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; @@ -440,10 +456,10 @@ int main(int, char**) //const bool isSelected = (currentItemIdx == i); bool isSelected = false; - if (ImGui::Selectable(directoryContents.at(idx), &isSelected)){ + if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ //currentItemIdx = i; strcat(currentPath, "\\"); - strcat(currentPath, directoryContents.at(idx)); + strcat(currentPath, directoryContents.at(idx)->name); moveDirectory(¤tPathPtr); //currentItemIdx = -1; isListDirectoriesAdequate = true; From b43817babf28d6413a11c87ef90fd6cb44fcb92f Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Tue, 1 Nov 2022 00:24:58 +0100 Subject: [PATCH 08/21] primera aproximacion a interfaz --- main.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/main.cpp b/main.cpp index 861dda6..7036d2e 100644 --- a/main.cpp +++ b/main.cpp @@ -448,32 +448,103 @@ int main(int, char**) isListDirectoriesAdequate = true; } - - //DIRECTORIOS ENCONTRADOS bien pinta2 - if (ImGui::BeginListBox("fpLb", ImVec2(-FLT_MIN, 25 * ImGui::GetTextLineHeightWithSpacing()))){ - int idx = 0; - for (; idx < numFiles; idx++) { + //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS + static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; + if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + //VOLUMENES + static ImGuiTableFlags volumesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY ; + if (ImGui::BeginTable("##volumesTable", 1, volumesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableSetupColumn("Volumes", ImGuiTableColumnFlags_NoHide); + //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); - //const bool isSelected = (currentItemIdx == i); - bool isSelected = false; - if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ - //currentItemIdx = i; - strcat(currentPath, "\\"); - strcat(currentPath, directoryContents.at(idx)->name); - moveDirectory(¤tPathPtr); - //currentItemIdx = -1; - isListDirectoriesAdequate = true; + if (numVolumes > 0) { + for (int i = 0; i < onPresentPaths.size(); i++) { + ImGui::TableNextColumn(); + //if (i > 0) ImGui::SameLine(); + // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; + // ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); + if(ImGui::Button(onPresentPaths.at(i))){ + moveDirectory(&onPresentPaths.at(i)); + isListDirectoriesAdequate = true; + } + ImGui::PopStyleColor(3); + if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); + // ImGui::PopID(); + } } - - //ImGui::SetItemDefaultFocus(); - + ImGui::EndTable(); } - if (!idx) ImGui::Text("Folder is empty"); - ImGui::EndListBox(); + + ImGui::TableNextColumn(); + + + //DIRECTORIOS ENCONTRADOS bien pinta2 + static ImGuiTableFlags directoriesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |ImGuiTableFlags_ScrollY ; + if (ImGui::BeginTable("##directoriesTable", 3, directoriesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_NoHide); + //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); + ImGui::TableHeadersRow(); + + int idx = 0; + for (; idx < numFiles; idx++) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + //NAME + bool isSelected = false; + if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ + //currentItemIdx = i; + strcat(currentPath, "\\"); + strcat(currentPath, directoryContents.at(idx)->name); + moveDirectory(¤tPathPtr); + //currentItemIdx = -1; + isListDirectoriesAdequate = true; + } + ImGui::TableNextColumn(); + + //TYPE + if (directoryContents.at(idx)->isFile) ImGui::Text("Folder"); else ImGui::Text("File"); + ImGui::TableNextColumn(); + + //SIZE + if (!directoryContents.at(idx)->isFile) ImGui::Text("%lld bytes", directoryContents.at(idx)->size); + else ImGui::Text(""); + + //ImGui::SetItemDefaultFocus(); + + } + if (!idx) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Folder is empty"); } + ImGui::EndTable(); + } + + ImGui::EndTable(); + } + + + + + + + //ImGui::TreePop(); + filepickerFailure: + + ImGui::Button("AAAAAAAAAAAAAAAAAAAAAAAAAQ" ); ImGui::End(); } From 65e42bbd1c2d251465ba2bfc36845aa49b3f8447 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Wed, 2 Nov 2022 17:29:05 +0100 Subject: [PATCH 09/21] Boton ocultar, mas estilo --- main.cpp | 75 +++++++++++++++++++------------------------------------- 1 file changed, 25 insertions(+), 50 deletions(-) diff --git a/main.cpp b/main.cpp index 7036d2e..9e9e27e 100644 --- a/main.cpp +++ b/main.cpp @@ -109,14 +109,6 @@ int listDirectory(std::string path, std::vector *directoryCont } if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && skipDueToExtension) continue;*/ - //TODO Borrar legado - // char* itemPath; - // if (directoryContents->size() <= numFiles){ - // itemPath = (char*)calloc(1, MAX_PATH * sizeof(WCHAR)); - // directoryContents->push_back(itemPath); - // } else { - // itemPath = directoryContents->at(numFiles); - // } directoriesInfo* itemInfo; if (directoryContents->size() <= numFiles){ itemInfo = new directoriesInfo(); @@ -189,7 +181,6 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, int listVolumes(std::vector *onPresentPaths){ -//int listVolumes(){ HANDLE hFind = INVALID_HANDLE_VALUE; const char separator = '\0'; @@ -225,6 +216,7 @@ int listVolumes(std::vector *onPresentPaths){ // std::cout << std::endl << "I IA" << std::endl; // exit(1); // } + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ for (int i = 0; i < onPresentPaths->size(); i++){ std::cout << onPresentPaths->at(i) << std::endl; @@ -366,6 +358,7 @@ int main(int, char**) { ImGui::Begin("File Picker in 4K"); + static bool isInvalidPath = false; static bool isListVolumesAdequate = true; static bool isListDirectoriesAdequate = true; @@ -406,48 +399,29 @@ int main(int, char**) numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); //std::cout << numFiles; if (numFiles < 0) { - //ImGui::Text("Path not valid"); - log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO"); - goto filepickerFailure; + log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); + isInvalidPath = true; } - - //displayContents = std::move(directoryContents); - //directoryContents.clear(); } //It's renderin' time + static bool showHidden = false; ImGui::Text("Select a file:"); - if (debug) ImGui::Text("%s TEMPADDRBAR", currentPath); - //Y a que los VOLUMENES pinta2 pa100pre - //if (debug) std::cout << std::to_string(onPresentPaths.size()) + "<- numvols" << std::endl; - if (numVolumes > 0) { - for (int i = 0; i < onPresentPaths.size(); i++) { - if (i > 0) ImGui::SameLine(); - // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; - // ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); - if(ImGui::Button(onPresentPaths.at(i))){ - moveDirectory(&onPresentPaths.at(i)); - isListDirectoriesAdequate = true; - } - ImGui::PopStyleColor(3); - // ImGui::PopID(); - } - } - - - //Permanentes e increiblemente utilitarios botones en Cuatro K + //Permanentes e increiblemente utilitarios botones en Cuatro K: MOVE UP //c pervirtio con unicode std::wstring s(L"←→↑↓"); static char moveUp[] = ".."; static char* moveUpPtr = &moveUp[0]; - if(ImGui::Button("Move Up")){ + if(ImGui::Button("Move Up")) { moveDirectory(&moveUpPtr); + isInvalidPath = false; isListDirectoriesAdequate = true; } + ImGui::SameLine(); + //BARRA DE DIRECCIONES + ImGui::Text("%s TEMPADDRBAR", currentPath); + //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ @@ -466,8 +440,6 @@ int main(int, char**) if (numVolumes > 0) { for (int i = 0; i < onPresentPaths.size(); i++) { ImGui::TableNextColumn(); - //if (i > 0) ImGui::SameLine(); - // if (debug) std::cout << std::to_string(i) + "<- WE IN" << std::endl; // ImGui::PushID(i); ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); @@ -499,6 +471,8 @@ int main(int, char**) int idx = 0; for (; idx < numFiles; idx++) { + if(directoryContents.at(idx)->isHidden && !showHidden) continue; + ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -525,26 +499,27 @@ int main(int, char**) //ImGui::SetItemDefaultFocus(); } - if (!idx) { ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Folder is empty"); } + //DISPLAYING ALTERNATIVE INFO + if (!idx) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); + if (isInvalidPath) ImGui::Text("Failed to access: Path not valid"); + else ImGui::Text("Folder is empty"); + } ImGui::EndTable(); } - ImGui::EndTable(); - } - - - - - //ImGui::TreePop(); - + + ImGui::Checkbox("Show hidden", &showHidden); + ImGui::SameLine(); + ImGui::Button("Select"); filepickerFailure: - ImGui::Button("AAAAAAAAAAAAAAAAAAAAAAAAAQ" ); + ImGui::End(); } From 62b9dc15bf8e47e1b7f34ec19b49b9e3e20b9541 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Wed, 2 Nov 2022 19:18:19 +0100 Subject: [PATCH 10/21] comparar final string; futuro getfiletipe? --- main.cpp | 67 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/main.cpp b/main.cpp index 9e9e27e..df12cbe 100644 --- a/main.cpp +++ b/main.cpp @@ -63,6 +63,28 @@ typedef struct { //TODO UNICORDEO /* FILE PICKER MOMENTO */ +/* PROPOSITO GENERAL */ + +bool compareLastChar(std::vector *lastCharCandidates, char* string){ + bool matchingExtension = false; + for (int i = 0; i < lastCharCandidates->size(); i++){ + log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); + + if(strlen(string) < lastCharCandidates->at(i).length()) continue; + char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; + + log_debugcpp(potentialExtension); + log_debugcpp(" LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); + //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) + if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { + log_debugcpp("VALID EXTENSION"); + matchingExtension = true; + } + } + return matchingExtension; +} + + /* DIRECTORIES */ @@ -77,11 +99,10 @@ bool moveDirectory(char** currentPath){ return false; } -int listDirectory(std::string path, std::vector *directoryContents, std::vector *desiredExtensions = nullptr){ +int listDirectory(std::string path, std::vector *directoryContents){ HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA ffd; LARGE_INTEGER filesize; - bool skipDueToExtension = false; int numFiles = 0; if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return -1; @@ -91,23 +112,6 @@ int listDirectory(std::string path, std::vector *directoryCont do { if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); - //Codigo pa saltarse arxibus k no estiendan - /*if(desiredExtensions != nullptr){ - TCHAR* fileExtension = strrchr(ffd.cFileName, '.'); - if (debug && fileExtension != NULL) { std::cout << fileExtension; std::cout << " <- FILTERING EXTENSION" << std::endl; } - std::cout << ffd.cFileName << std::endl; - - //TODO tabien no??? - if (fileExtension == NULL) continue; - - for (int i = 0; i < desiredExtensions->size(); i++) { - if (debug) { std::cout << std::to_string(strcmp(desiredExtensions->at(i).c_str(), fileExtension)) << std::endl; } - if (strcmp(desiredExtensions->at(i).c_str(), fileExtension)){ - skipDueToExtension = true; - } - } - } - if (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && skipDueToExtension) continue;*/ directoriesInfo* itemInfo; if (directoryContents->size() <= numFiles){ @@ -116,7 +120,7 @@ int listDirectory(std::string path, std::vector *directoryCont } else { itemInfo = directoryContents->at(numFiles); } - log_debugcpp("MEMORIA ASIGNADA iteracion " + std::to_string(numFiles)); + log_debugcpp("MEMORY ASSIGNES iteration " + std::to_string(numFiles)); //A registrar info, fiera itemInfo->isFile = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; @@ -168,11 +172,10 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, } itemPath[pathLengthIdx + 1] = '\0'; dest->push_back(itemPath); + //TODO concat de puto C + log_debugcpp(dest->at(dest->size() - 1)); + log_debugcpp( " EXPLODEPATH"); - if (debug) { - std::cout << dest->at(dest->size() - 1); - std::cout << " EXPLODEPATH" << std::endl; - } if (nextOcurrence[0] == nextOcurrence[1]) return depth; depth++; @@ -202,7 +205,7 @@ int listVolumes(std::vector *onPresentPaths){ if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } - if (debug) std::cout << volumeName; + log_debugcpp(volumeName); //pathSchecker // if (debug && volumePaths[0] == 'E') { @@ -395,8 +398,8 @@ int main(int, char**) } // //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE - //static std::vector restrictToExe{"exe", "DSDFD"}; - numFiles = (listDirectory(std::string(currentPath), &directoryContents));//, &restrictToExe)); + + numFiles = listDirectory(std::string(currentPath), &directoryContents); //std::cout << numFiles; if (numFiles < 0) { log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); @@ -406,6 +409,8 @@ int main(int, char**) //It's renderin' time static bool showHidden = false; + static bool filterByExtension = false; + static std::vector extensions; ImGui::Text("Select a file:"); //Permanentes e increiblemente utilitarios botones en Cuatro K: MOVE UP @@ -472,6 +477,12 @@ int main(int, char**) int idx = 0; for (; idx < numFiles; idx++) { if(directoryContents.at(idx)->isHidden && !showHidden) continue; + if(filterByExtension && !directoryContents.at(idx)->isFile) { + //TODO XAPUSITA + extensions.clear(); + extensions.push_back("exe"); + if (!compareLastChar(&extensions, directoryContents.at(idx)->name)) continue; + } ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -516,6 +527,8 @@ int main(int, char**) ImGui::Checkbox("Show hidden", &showHidden); ImGui::SameLine(); + ImGui::Checkbox("Filter by .exe", &filterByExtension); + ImGui::SameLine(); ImGui::Button("Select"); filepickerFailure: From 6c83b9036254b1f4a61d59e99ee3cea49dc72706 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Thu, 3 Nov 2022 01:36:14 +0100 Subject: [PATCH 11/21] casi addrbar lista, ai bug --- main.cpp | 112 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/main.cpp b/main.cpp index df12cbe..97ee972 100644 --- a/main.cpp +++ b/main.cpp @@ -16,6 +16,7 @@ #define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) +#define MAX_ERRORSTR_LEN 512 #include "imgui/misc/single_file/imgui_single_file.h" #include @@ -63,27 +64,6 @@ typedef struct { //TODO UNICORDEO /* FILE PICKER MOMENTO */ -/* PROPOSITO GENERAL */ - -bool compareLastChar(std::vector *lastCharCandidates, char* string){ - bool matchingExtension = false; - for (int i = 0; i < lastCharCandidates->size(); i++){ - log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); - - if(strlen(string) < lastCharCandidates->at(i).length()) continue; - char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; - - log_debugcpp(potentialExtension); - log_debugcpp(" LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); - //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) - if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { - log_debugcpp("VALID EXTENSION"); - matchingExtension = true; - } - } - return matchingExtension; -} - /* DIRECTORIES */ @@ -239,7 +219,37 @@ int listVolumes(std::vector *onPresentPaths){ log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); FindVolumeClose(hFind); return numVolumes; -} +} + + +/* DISPLAY */ + + +bool compareLastChar(std::vector *lastCharCandidates, char* string){ + bool matchingExtension = false; + for (int i = 0; i < lastCharCandidates->size(); i++){ + log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); + + if(strlen(string) < lastCharCandidates->at(i).length()) continue; + char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; + + log_debugcpp(potentialExtension); + log_debugcpp(" LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); + //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) + if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { + log_debugcpp("VALID EXTENSION"); + matchingExtension = true; + } + } + return matchingExtension; +} + +bool inputMove(bool* error, char** str){ + *error = false; + if(!moveDirectory(str)) { *error = true; return false; } + return true; +} + /* FIN FILE PICKER MOMENTO */ @@ -361,9 +371,10 @@ int main(int, char**) { ImGui::Begin("File Picker in 4K"); - static bool isInvalidPath = false; + static bool showError = false; static bool isListVolumesAdequate = true; static bool isListDirectoriesAdequate = true; + static char error[MAX_ERRORSTR_LEN]; //Listar UNA VEZ volumenes static int numVolumes; @@ -381,13 +392,15 @@ int main(int, char**) //cursed C momento: necesario ptr en otra var para mandar a char** de firma static char currentPath[MAX_PATH]; static char* currentPathPtr = ¤tPath[0]; - + static char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; + static char* addrBarValPtr = &addrBarVal[0]; + //static int currentItemIdx = -1; //static std::vector directoryContents; static std::vector directoryContents; static int numFiles = 0; - ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); + //ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); if(isListDirectoriesAdequate) { log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); @@ -403,7 +416,13 @@ int main(int, char**) //std::cout << numFiles; if (numFiles < 0) { log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); - isInvalidPath = true; + showError = true; + strcpy(error, "Failed to open folder: permission denied?"); + strcpy(currentPath, addrBarVal); + numFiles = listDirectory(std::string(currentPath), &directoryContents); + } else { + showError = false; + strcpy(addrBarVal, currentPath); } } @@ -411,6 +430,11 @@ int main(int, char**) static bool showHidden = false; static bool filterByExtension = false; static std::vector extensions; + + if(showError){ + ImGui::Text(error); + } + ImGui::Text("Select a file:"); //Permanentes e increiblemente utilitarios botones en Cuatro K: MOVE UP @@ -418,14 +442,31 @@ int main(int, char**) static char moveUp[] = ".."; static char* moveUpPtr = &moveUp[0]; if(ImGui::Button("Move Up")) { - moveDirectory(&moveUpPtr); - isInvalidPath = false; - isListDirectoriesAdequate = true; + isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); + strcpy(error, "Failed to move up: reached volume root?"); } ImGui::SameLine(); //BARRA DE DIRECCIONES - ImGui::Text("%s TEMPADDRBAR", currentPath); + if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ + log_debugcpp(currentPath); + log_debugcpp(" ADDRBAR INTENTO"); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); + else strcpy(addrBarVal, currentPath); + strcpy(error, "Failed to open folder: invalid path"); + }; + ImGui::SameLine(); + if(ImGui::Button("Enter")){ + log_debugcpp(currentPath); + log_debugcpp(" ADDRBAR INTENTO"); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); + else strcpy(addrBarVal, currentPath); + strcpy(error, "Failed to open folder: invalid path"); + } + + //ImGui::Text("%s TEMPADDRBAR", currentPath); //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; @@ -450,8 +491,7 @@ int main(int, char**) ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); if(ImGui::Button(onPresentPaths.at(i))){ - moveDirectory(&onPresentPaths.at(i)); - isListDirectoriesAdequate = true; + isListDirectoriesAdequate = inputMove(&showError, &onPresentPaths.at(i)); } ImGui::PopStyleColor(3); if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); @@ -493,9 +533,9 @@ int main(int, char**) //currentItemIdx = i; strcat(currentPath, "\\"); strcat(currentPath, directoryContents.at(idx)->name); - moveDirectory(¤tPathPtr); + isListDirectoriesAdequate = inputMove(&showError, ¤tPathPtr); + strcpy(error, "Failed to open folder: invalid option"); //currentItemIdx = -1; - isListDirectoriesAdequate = true; } ImGui::TableNextColumn(); @@ -512,9 +552,7 @@ int main(int, char**) } //DISPLAYING ALTERNATIVE INFO if (!idx) { - ImGui::TableNextRow(); ImGui::TableNextColumn(); - if (isInvalidPath) ImGui::Text("Failed to access: Path not valid"); - else ImGui::Text("Folder is empty"); + ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Folder is empty"); } ImGui::EndTable(); } From 4d598832c1194c60a9b27fc706de77b223a6abeb Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Thu, 3 Nov 2022 17:19:57 +0100 Subject: [PATCH 12/21] rip bug, mjr errstr hndl: string? adminright msg? --- main.cpp | 67 +++++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/main.cpp b/main.cpp index 97ee972..8d94996 100644 --- a/main.cpp +++ b/main.cpp @@ -8,7 +8,7 @@ #define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) #define log_debugcpp(str) do { \ - if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << (str) << std::endl; \ + if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ } while (0) #define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) #define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) @@ -61,6 +61,20 @@ typedef struct { } directoriesInfo; +struct ErrorMessages{ + const std::string addrBarError = "Failed to open folder: invalid path"; + const std::string tableElementError = "Failed to open folder: access restricted to non-admin users"; + const std::string listDirectoryError = "Failed to open folder: access denied"; + const std::string moveUpError = "Failed to move up: reached volume root?"; +}; + +// struct ErrorMessages{ + // static constexpr char addrBarError[] = "Failed to open folder: invalid path"; + // static constexpr char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; + // static constexpr char listDirectoryError[] = "Failed to open folder: access denied"; + // static constexpr char moveUpError[] = "Failed to move up: reached volume root?"; +// }; + //TODO UNICORDEO /* FILE PICKER MOMENTO */ @@ -153,9 +167,7 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, itemPath[pathLengthIdx + 1] = '\0'; dest->push_back(itemPath); //TODO concat de puto C - log_debugcpp(dest->at(dest->size() - 1)); - log_debugcpp( " EXPLODEPATH"); - + log_debugcpp(dest->at(dest->size() - 1) << " EXPLODEPATH"); if (nextOcurrence[0] == nextOcurrence[1]) return depth; depth++; @@ -177,10 +189,9 @@ int listVolumes(std::vector *onPresentPaths){ TCHAR volumePaths[volumePathsBufferSize]; std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); - hFind = FindFirstVolume(volumeName, volumeNameSize); //if (debug) std::cout << volumeNameSize << std::endl; + hFind = FindFirstVolume(volumeName, volumeNameSize); if (INVALID_HANDLE_VALUE == hFind) return -2; do { - //if (debug) std::cout << volumeName << std::endl; if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ @@ -233,8 +244,7 @@ bool compareLastChar(std::vector *lastCharCandidates, char* string) if(strlen(string) < lastCharCandidates->at(i).length()) continue; char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; - log_debugcpp(potentialExtension); - log_debugcpp(" LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); + log_debugcpp(potentialExtension << " LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { log_debugcpp("VALID EXTENSION"); @@ -250,6 +260,12 @@ bool inputMove(bool* error, char** str){ return true; } +void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, char* addrBarVal, char* errorDest, const char* errorContent){ + if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); + else strcpy(addrBarVal, currentPath); + strcpy(errorDest, errorContent); +} + /* FIN FILE PICKER MOMENTO */ @@ -372,9 +388,10 @@ int main(int, char**) ImGui::Begin("File Picker in 4K"); static bool showError = false; + static char error[MAX_ERRORSTR_LEN]; + static ErrorMessages eMsg; static bool isListVolumesAdequate = true; static bool isListDirectoriesAdequate = true; - static char error[MAX_ERRORSTR_LEN]; //Listar UNA VEZ volumenes static int numVolumes; @@ -417,7 +434,7 @@ int main(int, char**) if (numFiles < 0) { log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); showError = true; - strcpy(error, "Failed to open folder: permission denied?"); + strcpy(error, eMsg.listDirectoryError.c_str()); strcpy(currentPath, addrBarVal); numFiles = listDirectory(std::string(currentPath), &directoryContents); } else { @@ -443,31 +460,26 @@ int main(int, char**) static char* moveUpPtr = &moveUp[0]; if(ImGui::Button("Move Up")) { isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); - strcpy(error, "Failed to move up: reached volume root?"); + strcpy(error, eMsg.moveUpError.c_str()); } ImGui::SameLine(); //BARRA DE DIRECCIONES + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ - log_debugcpp(currentPath); - log_debugcpp(" ADDRBAR INTENTO"); + log_debugcpp(currentPath <<" ADDRBAR INTENTO"); isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); - else strcpy(addrBarVal, currentPath); - strcpy(error, "Failed to open folder: invalid path"); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.addrBarError.c_str()); }; + ImGui::PopItemWidth(); ImGui::SameLine(); if(ImGui::Button("Enter")){ - log_debugcpp(currentPath); - log_debugcpp(" ADDRBAR INTENTO"); + log_debugcpp(currentPath <<" ADDRBAR INTENTO"); isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); - else strcpy(addrBarVal, currentPath); - strcpy(error, "Failed to open folder: invalid path"); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.addrBarError.c_str()); } - //ImGui::Text("%s TEMPADDRBAR", currentPath); - + log_debugcpp(currentPath <<" LEMISIIMIA"); //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ @@ -530,11 +542,10 @@ int main(int, char**) //NAME bool isSelected = false; if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ - //currentItemIdx = i; - strcat(currentPath, "\\"); - strcat(currentPath, directoryContents.at(idx)->name); - isListDirectoriesAdequate = inputMove(&showError, ¤tPathPtr); - strcpy(error, "Failed to open folder: invalid option"); + strcat(addrBarVal, "\\"); + strcat(addrBarVal, directoryContents.at(idx)->name); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.tableElementError.c_str()); //currentItemIdx = -1; } ImGui::TableNextColumn(); From c8739ce010337590187fdf3431559d743d12598d Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sun, 6 Nov 2022 22:58:45 +0100 Subject: [PATCH 13/21] impl historial y adminrigh error --- main.cpp | 194 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 30 deletions(-) diff --git a/main.cpp b/main.cpp index 8d94996..5f6314b 100644 --- a/main.cpp +++ b/main.cpp @@ -6,6 +6,7 @@ #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION +//DEBUG MACRO #define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) #define log_debugcpp(str) do { \ if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ @@ -14,10 +15,14 @@ #define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) #define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) - +//PATH MACRO #define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) #define MAX_ERRORSTR_LEN 512 +//HISTORY HANDLING MACRO +#define MIN_HISTORY_POS history->historyTraversalPos <= 0 +#define MAX_HISTORY_POS history->historyTraversalPos >= history->historyDepth + #include "imgui/misc/single_file/imgui_single_file.h" #include #include @@ -61,20 +66,49 @@ typedef struct { } directoriesInfo; -struct ErrorMessages{ - const std::string addrBarError = "Failed to open folder: invalid path"; - const std::string tableElementError = "Failed to open folder: access restricted to non-admin users"; - const std::string listDirectoryError = "Failed to open folder: access denied"; - const std::string moveUpError = "Failed to move up: reached volume root?"; +struct History{ + std::vector previousPaths; + //TODO C++ david momento + //int historyDepth = -1; + int historyDepth; + int historyTraversalPos; + bool isAdditionTime; + + History(){ + historyTraversalPos = historyDepth = -1; + isAdditionTime = true; + } +}; + +enum HistoryMovement { + HISTORY_FORWARD = 1, + HISTORY_BACKWARD = 0 +}; + +enum ListDirectoryError { + DIRECTORY_ERROR_PATH_TOO_LONG = -1, + DIRECTORY_ERROR_ACCESSING_CONTENT = -2 +}; + +enum agnosticDirError { + AGDIR_ERROR_ACCESS_DENIED = 1 }; // struct ErrorMessages{ - // static constexpr char addrBarError[] = "Failed to open folder: invalid path"; - // static constexpr char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; - // static constexpr char listDirectoryError[] = "Failed to open folder: access denied"; - // static constexpr char moveUpError[] = "Failed to move up: reached volume root?"; + // const std::string addrBarError = "Failed to open folder: invalid path"; + // const std::string tableElementError = "Failed to open folder: access restricted to non-admin users"; + // const std::string listDirectoryError = "Failed to open folder: access denied"; + // const std::string moveUpError = "Failed to move up: reached volume root?"; // }; +namespace ErrorMessages { + char addrBarError[] = "Failed to open folder: invalid path"; + char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; + char accessDeniedError[] = "Failed to open folder: access denied"; + char listDirectoryError[] = "Failed to open folder: unknown error"; + char moveUpError[] = "Failed to move up: reached volume root?"; +}; + //TODO UNICORDEO /* FILE PICKER MOMENTO */ @@ -99,10 +133,10 @@ int listDirectory(std::string path, std::vector *directoryCont LARGE_INTEGER filesize; int numFiles = 0; - if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return -1; + if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return DIRECTORY_ERROR_PATH_TOO_LONG; path = path + "\\*"; hFind = FindFirstFile(path.c_str(), &ffd); - if (INVALID_HANDLE_VALUE == hFind) return -2; + if (INVALID_HANDLE_VALUE == hFind) return DIRECTORY_ERROR_ACCESSING_CONTENT; do { if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); @@ -141,6 +175,18 @@ int listDirectory(std::string path, std::vector *directoryCont return numFiles; } +long getLastDirectorySystemError(){ + long winLastError = GetLastError(); + switch(winLastError){ + case ERROR_ACCESS_DENIED: + return AGDIR_ERROR_ACCESS_DENIED; + break; + default: + return -1; + break; + } +} + /* VOLUMES */ //new char*[CharCount * sizeof(WCHAR) @@ -166,7 +212,7 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, } itemPath[pathLengthIdx + 1] = '\0'; dest->push_back(itemPath); - //TODO concat de puto C + log_debugcpp(dest->at(dest->size() - 1) << " EXPLODEPATH"); if (nextOcurrence[0] == nextOcurrence[1]) return depth; @@ -198,7 +244,7 @@ int listVolumes(std::vector *onPresentPaths){ if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } log_debugcpp(volumeName); - //pathSchecker + //DEBUG: pathSchecker // if (debug && volumePaths[0] == 'E') { // std::cout << volumePathLength << std::endl; // /*exit(1);*/ @@ -260,18 +306,54 @@ bool inputMove(bool* error, char** str){ return true; } -void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, char* addrBarVal, char* errorDest, const char* errorContent){ - if (isListDirectoriesAdequate) strcpy(currentPath, addrBarVal); +void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, \ + char* addrBarVal, char* errorDest, const char* errorContent, History* history){ + if (isListDirectoriesAdequate) { strcpy(currentPath, addrBarVal); history->isAdditionTime = true; } else strcpy(addrBarVal, currentPath); strcpy(errorDest, errorContent); } +/* HISTORIAL */ + +void addPathToHistory(History *history, char* pathToAdd){ + char* path; + int ajjj = history->previousPaths.size(); + if (history->historyTraversalPos + 1 >= ajjj){ + path = (char*)calloc(1, MAX_PATH); + history->previousPaths.push_back(path); + } else { + path = history->previousPaths.at(history->historyTraversalPos + 1); + } + strncpy(path, pathToAdd, MAX_PATH); + history->historyTraversalPos++; + history->historyDepth = history->historyTraversalPos; + history->isAdditionTime = false; +} + +char** moveThroughHistory(History *history, HistoryMovement movement){ + switch(movement){ + case HISTORY_BACKWARD: + if(MIN_HISTORY_POS) + return &history->previousPaths.at(history->historyTraversalPos); + history->historyTraversalPos--; + return &history->previousPaths.at(history->historyTraversalPos); + break; + case HISTORY_FORWARD: + if(MAX_HISTORY_POS) + return &history->previousPaths.at(history->historyDepth); + history->historyTraversalPos++; + return &history->previousPaths.at(history->historyTraversalPos); + break; + default: + return &history->previousPaths.at(history->historyDepth); + } +} + /* FIN FILE PICKER MOMENTO */ -int main(int, char**) -{ +int main(int, char**) { // Setup window glfwSetErrorCallback(glfw_error_callback); if (!glfwInit()) @@ -387,9 +469,10 @@ int main(int, char**) { ImGui::Begin("File Picker in 4K"); + //TODO is dis bector in hipp? + static History* history = new History(); static bool showError = false; static char error[MAX_ERRORSTR_LEN]; - static ErrorMessages eMsg; static bool isListVolumesAdequate = true; static bool isListDirectoriesAdequate = true; @@ -432,18 +515,46 @@ int main(int, char**) numFiles = listDirectory(std::string(currentPath), &directoryContents); //std::cout << numFiles; if (numFiles < 0) { + //TODO quitar GetLastError() de aqui + long directoryErrorCode; log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); + if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastDirectorySystemError(); showError = true; - strcpy(error, eMsg.listDirectoryError.c_str()); - strcpy(currentPath, addrBarVal); - numFiles = listDirectory(std::string(currentPath), &directoryContents); + switch(directoryErrorCode) { + case AGDIR_ERROR_ACCESS_DENIED: + strcpy(error, ErrorMessages::accessDeniedError); + strcpy(addrBarVal, history->previousPaths.at(history->historyTraversalPos)); + strcpy(currentPath, history->previousPaths.at(history->historyTraversalPos)); + numFiles = listDirectory(history->previousPaths.at(history->historyTraversalPos), &directoryContents); + break; + default: + strcpy(error, ErrorMessages::listDirectoryError); + strcpy(currentPath, addrBarVal); + numFiles = listDirectory(std::string(currentPath), &directoryContents); + history->isAdditionTime = false; + break; + } + history->isAdditionTime = false; } else { showError = false; strcpy(addrBarVal, currentPath); + if(history->isAdditionTime) addPathToHistory(history, addrBarVal); } } - //It's renderin' time + /* + + + + + + It's renderin' time + + + + + + */ static bool showHidden = false; static bool filterByExtension = false; static std::vector extensions; @@ -451,35 +562,57 @@ int main(int, char**) if(showError){ ImGui::Text(error); } - + ImGui::Text("Select a file:"); - //Permanentes e increiblemente utilitarios botones en Cuatro K: MOVE UP + //Permanentes e increiblemente utilitarios botones en Cuatro K: BACK + ImGui::BeginDisabled(MIN_HISTORY_POS); + if(ImGui::Button("Back")) { + char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); + isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); + //TODO: ERROR + strcpy(error, ErrorMessages::moveUpError); + } + ImGui::EndDisabled(); + //FORWARD + ImGui::SameLine(); + ImGui::BeginDisabled(MAX_HISTORY_POS); + if(ImGui::Button("Forward")) { + char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); + isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); + strcpy(error, ErrorMessages::moveUpError); + } + ImGui::EndDisabled(); + + //MOVE UP + ImGui::SameLine(); //c pervirtio con unicode std::wstring s(L"←→↑↓"); static char moveUp[] = ".."; static char* moveUpPtr = &moveUp[0]; if(ImGui::Button("Move Up")) { isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); - strcpy(error, eMsg.moveUpError.c_str()); + strcpy(error, ErrorMessages::moveUpError); + history->isAdditionTime = true; } ImGui::SameLine(); //BARRA DE DIRECCIONES + //TODO a point ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.addrBarError.c_str()); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); }; ImGui::PopItemWidth(); ImGui::SameLine(); if(ImGui::Button("Enter")){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.addrBarError.c_str()); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); } //ImGui::Text("%s TEMPADDRBAR", currentPath); - log_debugcpp(currentPath <<" LEMISIIMIA"); + //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ @@ -504,6 +637,7 @@ int main(int, char**) ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); if(ImGui::Button(onPresentPaths.at(i))){ isListDirectoriesAdequate = inputMove(&showError, &onPresentPaths.at(i)); + history->isAdditionTime = true; } ImGui::PopStyleColor(3); if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); @@ -545,7 +679,7 @@ int main(int, char**) strcat(addrBarVal, "\\"); strcat(addrBarVal, directoryContents.at(idx)->name); isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, eMsg.tableElementError.c_str()); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::tableElementError, history); //currentItemIdx = -1; } ImGui::TableNextColumn(); From ab036165598f6c12a2bb6805b5f84bb2285f3133 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Wed, 9 Nov 2022 18:29:31 +0100 Subject: [PATCH 14/21] arregos y qol menores --- main.cpp | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/main.cpp b/main.cpp index 5f6314b..5f92786 100644 --- a/main.cpp +++ b/main.cpp @@ -20,8 +20,8 @@ #define MAX_ERRORSTR_LEN 512 //HISTORY HANDLING MACRO -#define MIN_HISTORY_POS history->historyTraversalPos <= 0 -#define MAX_HISTORY_POS history->historyTraversalPos >= history->historyDepth +#define MIN_HISTORY_POS (history->historyTraversalPos <= 0) +#define MAX_HISTORY_POS (history->historyTraversalPos >= history->historyDepth) #include "imgui/misc/single_file/imgui_single_file.h" #include @@ -66,10 +66,13 @@ typedef struct { } directoriesInfo; + struct History{ std::vector previousPaths; - //TODO C++ david momento + //warnings e 1 cosa //int historyDepth = -1; + //TODO Limitar historial + //const int maxHistoryDepth = 10000; int historyDepth; int historyTraversalPos; bool isAdditionTime; @@ -94,13 +97,6 @@ enum agnosticDirError { AGDIR_ERROR_ACCESS_DENIED = 1 }; -// struct ErrorMessages{ - // const std::string addrBarError = "Failed to open folder: invalid path"; - // const std::string tableElementError = "Failed to open folder: access restricted to non-admin users"; - // const std::string listDirectoryError = "Failed to open folder: access denied"; - // const std::string moveUpError = "Failed to move up: reached volume root?"; -// }; - namespace ErrorMessages { char addrBarError[] = "Failed to open folder: invalid path"; char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; @@ -270,7 +266,7 @@ int listVolumes(std::vector *onPresentPaths){ }else{ log_debugcpp("no volumes found wtf"); } - + //TODO benchimarqui std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); @@ -317,8 +313,8 @@ void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, void addPathToHistory(History *history, char* pathToAdd){ char* path; - int ajjj = history->previousPaths.size(); - if (history->historyTraversalPos + 1 >= ajjj){ + //int ajjj = history->previousPaths.size(); + if (history->historyTraversalPos + 1 >= history->previousPaths.size()){ path = (char*)calloc(1, MAX_PATH); history->previousPaths.push_back(path); } else { @@ -330,23 +326,22 @@ void addPathToHistory(History *history, char* pathToAdd){ history->isAdditionTime = false; } -char** moveThroughHistory(History *history, HistoryMovement movement){ +char** moveThroughHistory(History *history, HistoryMovement movement) { + int sign = (movement == HISTORY_BACKWARD ? -1 : 1); switch(movement){ case HISTORY_BACKWARD: if(MIN_HISTORY_POS) return &history->previousPaths.at(history->historyTraversalPos); - history->historyTraversalPos--; - return &history->previousPaths.at(history->historyTraversalPos); break; case HISTORY_FORWARD: if(MAX_HISTORY_POS) return &history->previousPaths.at(history->historyDepth); - history->historyTraversalPos++; - return &history->previousPaths.at(history->historyTraversalPos); break; default: return &history->previousPaths.at(history->historyDepth); } + history->historyTraversalPos += sign; + return &history->previousPaths.at(history->historyTraversalPos); } @@ -469,7 +464,6 @@ int main(int, char**) { { ImGui::Begin("File Picker in 4K"); - //TODO is dis bector in hipp? static History* history = new History(); static bool showError = false; static char error[MAX_ERRORSTR_LEN]; @@ -487,7 +481,6 @@ int main(int, char**) { log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); } - //Listar UNA VEZ directorios //cursed C momento: necesario ptr en otra var para mandar a char** de firma static char currentPath[MAX_PATH]; @@ -531,7 +524,6 @@ int main(int, char**) { strcpy(error, ErrorMessages::listDirectoryError); strcpy(currentPath, addrBarVal); numFiles = listDirectory(std::string(currentPath), &directoryContents); - history->isAdditionTime = false; break; } history->isAdditionTime = false; @@ -597,7 +589,6 @@ int main(int, char**) { ImGui::SameLine(); //BARRA DE DIRECCIONES - //TODO a point ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); From ff86a38cd2c53c9c2f84f31f0c93ac36bdeb67a9 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Wed, 9 Nov 2022 23:06:55 +0100 Subject: [PATCH 15/21] primer paso hacia la lib filepicker --- filepicker.hpp | 598 +++++++++++++++++++++++++++++++++++++++++++++++ main.cpp | 614 +------------------------------------------------ 2 files changed, 600 insertions(+), 612 deletions(-) create mode 100644 filepicker.hpp diff --git a/filepicker.hpp b/filepicker.hpp new file mode 100644 index 0000000..5fb5f00 --- /dev/null +++ b/filepicker.hpp @@ -0,0 +1,598 @@ +//DEBUG MACRO +#define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) +#define log_debugcpp(str) do { \ + if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ + } while (0) +#define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) +#define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) +#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) + +//PATH MACRO +#define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) +#define MAX_ERRORSTR_LEN 512 + +//HISTORY HANDLING MACRO +#define MIN_HISTORY_POS (history->historyTraversalPos <= 0) +#define MAX_HISTORY_POS (history->historyTraversalPos >= history->historyDepth) + +#include +#include +#include +#include +#include + +#pragma once + + + +namespace fp { + + + + +bool debug = true; + +// int debugVerbosity = 0; +// enum enumDebugVerbosity { + // DEBUG_DIRECTORY = (1<<0), + // DEBUG_VOLUME = (1<<1), + // DEBUG_EXTENSION = (1<<2) +// }; +// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; +// debugVerbosity &= ~DEBUG_DIRECTORY; +// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); + +int windowFlags = 0; + +typedef struct { + long long size; + //TODO yel spacsio NELLA MEMoria + TCHAR name[MAX_LISTDIR_PATH_LENGTH]; + SYSTEMTIME createTime; + SYSTEMTIME lastAccessTime; + SYSTEMTIME lastWriteTime; + bool isFile; + bool isHidden; + +} directoriesInfo; + + +struct History{ + std::vector previousPaths; + //warnings e 1 cosa + //int historyDepth = -1; + //TODO Limitar historial + //const int maxHistoryDepth = 10000; + int historyDepth; + int historyTraversalPos; + bool isAdditionTime; + + History(){ + historyTraversalPos = historyDepth = -1; + isAdditionTime = true; + } +}; + +enum HistoryMovement { + HISTORY_FORWARD = 1, + HISTORY_BACKWARD = 0 +}; + +enum ListDirectoryError { + DIRECTORY_ERROR_PATH_TOO_LONG = -1, + DIRECTORY_ERROR_ACCESSING_CONTENT = -2 +}; + +enum agnosticDirError { + AGDIR_ERROR_ACCESS_DENIED = 1 +}; + +namespace ErrorMessages { + char addrBarError[] = "Failed to open folder: invalid path"; + char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; + char accessDeniedError[] = "Failed to open folder: access denied"; + char listDirectoryError[] = "Failed to open folder: unknown error"; + char moveUpError[] = "Failed to move up: reached volume root?"; +}; + +//TODO UNICORDEO +/* FILE PICKER MOMENTO */ + + +/* DIRECTORIES */ + + +bool retrieveCurrentDirectory(char** currentPath){ + if(GetCurrentDirectory(MAX_PATH, *currentPath)) return true; + return false; +} + +bool moveDirectory(char** currentPath){ + log_debugcpp(*currentPath); + if(SetCurrentDirectory(*currentPath)) return true; + return false; +} + +int listDirectory(std::string path, std::vector *directoryContents){ + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA ffd; + LARGE_INTEGER filesize; + int numFiles = 0; + + if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return DIRECTORY_ERROR_PATH_TOO_LONG; + path = path + "\\*"; + hFind = FindFirstFile(path.c_str(), &ffd); + if (INVALID_HANDLE_VALUE == hFind) return DIRECTORY_ERROR_ACCESSING_CONTENT; + do { + if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; + log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); + + directoriesInfo* itemInfo; + if (directoryContents->size() <= numFiles){ + itemInfo = new directoriesInfo(); + directoryContents->push_back(itemInfo); + } else { + itemInfo = directoryContents->at(numFiles); + } + log_debugcpp("MEMORY ASSIGNES iteration " + std::to_string(numFiles)); + + //A registrar info, fiera + itemInfo->isFile = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; + itemInfo->isHidden = (ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? true : false; + //Tamaño + filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); + itemInfo->size = filesize.QuadPart; + //Fechitssss + FileTimeToSystemTime(&ffd.ftCreationTime, &itemInfo->createTime); + FileTimeToSystemTime(&ffd.ftLastAccessTime, &itemInfo->lastAccessTime); + FileTimeToSystemTime(&ffd.ftLastWriteTime, &itemInfo->lastWriteTime); + //Nombre de la cosita + uint64_t idx = 0; + do { + itemInfo->name[idx] = ffd.cFileName[idx]; + idx++; + } while (ffd.cFileName[idx]); + itemInfo->name[idx] = '\0'; + log_debugcpp("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); + + numFiles++; + } while (FindNextFile(hFind, &ffd) != 0); + FindClose(hFind); + return numFiles; +} + +long getLastDirectorySystemError(){ + long winLastError = GetLastError(); + switch(winLastError){ + case ERROR_ACCESS_DENIED: + return AGDIR_ERROR_ACCESS_DENIED; + break; + default: + return -1; + break; + } +} + +/* VOLUMES */ + +//new char*[CharCount * sizeof(WCHAR) +/*idea, si quisiera que empezase en punto X del string + coger el param opcional y crear un puntero a la posicion X del array para empezar desde ahi + int explodePaths(TCHAR* volumePaths, int volumePathLength, int volumePathBufferSize, + char separator, std::vector *dest, , int startingFrom = 0); + if (nextOcurrence == NULL) return 0; +*/ +int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 1){ + //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. + //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew + + char* nextOcurrence = strchr(volumePaths, separator); + + char* itemPath; + itemPath = (char*)calloc(1, volumePathsBufferSize); + + int pathLengthIdx = 0; + for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { + if (debug) { std::cout << volumePaths[0]; std::cout << " ENTERS " + std::to_string(pathLengthIdx) << std::endl; } + itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; + } + itemPath[pathLengthIdx + 1] = '\0'; + dest->push_back(itemPath); + + log_debugcpp(dest->at(dest->size() - 1) << " EXPLODEPATH"); + + if (nextOcurrence[0] == nextOcurrence[1]) return depth; + depth++; + return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest, depth); +} + + +int listVolumes(std::vector *onPresentPaths){ + HANDLE hFind = INVALID_HANDLE_VALUE; + const char separator = '\0'; + + int numVolumes = 0; + //Volume name + TCHAR volumeName[MAX_PATH]; + int volumeNameSize = sizeof volumeName / sizeof volumeName[0]; + //Paths + int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); + unsigned long volumePathLength; + TCHAR volumePaths[volumePathsBufferSize]; + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); + + hFind = FindFirstVolume(volumeName, volumeNameSize); + if (INVALID_HANDLE_VALUE == hFind) return -2; + do { + + if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ + + if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } + log_debugcpp(volumeName); + + //DEBUG: pathSchecker + // if (debug && volumePaths[0] == 'E') { + // std::cout << volumePathLength << std::endl; + // /*exit(1);*/ + // for (int i = 0; i < volumePathsBufferSize; i++){ + // std::cout << std::to_string(i) + ": "; + // std::cout << volumePaths[i]; + // std::cout << " | "; + // } + // std::cout << std::endl << "I IA" << std::endl; + // exit(1); + // } + + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ + for (int i = 0; i < onPresentPaths->size(); i++){ + std::cout << onPresentPaths->at(i) << std::endl; + } + } + //std::cout << volumePaths << std::endl; + + log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); + log_debugcpp("THE END"); + + }else{ + log_debugcpp("no volumes found wtf"); + } + //TODO benchimarqui + std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); + } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); + log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); + FindVolumeClose(hFind); + return numVolumes; +} + + +/* DISPLAY */ + + +bool compareLastChar(std::vector *lastCharCandidates, char* string){ + bool matchingExtension = false; + for (int i = 0; i < lastCharCandidates->size(); i++){ + log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); + + if(strlen(string) < lastCharCandidates->at(i).length()) continue; + char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; + + log_debugcpp(potentialExtension << " LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); + //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) + if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { + log_debugcpp("VALID EXTENSION"); + matchingExtension = true; + } + } + return matchingExtension; +} + +bool inputMove(bool* error, char** str){ + *error = false; + if(!moveDirectory(str)) { *error = true; return false; } + return true; +} + +void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, \ + char* addrBarVal, char* errorDest, const char* errorContent, History* history){ + if (isListDirectoriesAdequate) { strcpy(currentPath, addrBarVal); history->isAdditionTime = true; } + else strcpy(addrBarVal, currentPath); + strcpy(errorDest, errorContent); +} + +/* HISTORIAL */ + +void addPathToHistory(History *history, char* pathToAdd){ + char* path; + //int ajjj = history->previousPaths.size(); + if (history->historyTraversalPos + 1 >= history->previousPaths.size()){ + path = (char*)calloc(1, MAX_PATH); + history->previousPaths.push_back(path); + } else { + path = history->previousPaths.at(history->historyTraversalPos + 1); + } + strncpy(path, pathToAdd, MAX_PATH); + history->historyTraversalPos++; + history->historyDepth = history->historyTraversalPos; + history->isAdditionTime = false; +} + +char** moveThroughHistory(History *history, HistoryMovement movement) { + int sign = (movement == HISTORY_BACKWARD ? -1 : 1); + switch(movement){ + case HISTORY_BACKWARD: + if(MIN_HISTORY_POS) + return &history->previousPaths.at(history->historyTraversalPos); + break; + case HISTORY_FORWARD: + if(MAX_HISTORY_POS) + return &history->previousPaths.at(history->historyDepth); + break; + default: + return &history->previousPaths.at(history->historyDepth); + } + history->historyTraversalPos += sign; + return &history->previousPaths.at(history->historyTraversalPos); +} + + +/* FIN FILE PICKER MOMENTO */ + + +void filePickerMomento(bool isFullViewport = false){ + if (isFullViewport){ + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); + ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize; + } + + ImGui::Begin("File Picker in 4K", NULL, windowFlags); + if (isFullViewport) ImGui::PopStyleVar(1); + + static History* history = new History(); + static bool showError = false; + static char error[MAX_ERRORSTR_LEN]; + static bool isListVolumesAdequate = true; + static bool isListDirectoriesAdequate = true; + + //Listar UNA VEZ volumenes + static int numVolumes; + static std::vector onPresentPaths; + + if (isListVolumesAdequate) { + isListVolumesAdequate = false; + //std::cout << isListVolumesAdequate << std::endl; + numVolumes = listVolumes(&onPresentPaths); + log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); + } + + //Listar UNA VEZ directorios + //cursed C momento: necesario ptr en otra var para mandar a char** de firma + static char currentPath[MAX_PATH]; + static char* currentPathPtr = ¤tPath[0]; + static char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; + static char* addrBarValPtr = &addrBarVal[0]; + + //static int currentItemIdx = -1; + //static std::vector directoryContents; + static std::vector directoryContents; + static int numFiles = 0; + + //ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); + + if(isListDirectoriesAdequate) { + log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); + isListDirectoriesAdequate = false; + if(!retrieveCurrentDirectory(¤tPathPtr)) { + log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); + goto filepickerFailure; + } + // + //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE + + numFiles = listDirectory(std::string(currentPath), &directoryContents); + //std::cout << numFiles; + if (numFiles < 0) { + //TODO quitar GetLastError() de aqui + long directoryErrorCode; + log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); + if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastDirectorySystemError(); + showError = true; + switch(directoryErrorCode) { + case AGDIR_ERROR_ACCESS_DENIED: + strcpy(error, ErrorMessages::accessDeniedError); + strcpy(addrBarVal, history->previousPaths.at(history->historyTraversalPos)); + strcpy(currentPath, history->previousPaths.at(history->historyTraversalPos)); + numFiles = listDirectory(history->previousPaths.at(history->historyTraversalPos), &directoryContents); + break; + default: + strcpy(error, ErrorMessages::listDirectoryError); + strcpy(currentPath, addrBarVal); + numFiles = listDirectory(std::string(currentPath), &directoryContents); + break; + } + history->isAdditionTime = false; + } else { + showError = false; + strcpy(addrBarVal, currentPath); + if(history->isAdditionTime) addPathToHistory(history, addrBarVal); + } + } + + /* + + + + + + It's renderin' time + + + + + + */ + static bool showHidden = false; + static bool filterByExtension = false; + static std::vector extensions; + + if(showError){ + ImGui::Text(error); + } + + ImGui::Text("Select a file:"); + + //Permanentes e increiblemente utilitarios botones en Cuatro K: BACK + ImGui::BeginDisabled(MIN_HISTORY_POS); + if(ImGui::Button("Back")) { + char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); + isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); + //TODO: ERROR + strcpy(error, ErrorMessages::moveUpError); + } + ImGui::EndDisabled(); + //FORWARD + ImGui::SameLine(); + ImGui::BeginDisabled(MAX_HISTORY_POS); + if(ImGui::Button("Forward")) { + char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); + isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); + strcpy(error, ErrorMessages::moveUpError); + } + ImGui::EndDisabled(); + + //MOVE UP + ImGui::SameLine(); + //c pervirtio con unicode std::wstring s(L"←→↑↓"); + static char moveUp[] = ".."; + static char* moveUpPtr = &moveUp[0]; + if(ImGui::Button("Move Up")) { + isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); + strcpy(error, ErrorMessages::moveUpError); + history->isAdditionTime = true; + } + + ImGui::SameLine(); + //BARRA DE DIRECCIONES + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); + if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ + log_debugcpp(currentPath <<" ADDRBAR INTENTO"); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); + }; + ImGui::PopItemWidth(); + ImGui::SameLine(); + if(ImGui::Button("Enter")){ + log_debugcpp(currentPath <<" ADDRBAR INTENTO"); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); + } + //ImGui::Text("%s TEMPADDRBAR", currentPath); + + //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS + static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; + if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + //VOLUMENES + static ImGuiTableFlags volumesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY ; + if (ImGui::BeginTable("##volumesTable", 1, volumesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableSetupColumn("Volumes", ImGuiTableColumnFlags_NoHide); + //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); + ImGui::TableHeadersRow(); + ImGui::TableNextRow(); + + if (numVolumes > 0) { + for (int i = 0; i < onPresentPaths.size(); i++) { + ImGui::TableNextColumn(); + // ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); + if(ImGui::Button(onPresentPaths.at(i))){ + isListDirectoriesAdequate = inputMove(&showError, &onPresentPaths.at(i)); + history->isAdditionTime = true; + } + ImGui::PopStyleColor(3); + if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); + // ImGui::PopID(); + } + } + ImGui::EndTable(); + } + + ImGui::TableNextColumn(); + + + //DIRECTORIOS ENCONTRADOS bien pinta2 + static ImGuiTableFlags directoriesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |ImGuiTableFlags_ScrollY ; + if (ImGui::BeginTable("##directoriesTable", 3, directoriesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ + ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoHide); + ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_NoHide); + //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); + //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); + ImGui::TableHeadersRow(); + + int idx = 0; + for (; idx < numFiles; idx++) { + if(directoryContents.at(idx)->isHidden && !showHidden) continue; + if(filterByExtension && !directoryContents.at(idx)->isFile) { + //TODO XAPUSITA + extensions.clear(); + extensions.push_back("exe"); + if (!compareLastChar(&extensions, directoryContents.at(idx)->name)) continue; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + //NAME + bool isSelected = false; + if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ + strcat(addrBarVal, "\\"); + strcat(addrBarVal, directoryContents.at(idx)->name); + isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); + handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::tableElementError, history); + //currentItemIdx = -1; + } + ImGui::TableNextColumn(); + + //TYPE + if (directoryContents.at(idx)->isFile) ImGui::Text("Folder"); else ImGui::Text("File"); + ImGui::TableNextColumn(); + + //SIZE + if (!directoryContents.at(idx)->isFile) ImGui::Text("%lld bytes", directoryContents.at(idx)->size); + else ImGui::Text(""); + + //ImGui::SetItemDefaultFocus(); + + } + //DISPLAYING ALTERNATIVE INFO + if (!idx) { + ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Folder is empty"); + } + ImGui::EndTable(); + } + ImGui::EndTable(); + } + + + + //ImGui::TreePop(); + + ImGui::Checkbox("Show hidden", &showHidden); + ImGui::SameLine(); + ImGui::Checkbox("Filter by .exe", &filterByExtension); + ImGui::SameLine(); + ImGui::Button("Select"); + filepickerFailure: + + + ImGui::End(); +} + +} \ No newline at end of file diff --git a/main.cpp b/main.cpp index 5f92786..0a40c4e 100644 --- a/main.cpp +++ b/main.cpp @@ -6,29 +6,8 @@ #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION -//DEBUG MACRO -#define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) -#define log_debugcpp(str) do { \ - if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ - } while (0) -#define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) -#define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) -#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) - -//PATH MACRO -#define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) -#define MAX_ERRORSTR_LEN 512 - -//HISTORY HANDLING MACRO -#define MIN_HISTORY_POS (history->historyTraversalPos <= 0) -#define MAX_HISTORY_POS (history->historyTraversalPos >= history->historyDepth) - #include "imgui/misc/single_file/imgui_single_file.h" -#include -#include -#include -#include -#include +#include "filepicker.hpp" // [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers. // To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma. @@ -41,312 +20,6 @@ static void glfw_error_callback(int error, const char* description) fprintf(stderr, "Glfw Error %d: %s\n", error, description); } -bool debug = true; - -// int debugVerbosity = 0; -// enum enumDebugVerbosity { - // DEBUG_DIRECTORY = (1<<0), - // DEBUG_VOLUME = (1<<1), - // DEBUG_EXTENSION = (1<<2) -// }; -// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; -// debugVerbosity &= ~DEBUG_DIRECTORY; -// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); - - -typedef struct { - long long size; - //TODO yel spacsio NELLA MEMoria - TCHAR name[MAX_LISTDIR_PATH_LENGTH]; - SYSTEMTIME createTime; - SYSTEMTIME lastAccessTime; - SYSTEMTIME lastWriteTime; - bool isFile; - bool isHidden; - -} directoriesInfo; - - -struct History{ - std::vector previousPaths; - //warnings e 1 cosa - //int historyDepth = -1; - //TODO Limitar historial - //const int maxHistoryDepth = 10000; - int historyDepth; - int historyTraversalPos; - bool isAdditionTime; - - History(){ - historyTraversalPos = historyDepth = -1; - isAdditionTime = true; - } -}; - -enum HistoryMovement { - HISTORY_FORWARD = 1, - HISTORY_BACKWARD = 0 -}; - -enum ListDirectoryError { - DIRECTORY_ERROR_PATH_TOO_LONG = -1, - DIRECTORY_ERROR_ACCESSING_CONTENT = -2 -}; - -enum agnosticDirError { - AGDIR_ERROR_ACCESS_DENIED = 1 -}; - -namespace ErrorMessages { - char addrBarError[] = "Failed to open folder: invalid path"; - char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; - char accessDeniedError[] = "Failed to open folder: access denied"; - char listDirectoryError[] = "Failed to open folder: unknown error"; - char moveUpError[] = "Failed to move up: reached volume root?"; -}; - -//TODO UNICORDEO -/* FILE PICKER MOMENTO */ - - -/* DIRECTORIES */ - - -bool retrieveCurrentDirectory(char** currentPath){ - if(GetCurrentDirectory(MAX_PATH, *currentPath)) return true; - return false; -} - -bool moveDirectory(char** currentPath){ - log_debugcpp(*currentPath); - if(SetCurrentDirectory(*currentPath)) return true; - return false; -} - -int listDirectory(std::string path, std::vector *directoryContents){ - HANDLE hFind = INVALID_HANDLE_VALUE; - WIN32_FIND_DATA ffd; - LARGE_INTEGER filesize; - int numFiles = 0; - - if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return DIRECTORY_ERROR_PATH_TOO_LONG; - path = path + "\\*"; - hFind = FindFirstFile(path.c_str(), &ffd); - if (INVALID_HANDLE_VALUE == hFind) return DIRECTORY_ERROR_ACCESSING_CONTENT; - do { - if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; - log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); - - directoriesInfo* itemInfo; - if (directoryContents->size() <= numFiles){ - itemInfo = new directoriesInfo(); - directoryContents->push_back(itemInfo); - } else { - itemInfo = directoryContents->at(numFiles); - } - log_debugcpp("MEMORY ASSIGNES iteration " + std::to_string(numFiles)); - - //A registrar info, fiera - itemInfo->isFile = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; - itemInfo->isHidden = (ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? true : false; - //Tamaño - filesize.QuadPart = ((ffd.nFileSizeHigh * (MAXDWORD+1)) + ffd.nFileSizeLow); - itemInfo->size = filesize.QuadPart; - //Fechitssss - FileTimeToSystemTime(&ffd.ftCreationTime, &itemInfo->createTime); - FileTimeToSystemTime(&ffd.ftLastAccessTime, &itemInfo->lastAccessTime); - FileTimeToSystemTime(&ffd.ftLastWriteTime, &itemInfo->lastWriteTime); - //Nombre de la cosita - uint64_t idx = 0; - do { - itemInfo->name[idx] = ffd.cFileName[idx]; - idx++; - } while (ffd.cFileName[idx]); - itemInfo->name[idx] = '\0'; - log_debugcpp("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); - - numFiles++; - } while (FindNextFile(hFind, &ffd) != 0); - FindClose(hFind); - return numFiles; -} - -long getLastDirectorySystemError(){ - long winLastError = GetLastError(); - switch(winLastError){ - case ERROR_ACCESS_DENIED: - return AGDIR_ERROR_ACCESS_DENIED; - break; - default: - return -1; - break; - } -} - -/* VOLUMES */ - -//new char*[CharCount * sizeof(WCHAR) -/*idea, si quisiera que empezase en punto X del string - coger el param opcional y crear un puntero a la posicion X del array para empezar desde ahi - int explodePaths(TCHAR* volumePaths, int volumePathLength, int volumePathBufferSize, - char separator, std::vector *dest, , int startingFrom = 0); - if (nextOcurrence == NULL) return 0; -*/ -int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 1){ - //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. - //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew - - char* nextOcurrence = strchr(volumePaths, separator); - - char* itemPath; - itemPath = (char*)calloc(1, volumePathsBufferSize); - - int pathLengthIdx = 0; - for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { - if (debug) { std::cout << volumePaths[0]; std::cout << " ENTERS " + std::to_string(pathLengthIdx) << std::endl; } - itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; - } - itemPath[pathLengthIdx + 1] = '\0'; - dest->push_back(itemPath); - - log_debugcpp(dest->at(dest->size() - 1) << " EXPLODEPATH"); - - if (nextOcurrence[0] == nextOcurrence[1]) return depth; - depth++; - return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest, depth); -} - - -int listVolumes(std::vector *onPresentPaths){ - HANDLE hFind = INVALID_HANDLE_VALUE; - const char separator = '\0'; - - int numVolumes = 0; - //Volume name - TCHAR volumeName[MAX_PATH]; - int volumeNameSize = sizeof volumeName / sizeof volumeName[0]; - //Paths - int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); - unsigned long volumePathLength; - TCHAR volumePaths[volumePathsBufferSize]; - std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); - - hFind = FindFirstVolume(volumeName, volumeNameSize); - if (INVALID_HANDLE_VALUE == hFind) return -2; - do { - - if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ - - if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } - log_debugcpp(volumeName); - - //DEBUG: pathSchecker - // if (debug && volumePaths[0] == 'E') { - // std::cout << volumePathLength << std::endl; - // /*exit(1);*/ - // for (int i = 0; i < volumePathsBufferSize; i++){ - // std::cout << std::to_string(i) + ": "; - // std::cout << volumePaths[i]; - // std::cout << " | "; - // } - // std::cout << std::endl << "I IA" << std::endl; - // exit(1); - // } - - if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ - for (int i = 0; i < onPresentPaths->size(); i++){ - std::cout << onPresentPaths->at(i) << std::endl; - } - } - //std::cout << volumePaths << std::endl; - - log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); - log_debugcpp("THE END"); - - }else{ - log_debugcpp("no volumes found wtf"); - } - //TODO benchimarqui - std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); - } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); - log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); - FindVolumeClose(hFind); - return numVolumes; -} - - -/* DISPLAY */ - - -bool compareLastChar(std::vector *lastCharCandidates, char* string){ - bool matchingExtension = false; - for (int i = 0; i < lastCharCandidates->size(); i++){ - log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); - - if(strlen(string) < lastCharCandidates->at(i).length()) continue; - char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; - - log_debugcpp(potentialExtension << " LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); - //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) - if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { - log_debugcpp("VALID EXTENSION"); - matchingExtension = true; - } - } - return matchingExtension; -} - -bool inputMove(bool* error, char** str){ - *error = false; - if(!moveDirectory(str)) { *error = true; return false; } - return true; -} - -void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, \ - char* addrBarVal, char* errorDest, const char* errorContent, History* history){ - if (isListDirectoriesAdequate) { strcpy(currentPath, addrBarVal); history->isAdditionTime = true; } - else strcpy(addrBarVal, currentPath); - strcpy(errorDest, errorContent); -} - -/* HISTORIAL */ - -void addPathToHistory(History *history, char* pathToAdd){ - char* path; - //int ajjj = history->previousPaths.size(); - if (history->historyTraversalPos + 1 >= history->previousPaths.size()){ - path = (char*)calloc(1, MAX_PATH); - history->previousPaths.push_back(path); - } else { - path = history->previousPaths.at(history->historyTraversalPos + 1); - } - strncpy(path, pathToAdd, MAX_PATH); - history->historyTraversalPos++; - history->historyDepth = history->historyTraversalPos; - history->isAdditionTime = false; -} - -char** moveThroughHistory(History *history, HistoryMovement movement) { - int sign = (movement == HISTORY_BACKWARD ? -1 : 1); - switch(movement){ - case HISTORY_BACKWARD: - if(MIN_HISTORY_POS) - return &history->previousPaths.at(history->historyTraversalPos); - break; - case HISTORY_FORWARD: - if(MAX_HISTORY_POS) - return &history->previousPaths.at(history->historyDepth); - break; - default: - return &history->previousPaths.at(history->historyDepth); - } - history->historyTraversalPos += sign; - return &history->previousPaths.at(history->historyTraversalPos); -} - - -/* FIN FILE PICKER MOMENTO */ - int main(int, char**) { // Setup window @@ -417,7 +90,6 @@ int main(int, char**) { // Our state bool show_demo_window = true; - bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // Main loop @@ -437,291 +109,9 @@ int main(int, char**) { // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); - - // 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window. - { - static float f = 0.0f; - static int counter = 0; - - ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. - - ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too) - ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state - ImGui::Checkbox("Another Window", &show_another_window); - - ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f - ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color - - if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated) - counter++; - ImGui::SameLine(); - ImGui::Text("counter = %d", counter); - - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::End(); - } - { - ImGui::Begin("File Picker in 4K"); - - static History* history = new History(); - static bool showError = false; - static char error[MAX_ERRORSTR_LEN]; - static bool isListVolumesAdequate = true; - static bool isListDirectoriesAdequate = true; - - //Listar UNA VEZ volumenes - static int numVolumes; - static std::vector onPresentPaths; - - if (isListVolumesAdequate) { - isListVolumesAdequate = false; - //std::cout << isListVolumesAdequate << std::endl; - numVolumes = listVolumes(&onPresentPaths); - log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); - } - - //Listar UNA VEZ directorios - //cursed C momento: necesario ptr en otra var para mandar a char** de firma - static char currentPath[MAX_PATH]; - static char* currentPathPtr = ¤tPath[0]; - static char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; - static char* addrBarValPtr = &addrBarVal[0]; - - //static int currentItemIdx = -1; - //static std::vector directoryContents; - static std::vector directoryContents; - static int numFiles = 0; - - //ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); - - if(isListDirectoriesAdequate) { - log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); - isListDirectoriesAdequate = false; - if(!retrieveCurrentDirectory(¤tPathPtr)) { - log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); - goto filepickerFailure; - } - // - //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE - - numFiles = listDirectory(std::string(currentPath), &directoryContents); - //std::cout << numFiles; - if (numFiles < 0) { - //TODO quitar GetLastError() de aqui - long directoryErrorCode; - log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); - if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastDirectorySystemError(); - showError = true; - switch(directoryErrorCode) { - case AGDIR_ERROR_ACCESS_DENIED: - strcpy(error, ErrorMessages::accessDeniedError); - strcpy(addrBarVal, history->previousPaths.at(history->historyTraversalPos)); - strcpy(currentPath, history->previousPaths.at(history->historyTraversalPos)); - numFiles = listDirectory(history->previousPaths.at(history->historyTraversalPos), &directoryContents); - break; - default: - strcpy(error, ErrorMessages::listDirectoryError); - strcpy(currentPath, addrBarVal); - numFiles = listDirectory(std::string(currentPath), &directoryContents); - break; - } - history->isAdditionTime = false; - } else { - showError = false; - strcpy(addrBarVal, currentPath); - if(history->isAdditionTime) addPathToHistory(history, addrBarVal); - } - } - - /* - - - - - - It's renderin' time - - - - - - */ - static bool showHidden = false; - static bool filterByExtension = false; - static std::vector extensions; - - if(showError){ - ImGui::Text(error); - } - - ImGui::Text("Select a file:"); - - //Permanentes e increiblemente utilitarios botones en Cuatro K: BACK - ImGui::BeginDisabled(MIN_HISTORY_POS); - if(ImGui::Button("Back")) { - char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); - isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); - //TODO: ERROR - strcpy(error, ErrorMessages::moveUpError); - } - ImGui::EndDisabled(); - //FORWARD - ImGui::SameLine(); - ImGui::BeginDisabled(MAX_HISTORY_POS); - if(ImGui::Button("Forward")) { - char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); - isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); - strcpy(error, ErrorMessages::moveUpError); - } - ImGui::EndDisabled(); - - //MOVE UP - ImGui::SameLine(); - //c pervirtio con unicode std::wstring s(L"←→↑↓"); - static char moveUp[] = ".."; - static char* moveUpPtr = &moveUp[0]; - if(ImGui::Button("Move Up")) { - isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); - strcpy(error, ErrorMessages::moveUpError); - history->isAdditionTime = true; - } - - ImGui::SameLine(); - //BARRA DE DIRECCIONES - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); - if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ - log_debugcpp(currentPath <<" ADDRBAR INTENTO"); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); - }; - ImGui::PopItemWidth(); - ImGui::SameLine(); - if(ImGui::Button("Enter")){ - log_debugcpp(currentPath <<" ADDRBAR INTENTO"); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); - } - //ImGui::Text("%s TEMPADDRBAR", currentPath); - - //LA GRAN TABLACIÓN: ACTOR EN LAS SOMBRAS - static ImGuiTableFlags splitterTableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV; - if (ImGui::BeginTable("##splitterTable", 2, splitterTableFlags, ImVec2(-FLT_MIN, 0.8f * ImGui::GetTextLineHeightWithSpacing()))){ - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - //VOLUMENES - static ImGuiTableFlags volumesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY ; - if (ImGui::BeginTable("##volumesTable", 1, volumesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ - ImGui::TableSetupColumn("Volumes", ImGuiTableColumnFlags_NoHide); - //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); - ImGui::TableHeadersRow(); - ImGui::TableNextRow(); - - if (numVolumes > 0) { - for (int i = 0; i < onPresentPaths.size(); i++) { - ImGui::TableNextColumn(); - // ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); - if(ImGui::Button(onPresentPaths.at(i))){ - isListDirectoriesAdequate = inputMove(&showError, &onPresentPaths.at(i)); - history->isAdditionTime = true; - } - ImGui::PopStyleColor(3); - if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); - // ImGui::PopID(); - } - } - ImGui::EndTable(); - } - - ImGui::TableNextColumn(); - - - //DIRECTORIOS ENCONTRADOS bien pinta2 - static ImGuiTableFlags directoriesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |ImGuiTableFlags_ScrollY ; - if (ImGui::BeginTable("##directoriesTable", 3, directoriesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ - ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_NoHide); - ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_NoHide); - //ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 12.0f); - //ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, TEXT_BASE_WIDTH * 18.0f); - ImGui::TableHeadersRow(); - - int idx = 0; - for (; idx < numFiles; idx++) { - if(directoryContents.at(idx)->isHidden && !showHidden) continue; - if(filterByExtension && !directoryContents.at(idx)->isFile) { - //TODO XAPUSITA - extensions.clear(); - extensions.push_back("exe"); - if (!compareLastChar(&extensions, directoryContents.at(idx)->name)) continue; - } - - ImGui::TableNextRow(); - ImGui::TableNextColumn(); - - //NAME - bool isSelected = false; - if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ - strcat(addrBarVal, "\\"); - strcat(addrBarVal, directoryContents.at(idx)->name); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::tableElementError, history); - //currentItemIdx = -1; - } - ImGui::TableNextColumn(); - - //TYPE - if (directoryContents.at(idx)->isFile) ImGui::Text("Folder"); else ImGui::Text("File"); - ImGui::TableNextColumn(); - - //SIZE - if (!directoryContents.at(idx)->isFile) ImGui::Text("%lld bytes", directoryContents.at(idx)->size); - else ImGui::Text(""); - - //ImGui::SetItemDefaultFocus(); - - } - //DISPLAYING ALTERNATIVE INFO - if (!idx) { - ImGui::TableNextRow(); ImGui::TableNextColumn(); ImGui::Text("Folder is empty"); - } - ImGui::EndTable(); - } - ImGui::EndTable(); - } - - - - //ImGui::TreePop(); + fp::filePickerMomento(true); - ImGui::Checkbox("Show hidden", &showHidden); - ImGui::SameLine(); - ImGui::Checkbox("Filter by .exe", &filterByExtension); - ImGui::SameLine(); - ImGui::Button("Select"); - filepickerFailure: - - - ImGui::End(); - - } - - - // 3. Show another simple window. - if (show_another_window) - { - ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) - ImGui::Text("Hello from another window!"); - if (ImGui::Button("Close Me")) - show_another_window = false; - ImGui::End(); - } - // Rendering ImGui::Render(); int display_w, display_h; From 44a4a088b3d43c4adfa23bc9e5b1a60b6c88c4a6 Mon Sep 17 00:00:00 2001 From: OugonNoHane Date: Sun, 13 Nov 2022 13:30:53 +0100 Subject: [PATCH 16/21] pwetty big refactor: isAdeq>LIST_DIR, char**>* --- filepicker.hpp | 252 ++++++++++++++++++++++++++++++------------------- 1 file changed, 153 insertions(+), 99 deletions(-) diff --git a/filepicker.hpp b/filepicker.hpp index 5fb5f00..45e5353 100644 --- a/filepicker.hpp +++ b/filepicker.hpp @@ -27,23 +27,6 @@ namespace fp { - - - -bool debug = true; - -// int debugVerbosity = 0; -// enum enumDebugVerbosity { - // DEBUG_DIRECTORY = (1<<0), - // DEBUG_VOLUME = (1<<1), - // DEBUG_EXTENSION = (1<<2) -// }; -// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; -// debugVerbosity &= ~DEBUG_DIRECTORY; -// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); - -int windowFlags = 0; - typedef struct { long long size; //TODO yel spacsio NELLA MEMoria @@ -95,6 +78,71 @@ namespace ErrorMessages { char moveUpError[] = "Failed to move up: reached volume root?"; }; +enum listFlags { + LIST_DIRECTORY = (1<<0), + LIST_VOLUME = (1<<1) +}; + +//VARS + +bool debug = true; + +History* history = new History(); + +//Flags used to determine: +// window format +int windowFlags = 0; +// if it's time to load new data +int listFlags = LIST_DIRECTORY | LIST_VOLUME; + +//Array used to display current error +char error[MAX_ERRORSTR_LEN]; +bool showError = false; + +//Stores found volumes +//TODO onPresentPaths???? wtf is dis naem bru +std::vector onPresentPaths; +int numVolumes; + +//currentPath is the one used to retrieve data, then copied to addrBalVar for display. +//addrBalVar is also used as a bacukp in case new currentPath is invalid +//TODO: hay historial. Coalescer en una sola? +//TODO: david wtf +//cursed C momento: necesario ptr en otra var para mandar a char** de firma +char currentPath[MAX_PATH]; +char* currentPathPtr = ¤tPath[0]; +char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; +char* addrBarValPtr = &addrBarVal[0]; + +//Stores entires found within a given directory +//static int currentItemIdx = -1; +std::vector directoryContents; +int numFiles = 0; + +//Going one level up needs to be hardcoded like this due to function inputMove needing a char** +//c pervirtio con unicode std::wstring s(L"←→↑↓"); +char moveUp[] = ".."; +char* moveUpPtr = &moveUp[0]; + +bool isListVolumesAdequate = true; +bool isListDirectoriesAdequate = true; + +//RenderTime vars for filtering +bool showHidden = false; +bool filterByExtension = false; +std::vector extensions; + + +// int debugVerbosity = 0; +// enum enumDebugVerbosity { + // DEBUG_DIRECTORY = (1<<0), + // DEBUG_VOLUME = (1<<1), + // DEBUG_EXTENSION = (1<<2) +// }; +// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; +// debugVerbosity &= ~DEBUG_DIRECTORY; +// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); + //TODO UNICORDEO /* FILE PICKER MOMENTO */ @@ -102,14 +150,14 @@ namespace ErrorMessages { /* DIRECTORIES */ -bool retrieveCurrentDirectory(char** currentPath){ - if(GetCurrentDirectory(MAX_PATH, *currentPath)) return true; +bool retrieveCurrentDirectory(char* currentPath){ + if(GetCurrentDirectory(MAX_PATH, currentPath)) return true; return false; } -bool moveDirectory(char** currentPath){ - log_debugcpp(*currentPath); - if(SetCurrentDirectory(*currentPath)) return true; +bool moveDirectory(char* currentPath){ + log_debugcpp(currentPath); + if(SetCurrentDirectory(currentPath)) return true; return false; } @@ -193,7 +241,7 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, int pathLengthIdx = 0; for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { - if (debug) { std::cout << volumePaths[0]; std::cout << " ENTERS " + std::to_string(pathLengthIdx) << std::endl; } + log_debugcpp(volumePaths[0] << " ENTERS " << std::to_string(pathLengthIdx)); itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; } itemPath[pathLengthIdx + 1] = '\0'; @@ -227,7 +275,10 @@ int listVolumes(std::vector *onPresentPaths){ if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ - if (volumePathLength == 1) { if (debug) std::cout << "Skill Issue" << std::endl; continue; } + if (volumePathLength == 1) { + log_debugcpp("Skill Issue"); + continue; + } log_debugcpp(volumeName); //DEBUG: pathSchecker @@ -242,18 +293,18 @@ int listVolumes(std::vector *onPresentPaths){ // std::cout << std::endl << "I IA" << std::endl; // exit(1); // } - - if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ - for (int i = 0; i < onPresentPaths->size(); i++){ - std::cout << onPresentPaths->at(i) << std::endl; + if (debug) { + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ + for (int i = 0; i < onPresentPaths->size(); i++){ + log_debugcpp(onPresentPaths->at(i)); + } } } //std::cout << volumePaths << std::endl; log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); log_debugcpp("THE END"); - - }else{ + } else { log_debugcpp("no volumes found wtf"); } //TODO benchimarqui @@ -286,24 +337,57 @@ bool compareLastChar(std::vector *lastCharCandidates, char* string) return matchingExtension; } -bool inputMove(bool* error, char** str){ +//TODO huh +// int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ + // const char* errorContent, History* history, char* currentPath = NULL, char* addrBarVal = NULL){ + // *error = false; + // if(!moveDirectory(nextPath)) { + // *error = true; + // if(currentPath && addrBarVal) { + // strcpy(errorDest, errorContent); + // strcpy(addrBarVal, currentPath); + // } + // return listFlags; + // } + // if(currentPath && addrBarVal) { + // strcpy(currentPath, addrBarVal); + // history->isAdditionTime = true; + // } + // return (listFlags |= LIST_DIRECTORY); +// } + + +//TODO huh, vale, no pasa por listdirectory por la flag, pero esto se podria hacer mejor manteniendo el no tirar de disco tb? +int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ + const char* errorContent, History* history, char* currentPath , char* addrBarVal){ *error = false; - if(!moveDirectory(str)) { *error = true; return false; } - return true; + if(!moveDirectory(nextPath)) { + *error = true; + strcpy(errorDest, errorContent); + strcpy(addrBarVal, currentPath); + return listFlags; + } + //strcpy(currentPath, addrBarVal); + history->isAdditionTime = true; + return (listFlags |= LIST_DIRECTORY); } -void handleFolderAccessResult(bool isListDirectoriesAdequate, char* currentPath, \ - char* addrBarVal, char* errorDest, const char* errorContent, History* history){ - if (isListDirectoriesAdequate) { strcpy(currentPath, addrBarVal); history->isAdditionTime = true; } - else strcpy(addrBarVal, currentPath); - strcpy(errorDest, errorContent); +int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ + const char* errorContent, History* history, bool isAdditionTime){ + *error = false; + if(!moveDirectory(nextPath)) { + *error = true; + strcpy(errorDest, errorContent); + return listFlags; + } + if (isAdditionTime) history->isAdditionTime = true; + return (listFlags |= LIST_DIRECTORY); } /* HISTORIAL */ void addPathToHistory(History *history, char* pathToAdd){ char* path; - //int ajjj = history->previousPaths.size(); if (history->historyTraversalPos + 1 >= history->previousPaths.size()){ path = (char*)calloc(1, MAX_PATH); history->previousPaths.push_back(path); @@ -316,22 +400,22 @@ void addPathToHistory(History *history, char* pathToAdd){ history->isAdditionTime = false; } -char** moveThroughHistory(History *history, HistoryMovement movement) { +char* moveThroughHistory(History *history, HistoryMovement movement) { int sign = (movement == HISTORY_BACKWARD ? -1 : 1); switch(movement){ case HISTORY_BACKWARD: if(MIN_HISTORY_POS) - return &history->previousPaths.at(history->historyTraversalPos); + return history->previousPaths.at(history->historyTraversalPos); break; case HISTORY_FORWARD: if(MAX_HISTORY_POS) - return &history->previousPaths.at(history->historyDepth); + return history->previousPaths.at(history->historyDepth); break; default: - return &history->previousPaths.at(history->historyDepth); + return history->previousPaths.at(history->historyDepth); } history->historyTraversalPos += sign; - return &history->previousPaths.at(history->historyTraversalPos); + return history->previousPaths.at(history->historyTraversalPos); } @@ -347,45 +431,23 @@ void filePickerMomento(bool isFullViewport = false){ } ImGui::Begin("File Picker in 4K", NULL, windowFlags); - if (isFullViewport) ImGui::PopStyleVar(1); - - static History* history = new History(); - static bool showError = false; - static char error[MAX_ERRORSTR_LEN]; - static bool isListVolumesAdequate = true; - static bool isListDirectoriesAdequate = true; + if (isFullViewport) ImGui::PopStyleVar(1); //Listar UNA VEZ volumenes - static int numVolumes; - static std::vector onPresentPaths; - - if (isListVolumesAdequate) { - isListVolumesAdequate = false; - //std::cout << isListVolumesAdequate << std::endl; + if (listFlags & LIST_VOLUME) { + listFlags &= ~LIST_VOLUME; numVolumes = listVolumes(&onPresentPaths); log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); } //Listar UNA VEZ directorios - //cursed C momento: necesario ptr en otra var para mandar a char** de firma - static char currentPath[MAX_PATH]; - static char* currentPathPtr = ¤tPath[0]; - static char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; - static char* addrBarValPtr = &addrBarVal[0]; - - //static int currentItemIdx = -1; - //static std::vector directoryContents; - static std::vector directoryContents; - static int numFiles = 0; - //ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); - - if(isListDirectoriesAdequate) { + if (listFlags & LIST_DIRECTORY) { + listFlags &= ~LIST_DIRECTORY; log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); - isListDirectoriesAdequate = false; - if(!retrieveCurrentDirectory(¤tPathPtr)) { + if(!retrieveCurrentDirectory(currentPath)) { log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); - goto filepickerFailure; + exit(EXIT_FAILURE); } // //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE @@ -432,9 +494,6 @@ void filePickerMomento(bool isFullViewport = false){ */ - static bool showHidden = false; - static bool filterByExtension = false; - static std::vector extensions; if(showError){ ImGui::Text(error); @@ -445,31 +504,28 @@ void filePickerMomento(bool isFullViewport = false){ //Permanentes e increiblemente utilitarios botones en Cuatro K: BACK ImGui::BeginDisabled(MIN_HISTORY_POS); if(ImGui::Button("Back")) { - char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); - isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); + char* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); //TODO: ERROR - strcpy(error, ErrorMessages::moveUpError); + listFlags = handleFolderAccessResult(listFlags, interfaceMovementButtonsPath, + &showError, error, ErrorMessages::moveUpError, history, false); } ImGui::EndDisabled(); //FORWARD ImGui::SameLine(); ImGui::BeginDisabled(MAX_HISTORY_POS); if(ImGui::Button("Forward")) { - char** interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); - isListDirectoriesAdequate = inputMove(&showError, interfaceMovementButtonsPath); - strcpy(error, ErrorMessages::moveUpError); + //TODO david???????????????????????????????????? + char* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); + listFlags = handleFolderAccessResult(listFlags, interfaceMovementButtonsPath, + &showError, error, ErrorMessages::moveUpError, history, false); } ImGui::EndDisabled(); //MOVE UP ImGui::SameLine(); - //c pervirtio con unicode std::wstring s(L"←→↑↓"); - static char moveUp[] = ".."; - static char* moveUpPtr = &moveUp[0]; if(ImGui::Button("Move Up")) { - isListDirectoriesAdequate = inputMove(&showError, (&moveUpPtr)); - strcpy(error, ErrorMessages::moveUpError); - history->isAdditionTime = true; + listFlags = handleFolderAccessResult(listFlags, moveUp, &showError, + error, ErrorMessages::moveUpError, history, true); } ImGui::SameLine(); @@ -477,15 +533,15 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); + listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, + history, currentPath, addrBarVal); }; ImGui::PopItemWidth(); ImGui::SameLine(); if(ImGui::Button("Enter")){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::addrBarError, history); + listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, + history, currentPath, addrBarVal); } //ImGui::Text("%s TEMPADDRBAR", currentPath); @@ -512,8 +568,9 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); if(ImGui::Button(onPresentPaths.at(i))){ - isListDirectoriesAdequate = inputMove(&showError, &onPresentPaths.at(i)); - history->isAdditionTime = true; + //TODO xdddddddd + listFlags = handleFolderAccessResult(listFlags, onPresentPaths.at(i), &showError, error, + ErrorMessages::moveUpError, history, true); } ImGui::PopStyleColor(3); if (i != onPresentPaths.size() - 1) ImGui::TableNextRow(); @@ -525,7 +582,6 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::TableNextColumn(); - //DIRECTORIOS ENCONTRADOS bien pinta2 static ImGuiTableFlags directoriesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |ImGuiTableFlags_ScrollY ; if (ImGui::BeginTable("##directoriesTable", 3, directoriesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ @@ -554,8 +610,8 @@ void filePickerMomento(bool isFullViewport = false){ if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ strcat(addrBarVal, "\\"); strcat(addrBarVal, directoryContents.at(idx)->name); - isListDirectoriesAdequate = inputMove(&showError, &addrBarValPtr); - handleFolderAccessResult(isListDirectoriesAdequate, currentPath, addrBarVal, error, ErrorMessages::tableElementError, history); + listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, + ErrorMessages::tableElementError, history, currentPath, addrBarVal); //currentItemIdx = -1; } ImGui::TableNextColumn(); @@ -589,8 +645,6 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::Checkbox("Filter by .exe", &filterByExtension); ImGui::SameLine(); ImGui::Button("Select"); - filepickerFailure: - ImGui::End(); } From 2fd70f55d5af84bcad449ad3ff74917f0474d087 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 11 Jan 2024 19:13:54 +0100 Subject: [PATCH 17/21] unicode support, finished base implementation --- filepicker.hpp | 399 ++++++++++++++++++++++++++++++++----------------- glfw3.dll | Bin 0 -> 234496 bytes main.cpp | 34 +++-- unityBuild.h | 25 ++++ 4 files changed, 307 insertions(+), 151 deletions(-) create mode 100644 glfw3.dll create mode 100644 unityBuild.h diff --git a/filepicker.hpp b/filepicker.hpp index 45e5353..2e5f8a4 100644 --- a/filepicker.hpp +++ b/filepicker.hpp @@ -1,11 +1,23 @@ //DEBUG MACRO +#ifdef DEBUG #define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) #define log_debugcpp(str) do { \ if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ - } while (0) + } while (0) +#define log_wdebugcpp(str) do { \ + if (debug) std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ + } while (0) #define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) #define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) -#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) +#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) +#else +#define log_debugc(str, ...) +#define log_debugcpp(str) +#define log_wdebugcpp(str) +#define log_directory(str) +#define log_volume(str) +#define log_extension(str) +#endif //PATH MACRO #define MAX_LISTDIR_PATH_LENGTH (MAX_PATH - 3) @@ -23,14 +35,13 @@ #pragma once - - namespace fp { typedef struct { long long size; //TODO yel spacsio NELLA MEMoria - TCHAR name[MAX_LISTDIR_PATH_LENGTH]; + WCHAR name[MAX_LISTDIR_PATH_LENGTH]; + char nameUTF8[MAX_LISTDIR_PATH_LENGTH * 4]; SYSTEMTIME createTime; SYSTEMTIME lastAccessTime; SYSTEMTIME lastWriteTime; @@ -40,16 +51,34 @@ typedef struct { } directoriesInfo; -struct History{ - std::vector previousPaths; +struct History { + std::vector previousPaths; + std::vector previousPathsUTF8; //warnings e 1 cosa //int historyDepth = -1; //TODO Limitar historial //const int maxHistoryDepth = 10000; + //int currentStart = 0; int historyDepth; int historyTraversalPos; bool isAdditionTime; + // wchar* operator[](int idx){ + // if (idx > previousPaths.size()) + // int newPos = idx + currentStart; + + // if (newPos < 0) return previousPaths[maxHistoryDepth - (newPos % maxHistoryDepth)]; + // else return previousPaths[newPos % maxHistoryDepth]; + + // } + + // void push_back(wchar* path){ + // if (previousPaths.size() < maxHistoryDepth) previousPaths.push_back(path); + // else { + // previousPaths[currentStart++] = path; + // } + // } + History(){ historyTraversalPos = historyDepth = -1; isAdditionTime = true; @@ -66,7 +95,7 @@ enum ListDirectoryError { DIRECTORY_ERROR_ACCESSING_CONTENT = -2 }; -enum agnosticDirError { +enum AgnosticDirError { AGDIR_ERROR_ACCESS_DENIED = 1 }; @@ -78,51 +107,81 @@ namespace ErrorMessages { char moveUpError[] = "Failed to move up: reached volume root?"; }; -enum listFlags { +namespace Label { + wchar_t saveFileLabel[] = L"Save file: "; + wchar_t loadFileLabel[] = L"Select a file: "; + wchar_t selectDirectoryLabel[] = L"Select a directory: "; +}; + +enum ListFlags { LIST_DIRECTORY = (1<<0), LIST_VOLUME = (1<<1) }; +enum WindowFlags { + FP_FULLSCREEN = (1<<0), + FP_MODAL = (1<<1), + FP_FILE_LOAD = (1<<2), + FP_FILE_SAVE = (1<<3), + FP_DIRECTORY_SELECT = (1<<4) +}; + +enum exitFlags { + EXIT_CONTINUE = (1<<0), + EXIT_SELECTED = (1<<1), + EXIT_CLOSED = (1<<2), + EXIT_ERROR = (1<<3) +}; + //VARS - +#ifdef DEBUG bool debug = true; +#else +bool debug = false; +#endif -History* history = new History(); - +// Window open bool +bool windowOpen = true; +bool windowConfigured = false; + //Flags used to determine: // window format -int windowFlags = 0; +//int windowFlags = 0; // if it's time to load new data int listFlags = LIST_DIRECTORY | LIST_VOLUME; //Array used to display current error char error[MAX_ERRORSTR_LEN]; +//wchar_t errorUTF8[MAX_ERRORSTR_LEN * 4]; bool showError = false; //Stores found volumes -//TODO onPresentPaths???? wtf is dis naem bru -std::vector onPresentPaths; +//TODO onPresentPaths might not be the best name +std::vector onPresentPaths; +std::vector onPresentPathsUTF8; int numVolumes; -//currentPath is the one used to retrieve data, then copied to addrBalVar for display. -//addrBalVar is also used as a bacukp in case new currentPath is invalid +//currentPath is the one used to retrieve data, then copied to addrBarVal for display. +//addrBarVal is also used as a bacukp in case new currentPath is invalid +//chosenFile stores absolute file path to return //TODO: hay historial. Coalescer en una sola? -//TODO: david wtf -//cursed C momento: necesario ptr en otra var para mandar a char** de firma -char currentPath[MAX_PATH]; -char* currentPathPtr = ¤tPath[0]; -char addrBarVal[MAX_LISTDIR_PATH_LENGTH]; -char* addrBarValPtr = &addrBarVal[0]; - +//cursed C momento: necesario ptr en otra var para mandar a wchar_t** de firma +wchar_t currentPath[MAX_PATH]; +//wchar_t* currentPathPtr = ¤tPath[0]; +wchar_t addrBarVal[MAX_LISTDIR_PATH_LENGTH]; +char addrBarValUTF8[MAX_LISTDIR_PATH_LENGTH * 4]; +//wchar_t* addrBarValPtr = &addrBarVal[0]; +wchar_t chosenPath[MAX_PATH]; +char chosenPathUTF8[MAX_PATH * 4]; + //Stores entires found within a given directory //static int currentItemIdx = -1; std::vector directoryContents; int numFiles = 0; -//Going one level up needs to be hardcoded like this due to function inputMove needing a char** +//Going one level up needs to be hardcoded like this due to function inputMove needing a wchar_t** //c pervirtio con unicode std::wstring s(L"←→↑↓"); -char moveUp[] = ".."; -char* moveUpPtr = &moveUp[0]; +wchar_t moveUp[] = L".."; bool isListVolumesAdequate = true; bool isListDirectoriesAdequate = true; @@ -130,8 +189,9 @@ bool isListDirectoriesAdequate = true; //RenderTime vars for filtering bool showHidden = false; bool filterByExtension = false; -std::vector extensions; +std::vector extensions; +History* history; // int debugVerbosity = 0; // enum enumDebugVerbosity { @@ -150,29 +210,29 @@ std::vector extensions; /* DIRECTORIES */ -bool retrieveCurrentDirectory(char* currentPath){ +bool retrieveCurrentDirectory(wchar_t* currentPath){ if(GetCurrentDirectory(MAX_PATH, currentPath)) return true; return false; } -bool moveDirectory(char* currentPath){ +bool moveDirectory(wchar_t* currentPath){ log_debugcpp(currentPath); if(SetCurrentDirectory(currentPath)) return true; return false; } -int listDirectory(std::string path, std::vector *directoryContents){ +int listDirectory(std::wstring path, std::vector *directoryContents){ HANDLE hFind = INVALID_HANDLE_VALUE; WIN32_FIND_DATA ffd; LARGE_INTEGER filesize; int numFiles = 0; if (path.length() > (MAX_LISTDIR_PATH_LENGTH)) return DIRECTORY_ERROR_PATH_TOO_LONG; - path = path + "\\*"; + path = path + L"\\*"; hFind = FindFirstFile(path.c_str(), &ffd); if (INVALID_HANDLE_VALUE == hFind) return DIRECTORY_ERROR_ACCESSING_CONTENT; do { - if(!strcmp(ffd.cFileName, ".") || !strcmp(ffd.cFileName, "..")) continue; + if(!wcscmp(ffd.cFileName, L".") || !wcscmp(ffd.cFileName, L"..")) continue; log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); directoriesInfo* itemInfo; @@ -198,9 +258,11 @@ int listDirectory(std::string path, std::vector *directoryCont uint64_t idx = 0; do { itemInfo->name[idx] = ffd.cFileName[idx]; + itemInfo->nameUTF8[idx] = ffd.cFileName[idx]; idx++; } while (ffd.cFileName[idx]); itemInfo->name[idx] = '\0'; + itemInfo->nameUTF8[idx] = '\0'; log_debugcpp("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); numFiles++; @@ -209,35 +271,37 @@ int listDirectory(std::string path, std::vector *directoryCont return numFiles; } -long getLastDirectorySystemError(){ - long winLastError = GetLastError(); +long getLastSystemError(){ + DWORD winLastError = GetLastError(); switch(winLastError){ case ERROR_ACCESS_DENIED: return AGDIR_ERROR_ACCESS_DENIED; break; default: - return -1; + return winLastError; break; } } /* VOLUMES */ -//new char*[CharCount * sizeof(WCHAR) +//new wchar_t*[Wchar_TCount * sizeof(WWCHAR_T) /*idea, si quisiera que empezase en punto X del string coger el param opcional y crear un puntero a la posicion X del array para empezar desde ahi - int explodePaths(TCHAR* volumePaths, int volumePathLength, int volumePathBufferSize, - char separator, std::vector *dest, , int startingFrom = 0); + int explodePaths(TWCHAR_T* volumePaths, int volumePathLength, int volumePathBufferSize, + wchar_t separator, std::vector *dest, , int startingFrom = 0); if (nextOcurrence == NULL) return 0; */ -int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, std::vector *dest, int depth = 1){ - //Por alguna razon esta puta mierda que dice acabar con 2 NULL acaba con 3; de locos. +int explodePaths(wchar_t* volumePaths, int volumePathsBufferSize, wchar_t separator, std::vector *dest, std::vector *destUTF8, int depth = 1){ + //Por alguna razón, esto que dice acabar con 2 NULL acaba con 3; de locos. //https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumepathnamesforvolumenamew - char* nextOcurrence = strchr(volumePaths, separator); + wchar_t* nextOcurrence = wcschr(volumePaths, separator); - char* itemPath; - itemPath = (char*)calloc(1, volumePathsBufferSize); + wchar_t* itemPath; + char* itemPathUTF8; + itemPath = (wchar_t*)calloc(2, volumePathsBufferSize); + itemPathUTF8 = (char*)calloc(4, volumePathsBufferSize); int pathLengthIdx = 0; for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { @@ -246,27 +310,32 @@ int explodePaths(TCHAR* volumePaths, int volumePathsBufferSize, char separator, } itemPath[pathLengthIdx + 1] = '\0'; dest->push_back(itemPath); + WideCharToMultiByte(CP_UTF8, NULL, itemPath, -1, itemPathUTF8, + 4 * volumePathsBufferSize, NULL, NULL); + destUTF8->push_back(itemPathUTF8); + - log_debugcpp(dest->at(dest->size() - 1) << " EXPLODEPATH"); + log_wdebugcpp(dest->at(dest->size() - 1) << L" EXPLODEPATH"); + log_debugcpp(dest->at(destUTF8->size() - 1) << " EXPLODEPATH"); if (nextOcurrence[0] == nextOcurrence[1]) return depth; depth++; - return explodePaths(nextOcurrence + sizeof(TCHAR), (volumePathsBufferSize - pathLengthIdx), separator, dest, depth); + return explodePaths(nextOcurrence + sizeof(wchar_t), (volumePathsBufferSize - pathLengthIdx), separator, dest, destUTF8, depth); } -int listVolumes(std::vector *onPresentPaths){ +int listVolumes(std::vector *onPresentPaths, std::vector *onPresentPathsUTF8){ HANDLE hFind = INVALID_HANDLE_VALUE; - const char separator = '\0'; + const wchar_t separator = '\0'; int numVolumes = 0; //Volume name - TCHAR volumeName[MAX_PATH]; + wchar_t volumeName[MAX_PATH]; int volumeNameSize = sizeof volumeName / sizeof volumeName[0]; //Paths - int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(TCHAR); + int volumePathsBufferSize = (MAX_PATH + 1) * sizeof(wchar_t); unsigned long volumePathLength; - TCHAR volumePaths[volumePathsBufferSize]; + wchar_t volumePaths[volumePathsBufferSize]; std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); hFind = FindFirstVolume(volumeName, volumeNameSize); @@ -294,7 +363,7 @@ int listVolumes(std::vector *onPresentPaths){ // exit(1); // } if (debug) { - if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths)){ + if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths, onPresentPathsUTF8)){ for (int i = 0; i < onPresentPaths->size(); i++){ log_debugcpp(onPresentPaths->at(i)); } @@ -305,7 +374,7 @@ int listVolumes(std::vector *onPresentPaths){ log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); log_debugcpp("THE END"); } else { - log_debugcpp("no volumes found wtf"); + log_debugcpp("no volumes found"); } //TODO benchimarqui std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); @@ -319,17 +388,17 @@ int listVolumes(std::vector *onPresentPaths){ /* DISPLAY */ -bool compareLastChar(std::vector *lastCharCandidates, char* string){ +bool compareLastWchar(std::vector *lastCharCandidates, wchar_t* string){ bool matchingExtension = false; for (int i = 0; i < lastCharCandidates->size(); i++){ - log_debugcpp(lastCharCandidates->at(i) + " <- FILTERING EXTENSION"); + log_wdebugcpp(lastCharCandidates->at(i) + L" <- FILTERING EXTENSION"); - if(strlen(string) < lastCharCandidates->at(i).length()) continue; - char* potentialExtension = &string[strlen(string) - lastCharCandidates->at(i).length()]; + if(wcslen(string) < lastCharCandidates->at(i).length()) continue; + wchar_t* potentialExtension = &string[wcslen(string) - lastCharCandidates->at(i).length()]; - log_debugcpp(potentialExtension << " LEN " + std::to_string(strlen(potentialExtension)) + " <- VS -> " + lastCharCandidates->at(i) + " LEN " + std::to_string(lastCharCandidates->at(i).length())); + log_wdebugcpp(potentialExtension << L" LEN " + std::to_wstring(wcslen(potentialExtension)) + L" <- VS -> " + lastCharCandidates->at(i) + L" LEN " + std::to_wstring(lastCharCandidates->at(i).length())); //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) - if(!strcmp(potentialExtension, lastCharCandidates->at(i).c_str())) { + if(!wcscmp(potentialExtension, lastCharCandidates->at(i).c_str())) { log_debugcpp("VALID EXTENSION"); matchingExtension = true; } @@ -338,19 +407,19 @@ bool compareLastChar(std::vector *lastCharCandidates, char* string) } //TODO huh -// int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ - // const char* errorContent, History* history, char* currentPath = NULL, char* addrBarVal = NULL){ +// int handleFolderAccessResult(int listFlags, wchar_t* nextPath, bool* error, wchar_t* errorDest, \ + // const wchar_t* errorContent, History* history, wchar_t* currentPath = NULL, wchar_t* addrBarVal = NULL){ // *error = false; // if(!moveDirectory(nextPath)) { // *error = true; // if(currentPath && addrBarVal) { - // strcpy(errorDest, errorContent); - // strcpy(addrBarVal, currentPath); + // wcscpy(errorDest, errorContent); + // wcscpy(addrBarVal, currentPath); // } // return listFlags; // } // if(currentPath && addrBarVal) { - // strcpy(currentPath, addrBarVal); + // wcscpy(currentPath, addrBarVal); // history->isAdditionTime = true; // } // return (listFlags |= LIST_DIRECTORY); @@ -358,21 +427,21 @@ bool compareLastChar(std::vector *lastCharCandidates, char* string) //TODO huh, vale, no pasa por listdirectory por la flag, pero esto se podria hacer mejor manteniendo el no tirar de disco tb? -int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ - const char* errorContent, History* history, char* currentPath , char* addrBarVal){ +int handleFolderAccessResult(int listFlags, wchar_t* nextPath, bool* error, char* errorDest, \ + const char* errorContent, History* history, wchar_t* currentPath , wchar_t* addrBarVal){ *error = false; if(!moveDirectory(nextPath)) { *error = true; strcpy(errorDest, errorContent); - strcpy(addrBarVal, currentPath); + wcscpy(addrBarVal, currentPath); return listFlags; } - //strcpy(currentPath, addrBarVal); + //wcscpy(currentPath, addrBarVal); history->isAdditionTime = true; return (listFlags |= LIST_DIRECTORY); } -int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* errorDest, \ +int handleFolderAccessResult(int listFlags, wchar_t* nextPath, bool* error, char* errorDest, \ const char* errorContent, History* history, bool isAdditionTime){ *error = false; if(!moveDirectory(nextPath)) { @@ -386,21 +455,27 @@ int handleFolderAccessResult(int listFlags, char* nextPath, bool* error, char* e /* HISTORIAL */ -void addPathToHistory(History *history, char* pathToAdd){ - char* path; +void addPathToHistory(History *history, wchar_t* pathToAdd){ + wchar_t* path; + char* pathUTF8; if (history->historyTraversalPos + 1 >= history->previousPaths.size()){ - path = (char*)calloc(1, MAX_PATH); + path = (wchar_t*)calloc(2, MAX_PATH); history->previousPaths.push_back(path); + pathUTF8 = (char*)calloc(4, MAX_PATH); + WideCharToMultiByte(CP_UTF8, NULL, path, -1, pathUTF8, + 4 * MAX_PATH, NULL, NULL); + history->previousPathsUTF8.push_back(pathUTF8); } else { path = history->previousPaths.at(history->historyTraversalPos + 1); + pathUTF8 = history->previousPathsUTF8.at(history->historyTraversalPos + 1); } - strncpy(path, pathToAdd, MAX_PATH); + wcsncpy(path, pathToAdd, MAX_PATH); history->historyTraversalPos++; history->historyDepth = history->historyTraversalPos; history->isAdditionTime = false; } -char* moveThroughHistory(History *history, HistoryMovement movement) { +wchar_t* moveThroughHistory(History *history, HistoryMovement movement) { int sign = (movement == HISTORY_BACKWARD ? -1 : 1); switch(movement){ case HISTORY_BACKWARD: @@ -418,25 +493,60 @@ char* moveThroughHistory(History *history, HistoryMovement movement) { return history->previousPaths.at(history->historyTraversalPos); } +/* RENDERING */ -/* FIN FILE PICKER MOMENTO */ - - -void filePickerMomento(bool isFullViewport = false){ - if (isFullViewport){ +int windowRendering(wchar_t*, bool*, int); +int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { + //TODO filesystem watcher + //TODO let user handle memory management + //TODO use local val to create actual windowFlags when expanding functionality + if (!windowConfigured) { + listFlags = LIST_DIRECTORY | LIST_VOLUME; + history = new History(); + if (windowFlags & FP_FULLSCREEN){ ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize; + } else { + ImGui::SetNextWindowPos(ImVec2(50.0f, 50.0f)); + ImGui::SetNextWindowSize(ImVec2(500.0f, 500.0f)); + windowFlags = ImGuiWindowFlags_None; + //const ImGuiViewport* viewport = ImGui::GetMainViewport(); } + windowConfigured = true; + } else windowFlags = 0; + return windowRendering(userPath, windowOpen, windowFlags); +} - ImGui::Begin("File Picker in 4K", NULL, windowFlags); - if (isFullViewport) ImGui::PopStyleVar(1); - +int exitWindow(wchar_t* userPath, bool* windowOpen, int exitFlag){ + if(exitFlag & EXIT_SELECTED) { + if (chosenPath[0] == '\0') return EXIT_CONTINUE; + wcscpy(userPath, chosenPath); + *windowOpen = false; + } + //Memory cleanup + windowConfigured = false; + for (int i = 0; i < history->previousPaths.size(); i++){ + free(history->previousPaths.at(i)); + } + for (int i = 0; i < onPresentPaths.size(); i++){ + free(onPresentPaths.at(i)); + } + onPresentPaths.resize(0); + std::vector previousPaths; + delete history; + return exitFlag; + } + +int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::Begin("File picker", windowOpen, windowFlags); + ImGui::PopStyleVar(1); + //log_debugcpp(*windowOpen); //Listar UNA VEZ volumenes if (listFlags & LIST_VOLUME) { listFlags &= ~LIST_VOLUME; - numVolumes = listVolumes(&onPresentPaths); + numVolumes = listVolumes(&onPresentPaths, &onPresentPathsUTF8); log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); } @@ -444,68 +554,52 @@ void filePickerMomento(bool isFullViewport = false){ //ImGui::Text("%s PRE DIRECTORY TREATMENTO", currentPath); if (listFlags & LIST_DIRECTORY) { listFlags &= ~LIST_DIRECTORY; + chosenPath[0] = '\0'; log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); if(!retrieveCurrentDirectory(currentPath)) { log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); exit(EXIT_FAILURE); } // - //TODO ELIMINAR RESTRICCION EXE; PRUEBITA DEL SIGNIORE + //TODO Expand extension filter - numFiles = listDirectory(std::string(currentPath), &directoryContents); + numFiles = listDirectory(std::wstring(currentPath), &directoryContents); //std::cout << numFiles; if (numFiles < 0) { - //TODO quitar GetLastError() de aqui long directoryErrorCode; log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); - if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastDirectorySystemError(); + if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastSystemError(); showError = true; switch(directoryErrorCode) { case AGDIR_ERROR_ACCESS_DENIED: - strcpy(error, ErrorMessages::accessDeniedError); - strcpy(addrBarVal, history->previousPaths.at(history->historyTraversalPos)); - strcpy(currentPath, history->previousPaths.at(history->historyTraversalPos)); - numFiles = listDirectory(history->previousPaths.at(history->historyTraversalPos), &directoryContents); - break; + strcpy(error, ErrorMessages::accessDeniedError); + wcscpy(addrBarVal, history->previousPaths.at(history->historyTraversalPos)); + wcscpy(currentPath, history->previousPaths.at(history->historyTraversalPos)); + numFiles = listDirectory(history->previousPaths.at(history->historyTraversalPos), &directoryContents); + break; default: - strcpy(error, ErrorMessages::listDirectoryError); - strcpy(currentPath, addrBarVal); - numFiles = listDirectory(std::string(currentPath), &directoryContents); + strcpy(error, ErrorMessages::listDirectoryError); + wcscpy(currentPath, addrBarVal); + numFiles = listDirectory(std::wstring(currentPath), &directoryContents); break; } history->isAdditionTime = false; } else { showError = false; - strcpy(addrBarVal, currentPath); + wcscpy(addrBarVal, currentPath); if(history->isAdditionTime) addPathToHistory(history, addrBarVal); } } - /* + /* It's renderin' time */ - - - - - It's renderin' time - - - - - - */ - - if(showError){ - ImGui::Text(error); - } - + if(showError) ImGui::Text(error); ImGui::Text("Select a file:"); - //Permanentes e increiblemente utilitarios botones en Cuatro K: BACK + //BACK ImGui::BeginDisabled(MIN_HISTORY_POS); if(ImGui::Button("Back")) { - char* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); - //TODO: ERROR + wchar_t* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_BACKWARD); listFlags = handleFolderAccessResult(listFlags, interfaceMovementButtonsPath, &showError, error, ErrorMessages::moveUpError, history, false); } @@ -514,8 +608,7 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::SameLine(); ImGui::BeginDisabled(MAX_HISTORY_POS); if(ImGui::Button("Forward")) { - //TODO david???????????????????????????????????? - char* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); + wchar_t* interfaceMovementButtonsPath = moveThroughHistory(history, HISTORY_FORWARD); listFlags = handleFolderAccessResult(listFlags, interfaceMovementButtonsPath, &showError, error, ErrorMessages::moveUpError, history, false); } @@ -531,11 +624,13 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::SameLine(); //BARRA DE DIRECCIONES ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Enter").x + ImGui::GetStyle().ItemSpacing.x * 2) ); - if(ImGui::InputText("##addrbar", addrBarVal, IM_ARRAYSIZE(addrBarVal), ImGuiInputTextFlags_EnterReturnsTrue )){ + WideCharToMultiByte(CP_UTF8, NULL, addrBarVal, -1, addrBarValUTF8, + 4 * MAX_PATH, NULL, NULL); + if(ImGui::InputText("##addrbar", addrBarValUTF8, IM_ARRAYSIZE(addrBarValUTF8), ImGuiInputTextFlags_EnterReturnsTrue )){ log_debugcpp(currentPath <<" ADDRBAR INTENTO"); - listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, - history, currentPath, addrBarVal); - }; + MultiByteToWideChar(CP_UTF8, NULL, addrBarValUTF8, -1, addrBarVal, 2 * MAX_PATH); + listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); + } ImGui::PopItemWidth(); ImGui::SameLine(); if(ImGui::Button("Enter")){ @@ -567,8 +662,7 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i / 7.0f, 0.6f, 0.6f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i / 7.0f, 0.7f, 0.7f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i / 7.0f, 0.8f, 0.8f)); - if(ImGui::Button(onPresentPaths.at(i))){ - //TODO xdddddddd + if(ImGui::Button(onPresentPathsUTF8.at(i))){ listFlags = handleFolderAccessResult(listFlags, onPresentPaths.at(i), &showError, error, ErrorMessages::moveUpError, history, true); } @@ -582,7 +676,7 @@ void filePickerMomento(bool isFullViewport = false){ ImGui::TableNextColumn(); - //DIRECTORIOS ENCONTRADOS bien pinta2 + //DRAW DIRECTORIOS ENCONTRADOS static ImGuiTableFlags directoriesFlags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Resizable |ImGuiTableFlags_ScrollY ; if (ImGui::BeginTable("##directoriesTable", 3, directoriesFlags, ImVec2(-FLT_MIN, 20 * ImGui::GetTextLineHeightWithSpacing()))){ ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_NoHide); @@ -596,10 +690,10 @@ void filePickerMomento(bool isFullViewport = false){ for (; idx < numFiles; idx++) { if(directoryContents.at(idx)->isHidden && !showHidden) continue; if(filterByExtension && !directoryContents.at(idx)->isFile) { - //TODO XAPUSITA + //TODO Expand extensions filter extensions.clear(); - extensions.push_back("exe"); - if (!compareLastChar(&extensions, directoryContents.at(idx)->name)) continue; + extensions.push_back(L"exe"); + if (!compareLastWchar(&extensions, directoryContents.at(idx)->name)) continue; } ImGui::TableNextRow(); @@ -607,11 +701,21 @@ void filePickerMomento(bool isFullViewport = false){ //NAME bool isSelected = false; - if (ImGui::Selectable(directoryContents.at(idx)->name, &isSelected)){ - strcat(addrBarVal, "\\"); - strcat(addrBarVal, directoryContents.at(idx)->name); - listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, + WideCharToMultiByte(CP_UTF8, NULL, addrBarVal, -1, addrBarValUTF8, + 4 * MAX_PATH, NULL, NULL); + if (ImGui::Selectable(directoryContents.at(idx)->nameUTF8, &isSelected)){ + if(!directoryContents.at(idx)->isFile) { + wcscpy(chosenPath, addrBarVal); + wcscat(chosenPath, L"\\"); + wcscat(chosenPath, directoryContents.at(idx)->name); + log_wdebugcpp(chosenPath << L" selected"); + } else { + wcscat(addrBarVal, L"\\"); + wcscat(addrBarVal, directoryContents.at(idx)->name); + log_debugcpp("directo selected"); + listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::tableElementError, history, currentPath, addrBarVal); + } //currentItemIdx = -1; } ImGui::TableNextColumn(); @@ -639,14 +743,37 @@ void filePickerMomento(bool isFullViewport = false){ //ImGui::TreePop(); - + //BOTTOM ROW ImGui::Checkbox("Show hidden", &showHidden); ImGui::SameLine(); ImGui::Checkbox("Filter by .exe", &filterByExtension); ImGui::SameLine(); - ImGui::Button("Select"); + ImGui::SameLine(); + ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x - (ImGui::CalcTextSize("Select").x + ImGui::GetStyle().ItemSpacing.x * 2) ); + + ImGui::BeginDisabled(); + WideCharToMultiByte(CP_UTF8, NULL, chosenPath, -1, chosenPathUTF8, + 4 * MAX_PATH, NULL, NULL); + ImGui::InputText("##chosenbar", chosenPathUTF8, IM_ARRAYSIZE(chosenPathUTF8), ImGuiInputTextFlags_EnterReturnsTrue ); + ImGui::EndDisabled(); + + ImGui::SameLine(); + if(ImGui::Button("Select")) { + log_wdebugcpp(chosenPath << " TRIED TO RETURN"); + ImGui::End(); + return exitWindow(userPath, windowOpen, exitFlags::EXIT_SELECTED); + } + //listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); + ImGui::End(); + + if(windowOpen) return EXIT_CONTINUE; + else return exitWindow(userPath, windowOpen, exitFlags::EXIT_CLOSED); + +} + + } -} \ No newline at end of file + diff --git a/glfw3.dll b/glfw3.dll new file mode 100644 index 0000000000000000000000000000000000000000..f355ace0e2ed4c764671cd66d794022e0efe2b17 GIT binary patch literal 234496 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 G`u87Gzpcgq literal 0 HcmV?d00001 diff --git a/main.cpp b/main.cpp index 0a40c4e..0b229e9 100644 --- a/main.cpp +++ b/main.cpp @@ -6,7 +6,7 @@ #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION -#include "imgui/misc/single_file/imgui_single_file.h" +#include "unityBuild.h" #include "filepicker.hpp" // [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers. @@ -60,7 +60,7 @@ int main(int, char**) { // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; + ImGuiIO& io = ImGui::GetIO(); (void)io; //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls @@ -80,18 +80,18 @@ int main(int, char**) { // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Read 'docs/FONTS.md' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != NULL); + //io.Fonts->AddFontDefault(); + //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); + //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != NULL); // Our state bool show_demo_window = true; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); - + // Main loop while (!glfwWindowShouldClose(window)) { // Poll and handle events (inputs, window resize, etc.) @@ -107,10 +107,14 @@ int main(int, char**) { ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - if (show_demo_window) + static wchar_t path[MAX_PATH];// = malloc(MAX_PATH * sizeof(char)); + static bool windowOpen = true; + if(windowOpen) + fp::renderFilePicker(&path[0], &windowOpen, 0); + + if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); - fp::filePickerMomento(true); // Rendering ImGui::Render(); @@ -120,7 +124,7 @@ int main(int, char**) { glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - + ImGui::EndFrame(); glfwSwapBuffers(window); } @@ -130,10 +134,10 @@ int main(int, char**) { - - + + // Cleanup diff --git a/unityBuild.h b/unityBuild.h new file mode 100644 index 0000000..502c54c --- /dev/null +++ b/unityBuild.h @@ -0,0 +1,25 @@ +// dear imgui: single-file wrapper include +// We use this to validate compiling all *.cpp files in a same compilation unit. +// Users of that technique (also called "Unity builds") can generally provide this themselves, +// so we don't really recommend you use this in your projects. + +// Do this: +// #define IMGUI_IMPLEMENTATION +// Before you include this file in *one* C++ file to create the implementation. +// Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit. +#define WIN32_LEAN_AND_MEAN +#define UNICODE +#define _UNICODE +#include "imgui/imgui.h" + +#ifdef IMGUI_IMPLEMENTATION +#include "imgui/imgui.cpp" +#include "imgui/imgui_demo.cpp" +#include "imgui/imgui_draw.cpp" +#include "imgui/imgui_tables.cpp" +#include "imgui/imgui_widgets.cpp" +#include "imgui/backends/imgui_impl_glfw.h" +#include "imgui/backends/imgui_impl_opengl3.h" +#include "imgui/backends/imgui_impl_opengl3.cpp" +#include "imgui/backends/imgui_impl_glfw.cpp" +#endif From 729e72d4bde6d7733421d07b556a6e719e6445a2 Mon Sep 17 00:00:00 2001 From: Hane Date: Thu, 11 Jan 2024 23:54:03 +0100 Subject: [PATCH 18/21] files moved --- main.cpp => demo/main.cpp | 0 unityBuild.h => demo/unityBuild.h | 0 glfw3.dll | Bin 234496 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) rename main.cpp => demo/main.cpp (100%) rename unityBuild.h => demo/unityBuild.h (100%) delete mode 100644 glfw3.dll diff --git a/main.cpp b/demo/main.cpp similarity index 100% rename from main.cpp rename to demo/main.cpp diff --git a/unityBuild.h b/demo/unityBuild.h similarity index 100% rename from unityBuild.h rename to demo/unityBuild.h diff --git a/glfw3.dll b/glfw3.dll deleted file mode 100644 index f355ace0e2ed4c764671cd66d794022e0efe2b17..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234496 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 G`u87Gzpcgq From b5a87d3779b8eba532e254707695b8e34d04e018 Mon Sep 17 00:00:00 2001 From: Hane Date: Fri, 12 Jan 2024 01:13:48 +0100 Subject: [PATCH 19/21] demo source cleanup --- demo/main.cpp | 53 ++++------------------------------------------- demo/unityBuild.h | 29 +++++++++----------------- 2 files changed, 14 insertions(+), 68 deletions(-) diff --git a/demo/main.cpp b/demo/main.cpp index 0b229e9..af59500 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,26 +1,15 @@ -// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline -// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs +//Demo executable based on GLFW #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION #include "unityBuild.h" -#include "filepicker.hpp" +#include "../filepicker.hpp" -// [Win32] Our example includes a copy of glfw3.lib pre-compiled with VS2010 to maximize ease of testing and compatibility with old VS compilers. -// To link with VS2010-era libraries, VS2015+ requires linking with legacy_stdio_definitions.lib, which we do using this pragma. -// Your own project should not be affected, as you are likely to link with a newer binary of GLFW that is adequate for your version of Visual Studio. -#if defined(_MSC_VER) && (_MSC_VER >= 1900) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) -#pragma comment(lib, "legacy_stdio_definitions") -#endif -static void glfw_error_callback(int error, const char* description) -{ +static void glfw_error_callback(int error, const char* description) { fprintf(stderr, "Glfw Error %d: %s\n", error, description); } - int main(int, char**) { // Setup window glfwSetErrorCallback(glfw_error_callback); @@ -61,8 +50,6 @@ int main(int, char**) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls // Setup Dear ImGui style ImGui::StyleColorsDark(); @@ -72,22 +59,6 @@ int main(int, char**) { ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); - // Load Fonts - // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. - // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. - // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). - // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. - // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. - // - Read 'docs/FONTS.md' for more instructions and details. - // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! - //io.Fonts->AddFontDefault(); - //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); - //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); - //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); - //IM_ASSERT(font != NULL); - // Our state bool show_demo_window = true; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); @@ -96,9 +67,6 @@ int main(int, char**) { while (!glfwWindowShouldClose(window)) { // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. - // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. - // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. - // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. glfwPollEvents(); // Start the Dear ImGui frame @@ -114,8 +82,7 @@ int main(int, char**) { if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); - - + // Rendering ImGui::Render(); int display_w, display_h; @@ -128,18 +95,6 @@ int main(int, char**) { glfwSwapBuffers(window); } - - - - - - - - - - - - // Cleanup ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); diff --git a/demo/unityBuild.h b/demo/unityBuild.h index 502c54c..d2a675a 100644 --- a/demo/unityBuild.h +++ b/demo/unityBuild.h @@ -1,25 +1,16 @@ -// dear imgui: single-file wrapper include -// We use this to validate compiling all *.cpp files in a same compilation unit. -// Users of that technique (also called "Unity builds") can generally provide this themselves, -// so we don't really recommend you use this in your projects. - -// Do this: -// #define IMGUI_IMPLEMENTATION -// Before you include this file in *one* C++ file to create the implementation. -// Using this in your project will leak the contents of imgui_internal.h and ImVec2 operators in this compilation unit. #define WIN32_LEAN_AND_MEAN #define UNICODE #define _UNICODE -#include "imgui/imgui.h" +#include "../imgui/imgui.h" #ifdef IMGUI_IMPLEMENTATION -#include "imgui/imgui.cpp" -#include "imgui/imgui_demo.cpp" -#include "imgui/imgui_draw.cpp" -#include "imgui/imgui_tables.cpp" -#include "imgui/imgui_widgets.cpp" -#include "imgui/backends/imgui_impl_glfw.h" -#include "imgui/backends/imgui_impl_opengl3.h" -#include "imgui/backends/imgui_impl_opengl3.cpp" -#include "imgui/backends/imgui_impl_glfw.cpp" +#include "../imgui/imgui.cpp" +#include "../imgui/imgui_demo.cpp" +#include "../imgui/imgui_draw.cpp" +#include "../imgui/imgui_tables.cpp" +#include "../imgui/imgui_widgets.cpp" +#include "../imgui/backends/imgui_impl_glfw.h" +#include "../imgui/backends/imgui_impl_opengl3.h" +#include "../imgui/backends/imgui_impl_opengl3.cpp" +#include "../imgui/backends/imgui_impl_glfw.cpp" #endif From c39862160451561b98bb1319d4c061b31a971284 Mon Sep 17 00:00:00 2001 From: Hane Date: Sun, 14 Jan 2024 22:01:14 +0100 Subject: [PATCH 20/21] Add README.md --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ filepicker.hpp | 6 ++--- 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..642ed45 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +Single-header file picker Windows library for ImGui. + +# How to use + +int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { + + Call `int fp::renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0);` within the render loop providing a pre-allocated C-string buffer of MAX_PATH characters in `userPath`. `windowOpen` must be initially set to `true`, and will set itself to `false` when execution is done. If succesful, your buffer will contain a valid path. You can check the file picker state each frame by parsing this function's return value. You can provide `windowFlags` to customize its appearance. + +## Window states + +``` + - fp::ExitFlags::EXIT_CONTINUE : Window will render next frame. + - fp::ExitFlags::EXIT_SELECTED : A path has been successfully returned, window will close. + - fp::ExitFlags::EXIT_CLOSED : User has closed the window. No path is returned. + - fp::ExitFlags::EXIT_ERROR : An unexpected error has ocurred. No path is returned. +``` + +## Valid window flags +``` + - fp::WindowFlags::FP_FULLSCREEN`: Render window in full screen, without decorations. +``` + +# Library dependencies + + - **C++ STL**: This library uses `vector` and `w\string`. + +## Build considerations + + Define `DEBUG` to print relevant information to the console. + +# Demo build instructions + +## Build requirements + + - [**GLFW v3**](https://github.com/glfw/glfw/releases): used as rendering backend. Executable in releases is compiled with version [`3.3.8`](https://github.com/glfw/glfw/releases/tag/3.3.8). + - Compiled with `clang` using [**llvm-mingw 20220906**](https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906). + +## How to compile + + Download GLFW and pick your libraries according to your toolchain(`mingw-w64` in this example). + + Then, call your compiler directly (`clang` is demonstrated here) specifying both libs needed and your paths to the necessary header and lib files: + + ``` + clang++ demo/main.cpp -o demo/demo.exe -L C:/pathtollvmmingw/x86_64-w64-mingw32/bin -I C:\pathtollvmmingw\include -L C:/pathtoglfw/lib-mingw-w64 -I C:\pathtoglfw\include -lglfw3 -l libc++ + ``` + + If you want to statically link: + + ``` + clang++ demo/main.cpp -o demo/si.exe -DDEBUG -L C:\capybara\libclang\x86_64-w64-mingw32\bin -L C:\capybara\libclang\x86_64-w64-mingw32\lib -I C:\capybara\libclang\include -std=c++17 -lglfw3 -l opengl32 -l gdi32 -l user32 -l kernel32 -l imm32 -static-libstdc++ --verbose + ``` + + Add `-DDEBUG` to print filepicker's debug information, if you so desire. + +# To-Dos + + - Implement a file watcher to check for changes on the current directory. + - Add Linux support. + - Allow users to provide their own memory allocations as an option. diff --git a/filepicker.hpp b/filepicker.hpp index 2e5f8a4..6d0f1d5 100644 --- a/filepicker.hpp +++ b/filepicker.hpp @@ -126,7 +126,7 @@ enum WindowFlags { FP_DIRECTORY_SELECT = (1<<4) }; -enum exitFlags { +enum ExitFlags { EXIT_CONTINUE = (1<<0), EXIT_SELECTED = (1<<1), EXIT_CLOSED = (1<<2), @@ -762,14 +762,14 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ if(ImGui::Button("Select")) { log_wdebugcpp(chosenPath << " TRIED TO RETURN"); ImGui::End(); - return exitWindow(userPath, windowOpen, exitFlags::EXIT_SELECTED); + return exitWindow(userPath, windowOpen, ExitFlags::EXIT_SELECTED); } //listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); ImGui::End(); if(windowOpen) return EXIT_CONTINUE; - else return exitWindow(userPath, windowOpen, exitFlags::EXIT_CLOSED); + else return exitWindow(userPath, windowOpen, ExitFlags::EXIT_CLOSED); } From 380008ec9503f4fb148888e890ecfcbe4f3a9b5a Mon Sep 17 00:00:00 2001 From: Hane Date: Mon, 15 Jan 2024 21:27:42 +0100 Subject: [PATCH 21/21] Debug verbosity, modified README --- README.md | 59 +++++++------ demo/main.cpp | 56 ++++++++++--- filepicker.hpp | 218 +++++++++++++++++++++++++++++-------------------- 3 files changed, 209 insertions(+), 124 deletions(-) diff --git a/README.md b/README.md index 642ed45..a158a3d 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,56 @@ -Single-header file picker Windows library for ImGui. +Single-header file picker library for ImGui. + +Currently, this library is Windows-only. Support for Linux is planned. # How to use -int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { - - Call `int fp::renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0);` within the render loop providing a pre-allocated C-string buffer of MAX_PATH characters in `userPath`. `windowOpen` must be initially set to `true`, and will set itself to `false` when execution is done. If succesful, your buffer will contain a valid path. You can check the file picker state each frame by parsing this function's return value. You can provide `windowFlags` to customize its appearance. - -## Window states + This library exposes three functions: ``` - - fp::ExitFlags::EXIT_CONTINUE : Window will render next frame. - - fp::ExitFlags::EXIT_SELECTED : A path has been successfully returned, window will close. - - fp::ExitFlags::EXIT_CLOSED : User has closed the window. No path is returned. - - fp::ExitFlags::EXIT_ERROR : An unexpected error has ocurred. No path is returned. +void renderFilePicker(char* userPath, bool* windowOpen, bool* memoryFreed, int windowFlags); ``` -## Valid window flags + Call this function within the render loop providing a pre-allocated buffer of MAX_PATH characters in `userPath`, which will be filled with a UTF-8 string. `windowOpen` must be initially set to `true`, and will set itself to `false` when execution is done. If succesful, your buffer will contain a valid path. `memoryFreed` will be set to `true`, and must be checked once the window is closed to free memory resources acquired by this library. You can provide `windowFlags` to customize its appearance. + +### Window customization flags ``` - - fp::WindowFlags::FP_FULLSCREEN`: Render window in full screen, without decorations. + fp::WindowFlags::FULLSCREEN : Render window in full screen, without decorations. +``` + +``` +void freeMemory(bool* memoryFreed); +``` + + Memory resources will be deallocated alongside miscellaneous internal tasks, and `memoryFreed` will be set to `true`. + +``` +void setDebugInfo(int debugVerbosity); +``` + + When `FPDEBUG` is enabled, this allows you to configure debug granularity. + +### Debug flags + +``` + fp::DebugVerbosity::DIRECTORY + fp::DebugVerbosity::VOLUME + fp::DebugVerbosity::EXTENSION ``` # Library dependencies - - **C++ STL**: This library uses `vector` and `w\string`. - -## Build considerations - - Define `DEBUG` to print relevant information to the console. + - **C++ STL**: This library uses `vector`, `fill` and `w\string`. # Demo build instructions ## Build requirements - - [**GLFW v3**](https://github.com/glfw/glfw/releases): used as rendering backend. Executable in releases is compiled with version [`3.3.8`](https://github.com/glfw/glfw/releases/tag/3.3.8). - - Compiled with `clang` using [**llvm-mingw 20220906**](https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906). + - [**GLFW v3**](https://github.com/glfw/glfw/releases): used as rendering backend. Executable in releases is compiled with version [`3.3.9`](https://github.com/glfw/glfw/releases/tag/3.3.9). + - Developed and tested with `clang` using [**llvm-mingw 20220906**](https://github.com/mstorsjo/llvm-mingw/releases/tag/20220906). Any `MinGW`-backed `clang` compiler should work, but your mileage may vary. ## How to compile - Download GLFW and pick your libraries according to your toolchain(`mingw-w64` in this example). - - Then, call your compiler directly (`clang` is demonstrated here) specifying both libs needed and your paths to the necessary header and lib files: + Download GLFW and pick your libraries according to your toolchain. Then, call your compiler directly specifying both libs needed and your paths to the necessary header and lib files: ``` clang++ demo/main.cpp -o demo/demo.exe -L C:/pathtollvmmingw/x86_64-w64-mingw32/bin -I C:\pathtollvmmingw\include -L C:/pathtoglfw/lib-mingw-w64 -I C:\pathtoglfw\include -lglfw3 -l libc++ @@ -48,10 +59,8 @@ int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { If you want to statically link: ``` - clang++ demo/main.cpp -o demo/si.exe -DDEBUG -L C:\capybara\libclang\x86_64-w64-mingw32\bin -L C:\capybara\libclang\x86_64-w64-mingw32\lib -I C:\capybara\libclang\include -std=c++17 -lglfw3 -l opengl32 -l gdi32 -l user32 -l kernel32 -l imm32 -static-libstdc++ --verbose + clang++ demo/main.cpp -o demo/demo.exe -L C:/pathtoglfw/lib-mingw-w64 -L C:/pathtollvmmingw/x86_64-w64-mingw32/lib -I C:\pathtollvmmingw\include -I C:\pathtoglfw\include -std=c++17 -stdlib=libc++ -rtlib=compiler-rt -static -l glfw3 -l gdi32 -l opengl32 -l user32 -l kernel32 -static-libstdc++ ``` - - Add `-DDEBUG` to print filepicker's debug information, if you so desire. # To-Dos diff --git a/demo/main.cpp b/demo/main.cpp index af59500..19be71e 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -1,5 +1,4 @@ //Demo executable based on GLFW - #define IMGUI_IMPLEMENTATION #define GL_SILENCE_DEPRECATION @@ -40,7 +39,7 @@ int main(int, char**) { #endif // Create window with graphics context - GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", NULL, NULL); + GLFWwindow* window = glfwCreateWindow(1280, 720, "File Picker Demo", NULL, NULL); if (window == NULL) return 1; glfwMakeContextCurrent(window); @@ -58,11 +57,20 @@ int main(int, char**) { // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); - - // Our state - bool show_demo_window = true; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + // + bool debugDirectory = true; + bool debugVolume = true; + bool debugExtension = true; + bool debugLogic = true; + + // Our state + char path[MAX_PATH * 4] = "";// = malloc(MAX_PATH * sizeof(char)); + int debugVerbosity = fp::DebugVerbosity::DIRECTORY | fp::DebugVerbosity::VOLUME | fp::DebugVerbosity::EXTENSION | fp::DebugVerbosity::LOGIC; + bool windowOpen = true; + bool memoryFreed = true; + // Main loop while (!glfwWindowShouldClose(window)) { // Poll and handle events (inputs, window resize, etc.) @@ -75,13 +83,41 @@ int main(int, char**) { ImGui::NewFrame(); // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). - static wchar_t path[MAX_PATH];// = malloc(MAX_PATH * sizeof(char)); - static bool windowOpen = true; + + fp::setDebugInfo(debugVerbosity); if(windowOpen) - fp::renderFilePicker(&path[0], &windowOpen, 0); + fp::renderFilePicker(&path[0], &windowOpen, &memoryFreed, 0); + else if(!memoryFreed) + fp::freeResources(&memoryFreed); - if (show_demo_window) - ImGui::ShowDemoWindow(&show_demo_window); + //if (show_demo_window) + ImGui::SetNextWindowSize(ImVec2(500.0f, 500.0f)); + ImGui::Begin("Values", NULL, 0); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PopStyleVar(1); + ImGui::Text(path); + // debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; +// debugVerbosity &= ~DEBUG_DIRECTORY; +// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); + ImGui::Text("Debug verbosity"); + ImGui::Checkbox("Directory" , &debugDirectory); + ImGui::Checkbox("Volume" , &debugVolume ); + ImGui::Checkbox("Extension" , &debugExtension); + ImGui::Checkbox("Logic" , &debugLogic ); + debugVerbosity = (debugDirectory) ? debugVerbosity | fp::DIRECTORY : debugVerbosity& ~fp::DIRECTORY; + debugVerbosity = (debugVolume) ? debugVerbosity | fp::VOLUME : debugVerbosity& ~fp::VOLUME; + debugVerbosity = (debugExtension) ? debugVerbosity | fp::EXTENSION : debugVerbosity& ~fp::EXTENSION; + debugVerbosity = (debugLogic) ? debugVerbosity | fp::LOGIC : debugVerbosity& ~fp::LOGIC; + + //fp::DebugVerbosity::DIRECTORY | fp::DebugVerbosity::VOLUME | fp::DebugVerbosity::EXTENSION | fp::DebugVerbosity::LOGIC; + if (!windowOpen) + if(ImGui::Button("Open file picker")) { + windowOpen = !windowOpen; + } + //ImGui::Text(std::to_string(state).c_str()); + //ImGui::Text(state); + ImGui::End(); + //ImGui::ShowDemoWindow(&show_demo_window); // Rendering ImGui::Render(); diff --git a/filepicker.hpp b/filepicker.hpp index 6d0f1d5..9b6dbc5 100644 --- a/filepicker.hpp +++ b/filepicker.hpp @@ -1,5 +1,5 @@ //DEBUG MACRO -#ifdef DEBUG +#ifdef FPDEBUG #define log_debugc(str, ...) do { if (debug) fprintf(stdout, "[DEBUG] (%s:%d): " (str) "\n", __FILE__, __LINE__, ##__VA_ARGS__); } while (0) #define log_debugcpp(str) do { \ if (debug) std::cout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ @@ -7,9 +7,30 @@ #define log_wdebugcpp(str) do { \ if (debug) std::wcout << "[DEBUG]" << "(" << __FILE__ << ":" << __LINE__ << "): " << str << std::endl; \ } while (0) -#define log_directory(str) if(debugVerbosity & DEBUG_DIRECTORY) log_debugcpp((str)) -#define log_volume(str) if(debugVerbosity & DEBUG_VOLUME) log_debugcpp((str)) -#define log_extension(str) if(debugVerbosity & DEBUG_EXTENSION) log_debugcpp((str)) +#define log_directory(str) do { \ + if(debugVerbosity & DIRECTORY) log_debugcpp(str); \ + } while (0) +#define log_volume(str) do { \ + if(debugVerbosity & VOLUME) log_debugcpp(str); \ + } while (0) +#define log_logic(str) do { \ + if(debugVerbosity & LOGIC) log_debugcpp(str); \ + } while (0) +#define log_extension(str) do { \ + if(debugVerbosity & EXTENSION) log_debugcpp(str); \ + } while (0) +#define log_wdirectory(str) do { \ + if(debugVerbosity & DIRECTORY) log_wdebugcpp(str); \ + } while (0) +#define log_wvolume(str) do { \ + if(debugVerbosity & VOLUME) log_wdebugcpp(str); \ + } while (0) +#define log_wextension(str) do { \ + if(debugVerbosity & EXTENSION) log_wdebugcpp(str); \ + } while (0) +#define log_wlogic(str) do { \ + if(debugVerbosity & LOGIC) log_wdebugcpp(str); \ + } while (0) #else #define log_debugc(str, ...) #define log_debugcpp(str) @@ -17,6 +38,11 @@ #define log_directory(str) #define log_volume(str) #define log_extension(str) +#define log_wdirectory(str) +#define log_wvolume(str) +#define log_wextension(str) +#define log_logic(str) +#define log_wlogic(str) #endif //PATH MACRO @@ -62,6 +88,11 @@ struct History { int historyDepth; int historyTraversalPos; bool isAdditionTime; + + History(){ + historyTraversalPos = historyDepth = -1; + isAdditionTime = true; + } // wchar* operator[](int idx){ // if (idx > previousPaths.size()) @@ -79,19 +110,16 @@ struct History { // } // } - History(){ - historyTraversalPos = historyDepth = -1; - isAdditionTime = true; - } + }; enum HistoryMovement { - HISTORY_FORWARD = 1, + HISTORY_FORWARD = 1, HISTORY_BACKWARD = 0 }; enum ListDirectoryError { - DIRECTORY_ERROR_PATH_TOO_LONG = -1, + DIRECTORY_ERROR_PATH_TOO_LONG = -1, DIRECTORY_ERROR_ACCESSING_CONTENT = -2 }; @@ -100,41 +128,47 @@ enum AgnosticDirError { }; namespace ErrorMessages { - char addrBarError[] = "Failed to open folder: invalid path"; - char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; - char accessDeniedError[] = "Failed to open folder: access denied"; + char addrBarError[] = "Failed to open folder: invalid path"; + char tableElementError[] = "Failed to open folder: access restricted to non-admin users"; + char accessDeniedError[] = "Failed to open folder: access denied"; char listDirectoryError[] = "Failed to open folder: unknown error"; - char moveUpError[] = "Failed to move up: reached volume root?"; + char moveUpError[] = "Failed to move up: reached volume root?"; }; namespace Label { - wchar_t saveFileLabel[] = L"Save file: "; - wchar_t loadFileLabel[] = L"Select a file: "; + wchar_t saveFileLabel[] = L"Save file: "; + wchar_t loadFileLabel[] = L"Select a file: "; wchar_t selectDirectoryLabel[] = L"Select a directory: "; }; enum ListFlags { LIST_DIRECTORY = (1<<0), - LIST_VOLUME = (1<<1) + LIST_VOLUME = (1<<1) }; enum WindowFlags { - FP_FULLSCREEN = (1<<0), - FP_MODAL = (1<<1), - FP_FILE_LOAD = (1<<2), - FP_FILE_SAVE = (1<<3), - FP_DIRECTORY_SELECT = (1<<4) + FULLSCREEN = (1<<0), + MODAL = (1<<1), + FILE_LOAD = (1<<2), + FILE_SAVE = (1<<3), + DIRECTORY_SELECT = (1<<4) }; enum ExitFlags { - EXIT_CONTINUE = (1<<0), - EXIT_SELECTED = (1<<1), - EXIT_CLOSED = (1<<2), - EXIT_ERROR = (1<<3) + CONTINUE = (1<<0), + SELECTED = (1<<1), + CLOSED = (1<<2) +}; + +enum DebugVerbosity { + DIRECTORY = (1<<0), + VOLUME = (1<<1), + EXTENSION = (1<<2), + LOGIC = (1<<3) }; //VARS -#ifdef DEBUG +#ifdef FPDEBUG bool debug = true; #else bool debug = false; @@ -193,30 +227,24 @@ std::vector extensions; History* history; -// int debugVerbosity = 0; -// enum enumDebugVerbosity { - // DEBUG_DIRECTORY = (1<<0), - // DEBUG_VOLUME = (1<<1), - // DEBUG_EXTENSION = (1<<2) -// }; -// debugVerbosity = DEBUG_DIRECTORY | DEBUG_EXTENSION; -// debugVerbosity &= ~DEBUG_DIRECTORY; -// if (debugVerbosity & (DEBUG_DIRECTORY | DEBUG_EXTENSION)); - -//TODO UNICORDEO -/* FILE PICKER MOMENTO */ +int debugVerbosity = 0; +/* DEBUG VERBOSITY */ +void setDebugInfo(int debugFlags) { + debugVerbosity = debugFlags; +} + +/* FILE PICKER MOMENTO */ /* DIRECTORIES */ - bool retrieveCurrentDirectory(wchar_t* currentPath){ if(GetCurrentDirectory(MAX_PATH, currentPath)) return true; return false; } bool moveDirectory(wchar_t* currentPath){ - log_debugcpp(currentPath); + log_directory(currentPath); if(SetCurrentDirectory(currentPath)) return true; return false; } @@ -233,7 +261,7 @@ int listDirectory(std::wstring path, std::vector *directoryCon if (INVALID_HANDLE_VALUE == hFind) return DIRECTORY_ERROR_ACCESSING_CONTENT; do { if(!wcscmp(ffd.cFileName, L".") || !wcscmp(ffd.cFileName, L"..")) continue; - log_debugcpp("BUCLE listDirectory iteracion " + std::to_string(numFiles)); + log_directory("BUCLE listDirectory iteracion " + std::to_string(numFiles)); directoriesInfo* itemInfo; if (directoryContents->size() <= numFiles){ @@ -242,7 +270,7 @@ int listDirectory(std::wstring path, std::vector *directoryCon } else { itemInfo = directoryContents->at(numFiles); } - log_debugcpp("MEMORY ASSIGNES iteration " + std::to_string(numFiles)); + log_directory("MEMORY ASSIGNES iteration " + std::to_string(numFiles)); //A registrar info, fiera itemInfo->isFile = (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false; @@ -263,7 +291,7 @@ int listDirectory(std::wstring path, std::vector *directoryCon } while (ffd.cFileName[idx]); itemInfo->name[idx] = '\0'; itemInfo->nameUTF8[idx] = '\0'; - log_debugcpp("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); + log_directory("INFORMACION ALMACENADA iteracion " + std::to_string(numFiles)); numFiles++; } while (FindNextFile(hFind, &ffd) != 0); @@ -305,7 +333,7 @@ int explodePaths(wchar_t* volumePaths, int volumePathsBufferSize, wchar_t separa int pathLengthIdx = 0; for (; volumePaths[pathLengthIdx] != nextOcurrence[0]; pathLengthIdx++) { - log_debugcpp(volumePaths[0] << " ENTERS " << std::to_string(pathLengthIdx)); + log_volume(volumePaths[0] << " ENTERS " << std::to_string(pathLengthIdx)); itemPath[pathLengthIdx] = volumePaths[pathLengthIdx]; } itemPath[pathLengthIdx + 1] = '\0'; @@ -315,8 +343,8 @@ int explodePaths(wchar_t* volumePaths, int volumePathsBufferSize, wchar_t separa destUTF8->push_back(itemPathUTF8); - log_wdebugcpp(dest->at(dest->size() - 1) << L" EXPLODEPATH"); - log_debugcpp(dest->at(destUTF8->size() - 1) << " EXPLODEPATH"); + log_wvolume(dest->at(dest->size() - 1) << L" EXPLODEPATH"); + log_volume(destUTF8->at(destUTF8->size() - 1) << " EXPLODEPATH"); if (nextOcurrence[0] == nextOcurrence[1]) return depth; depth++; @@ -345,10 +373,10 @@ int listVolumes(std::vector *onPresentPaths, std::vector *onPre if(GetVolumePathNamesForVolumeName(volumeName, volumePaths, volumePathsBufferSize, &volumePathLength)){ if (volumePathLength == 1) { - log_debugcpp("Skill Issue"); + log_volume("Skill Issue"); continue; } - log_debugcpp(volumeName); + log_wvolume(volumeName); //DEBUG: pathSchecker // if (debug && volumePaths[0] == 'E') { @@ -365,21 +393,21 @@ int listVolumes(std::vector *onPresentPaths, std::vector *onPre if (debug) { if(numVolumes += explodePaths(volumePaths, volumePathsBufferSize, separator, onPresentPaths, onPresentPathsUTF8)){ for (int i = 0; i < onPresentPaths->size(); i++){ - log_debugcpp(onPresentPaths->at(i)); + log_wvolume(onPresentPaths->at(i)); } } } //std::cout << volumePaths << std::endl; - log_debugcpp(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); - log_debugcpp("THE END"); + log_volume(std::to_string(volumePathLength) + "<- VOLUME PATH LENGTH"); + log_volume("THE END"); } else { - log_debugcpp("no volumes found"); + log_volume("no volumes found"); } //TODO benchimarqui std::fill(volumePaths, volumePaths + volumePathsBufferSize, '\0'); } while (FindNextVolume(hFind, volumeName, volumeNameSize) != 0); - log_debugcpp(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); + log_volume(std::to_string(onPresentPaths->size()) + " JUST BEFORE MAIN"); FindVolumeClose(hFind); return numVolumes; } @@ -391,15 +419,15 @@ int listVolumes(std::vector *onPresentPaths, std::vector *onPre bool compareLastWchar(std::vector *lastCharCandidates, wchar_t* string){ bool matchingExtension = false; for (int i = 0; i < lastCharCandidates->size(); i++){ - log_wdebugcpp(lastCharCandidates->at(i) + L" <- FILTERING EXTENSION"); + log_wextension(lastCharCandidates->at(i) << L" <- FILTERING EXTENSION"); if(wcslen(string) < lastCharCandidates->at(i).length()) continue; wchar_t* potentialExtension = &string[wcslen(string) - lastCharCandidates->at(i).length()]; - log_wdebugcpp(potentialExtension << L" LEN " + std::to_wstring(wcslen(potentialExtension)) + L" <- VS -> " + lastCharCandidates->at(i) + L" LEN " + std::to_wstring(lastCharCandidates->at(i).length())); + log_wextension(potentialExtension << L" LEN " + std::to_wstring(wcslen(potentialExtension)) + L" <- VS -> " + lastCharCandidates->at(i) + L" LEN " + std::to_wstring(lastCharCandidates->at(i).length())); //+ strlen(potentialExtension) + std::to_string(lastCharCandidates->at(i).length()) if(!wcscmp(potentialExtension, lastCharCandidates->at(i).c_str())) { - log_debugcpp("VALID EXTENSION"); + log_extension("VALID EXTENSION"); matchingExtension = true; } } @@ -495,15 +523,19 @@ wchar_t* moveThroughHistory(History *history, HistoryMovement movement) { /* RENDERING */ -int windowRendering(wchar_t*, bool*, int); -int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { +void windowRendering(char*, bool*, bool*, int); +void renderFilePicker(char* userPath, bool* windowOpen, bool* memoryFreed, int windowFlags = 0) { //TODO filesystem watcher //TODO let user handle memory management //TODO use local val to create actual windowFlags when expanding functionality + #ifdef WIN32 + SetConsoleOutputCP(65001); + #endif + if (!windowConfigured) { listFlags = LIST_DIRECTORY | LIST_VOLUME; history = new History(); - if (windowFlags & FP_FULLSCREEN){ + if (windowFlags & FULLSCREEN){ ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); ImGui::SetNextWindowSize(ImGui::GetIO().DisplaySize); windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize; @@ -514,17 +546,12 @@ int renderFilePicker(wchar_t* userPath, bool* windowOpen, int windowFlags = 0) { //const ImGuiViewport* viewport = ImGui::GetMainViewport(); } windowConfigured = true; + *memoryFreed = false; } else windowFlags = 0; - return windowRendering(userPath, windowOpen, windowFlags); + windowRendering(userPath, windowOpen, memoryFreed, windowFlags); } - -int exitWindow(wchar_t* userPath, bool* windowOpen, int exitFlag){ - if(exitFlag & EXIT_SELECTED) { - if (chosenPath[0] == '\0') return EXIT_CONTINUE; - wcscpy(userPath, chosenPath); - *windowOpen = false; - } - //Memory cleanup + +void freeResources(bool* memoryFreed){ windowConfigured = false; for (int i = 0; i < history->previousPaths.size(); i++){ free(history->previousPaths.at(i)); @@ -532,13 +559,30 @@ int exitWindow(wchar_t* userPath, bool* windowOpen, int exitFlag){ for (int i = 0; i < onPresentPaths.size(); i++){ free(onPresentPaths.at(i)); } - onPresentPaths.resize(0); - std::vector previousPaths; - delete history; - return exitFlag; + for (int i = 0; i < history->previousPathsUTF8.size(); i++){ + free(history->previousPathsUTF8.at(i)); } + for (int i = 0; i < onPresentPathsUTF8.size(); i++){ + free(onPresentPathsUTF8.at(i)); + } + onPresentPaths.resize(0); + onPresentPathsUTF8.resize(0); + //previousPaths.resize(0); + delete history; + *memoryFreed = true; +} + +void exitWindow(char* userPath, bool* windowOpen, bool* memoryFreed, int exitFlag){ + if(exitFlag & SELECTED) { + if (chosenPath[0] == '\0') return; + strcpy(userPath, chosenPathUTF8); + *windowOpen = false; + } + freeResources(memoryFreed); + return; +} -int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ +void windowRendering(char* userPath, bool* windowOpen, bool* memoryFreed, int windowFlags = 0){ ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::Begin("File picker", windowOpen, windowFlags); ImGui::PopStyleVar(1); @@ -547,7 +591,7 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ if (listFlags & LIST_VOLUME) { listFlags &= ~LIST_VOLUME; numVolumes = listVolumes(&onPresentPaths, &onPresentPathsUTF8); - log_debugcpp(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); + log_volume(std::to_string(numVolumes) + "<- depth MAIN size() ->" + std::to_string(onPresentPaths.size())); } //Listar UNA VEZ directorios @@ -555,9 +599,9 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ if (listFlags & LIST_DIRECTORY) { listFlags &= ~LIST_DIRECTORY; chosenPath[0] = '\0'; - log_debugcpp("ADECUADO LISTAR DIRECTORIOS"); + log_directory("ADECUADO LISTAR DIRECTORIOS"); if(!retrieveCurrentDirectory(currentPath)) { - log_debugcpp("NO HABIA DIRECTORIO GetCurrentPath()"); + log_directory("NO HABIA DIRECTORIO GetCurrentPath()"); exit(EXIT_FAILURE); } // @@ -567,7 +611,7 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ //std::cout << numFiles; if (numFiles < 0) { long directoryErrorCode; - log_debugcpp("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); + log_directory("FALLO LA FUNCION, VALOR NEGATIVO " + std::to_string(numFiles)); if (numFiles == DIRECTORY_ERROR_ACCESSING_CONTENT) directoryErrorCode = getLastSystemError(); showError = true; switch(directoryErrorCode) { @@ -627,14 +671,14 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ WideCharToMultiByte(CP_UTF8, NULL, addrBarVal, -1, addrBarValUTF8, 4 * MAX_PATH, NULL, NULL); if(ImGui::InputText("##addrbar", addrBarValUTF8, IM_ARRAYSIZE(addrBarValUTF8), ImGuiInputTextFlags_EnterReturnsTrue )){ - log_debugcpp(currentPath <<" ADDRBAR INTENTO"); + log_directory(currentPath <<" ADDRBAR INTENTO"); MultiByteToWideChar(CP_UTF8, NULL, addrBarValUTF8, -1, addrBarVal, 2 * MAX_PATH); listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); } ImGui::PopItemWidth(); ImGui::SameLine(); if(ImGui::Button("Enter")){ - log_debugcpp(currentPath <<" ADDRBAR INTENTO"); + log_directory(currentPath <<" ADDRBAR INTENTO"); listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); } @@ -708,11 +752,11 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ wcscpy(chosenPath, addrBarVal); wcscat(chosenPath, L"\\"); wcscat(chosenPath, directoryContents.at(idx)->name); - log_wdebugcpp(chosenPath << L" selected"); + log_directory(chosenPath << L" selected"); } else { wcscat(addrBarVal, L"\\"); wcscat(addrBarVal, directoryContents.at(idx)->name); - log_debugcpp("directo selected"); + log_directory("directo selected"); listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::tableElementError, history, currentPath, addrBarVal); } @@ -760,17 +804,13 @@ int windowRendering(wchar_t* userPath, bool* windowOpen, int windowFlags = 0){ ImGui::SameLine(); if(ImGui::Button("Select")) { - log_wdebugcpp(chosenPath << " TRIED TO RETURN"); - ImGui::End(); - return exitWindow(userPath, windowOpen, ExitFlags::EXIT_SELECTED); + log_directory(chosenPath << " TRIED TO RETURN"); + //ImGui::End(); + exitWindow(userPath, windowOpen, memoryFreed, ExitFlags::SELECTED); } //listFlags = handleFolderAccessResult(listFlags, addrBarVal, &showError, error, ErrorMessages::addrBarError, history, currentPath, addrBarVal); ImGui::End(); - - if(windowOpen) return EXIT_CONTINUE; - else return exitWindow(userPath, windowOpen, ExitFlags::EXIT_CLOSED); - }